summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub9
-rw-r--r--modules/arkit/arkit.gdip18
-rw-r--r--modules/arkit/arkit_interface.h13
-rw-r--r--modules/arkit/arkit_interface.mm12
-rw-r--r--modules/arkit/arkit_module.cpp (renamed from modules/arkit/register_types.cpp)4
-rw-r--r--modules/arkit/arkit_module.h37
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp3
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h4
-rw-r--r--modules/assimp/import_state.h4
-rw-r--r--modules/basis_universal/texture_basisu.h5
-rw-r--r--modules/bmp/image_loader_bmp.cpp51
-rw-r--r--modules/bullet/area_bullet.cpp27
-rw-r--r--modules/bullet/area_bullet.h24
-rw-r--r--modules/bullet/bullet_physics_server.cpp9
-rw-r--r--modules/bullet/bullet_physics_server.h7
-rw-r--r--modules/bullet/bullet_types_converter.cpp59
-rw-r--r--modules/bullet/collision_object_bullet.cpp76
-rw-r--r--modules/bullet/collision_object_bullet.h54
-rw-r--r--modules/bullet/godot_collision_dispatcher.h2
-rw-r--r--modules/bullet/register_types.cpp4
-rw-r--r--modules/bullet/rid_bullet.h2
-rw-r--r--modules/bullet/rigid_body_bullet.cpp118
-rw-r--r--modules/bullet/rigid_body_bullet.h52
-rw-r--r--modules/bullet/shape_bullet.cpp59
-rw-r--r--modules/bullet/shape_bullet.h30
-rw-r--r--modules/bullet/soft_body_bullet.cpp24
-rw-r--r--modules/bullet/soft_body_bullet.h20
-rw-r--r--modules/bullet/space_bullet.cpp115
-rw-r--r--modules/bullet/space_bullet.h22
-rw-r--r--modules/camera/SCsub12
-rw-r--r--modules/camera/config.py2
-rw-r--r--modules/camera/register_types.cpp6
-rw-r--r--modules/camera/register_types.h5
-rw-r--r--modules/camera_iphone/SCsub15
-rw-r--r--modules/camera_iphone/camera.gdip18
-rw-r--r--modules/camera_iphone/camera_ios.h (renamed from modules/camera/camera_ios.h)0
-rw-r--r--modules/camera_iphone/camera_ios.mm (renamed from modules/camera/camera_ios.mm)24
-rw-r--r--modules/camera_iphone/camera_module.cpp40
-rw-r--r--modules/camera_iphone/camera_module.h (renamed from modules/arkit/register_types.h)6
-rw-r--r--modules/camera_iphone/config.py (renamed from modules/gdnative/xr/config.py)2
-rw-r--r--modules/csg/csg.cpp2
-rw-r--r--modules/csg/csg.h10
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml4
-rw-r--r--modules/csg/icons/CSGBox3D.svg7
-rw-r--r--modules/csg/icons/CSGCapsule3D.svg7
-rw-r--r--modules/csg/icons/CSGCombiner3D.svg9
-rw-r--r--modules/csg/icons/CSGCylinder3D.svg7
-rw-r--r--modules/csg/icons/CSGMesh3D.svg7
-rw-r--r--modules/csg/icons/CSGPolygon3D.svg7
-rw-r--r--modules/csg/icons/CSGSphere3D.svg7
-rw-r--r--modules/csg/icons/CSGTorus3D.svg7
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp2
-rw-r--r--modules/cvtt/image_compress_cvtt.h2
-rw-r--r--modules/cvtt/register_types.h8
-rw-r--r--modules/denoise/lightmap_denoiser.h2
-rw-r--r--modules/denoise/register_types.cpp2
-rw-r--r--modules/denoise/register_types.h5
-rw-r--r--modules/denoise/resource_to_cpp.py2
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml4
-rw-r--r--modules/enet/register_types.cpp2
-rw-r--r--modules/etc/image_etc.cpp4
-rw-r--r--modules/gamecenter/SCsub15
-rw-r--r--modules/gamecenter/config.py6
-rw-r--r--modules/gamecenter/game_center.h71
-rw-r--r--modules/gamecenter/game_center.mm380
-rw-r--r--modules/gamecenter/game_center_delegate.h35
-rw-r--r--modules/gamecenter/game_center_delegate.mm45
-rw-r--r--modules/gamecenter/game_center_module.cpp48
-rw-r--r--modules/gamecenter/game_center_module.h32
-rw-r--r--modules/gamecenter/gamecenter.gdip17
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml4
-rw-r--r--modules/gdnative/gdnative.cpp4
-rw-r--r--modules/gdnative/gdnative.h2
-rw-r--r--modules/gdnative/gdnative/aabb.cpp2
-rw-r--r--modules/gdnative/gdnative/array.cpp6
-rw-r--r--modules/gdnative/gdnative/basis.cpp2
-rw-r--r--modules/gdnative/gdnative/callable.cpp6
-rw-r--r--modules/gdnative/gdnative/color.cpp11
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp6
-rw-r--r--modules/gdnative/gdnative/gdnative.cpp16
-rw-r--r--modules/gdnative/gdnative/node_path.cpp4
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp6
-rw-r--r--modules/gdnative/gdnative/plane.cpp2
-rw-r--r--modules/gdnative/gdnative/quat.cpp2
-rw-r--r--modules/gdnative/gdnative/rect2.cpp2
-rw-r--r--modules/gdnative/gdnative/rid.cpp6
-rw-r--r--modules/gdnative/gdnative/string.cpp655
-rw-r--r--modules/gdnative/gdnative/string_name.cpp4
-rw-r--r--modules/gdnative/gdnative/transform.cpp2
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp2
-rw-r--r--modules/gdnative/gdnative/variant.cpp8
-rw-r--r--modules/gdnative/gdnative/vector2.cpp2
-rw-r--r--modules/gdnative/gdnative/vector3.cpp4
-rw-r--r--modules/gdnative/gdnative_api.json397
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp1
-rw-r--r--modules/gdnative/icons/GDNativeLibrary.svg6
-rw-r--r--modules/gdnative/icons/NativeScript.svg6
-rw-r--r--modules/gdnative/include/gdnative/color.h2
-rw-r--r--modules/gdnative/include/gdnative/string.h165
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp20
-rw-r--r--modules/gdnative/nativescript/api_generator.h2
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp10
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp4
-rw-r--r--modules/gdnative/nativescript/nativescript.h10
-rw-r--r--modules/gdnative/net/webrtc_gdnative.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h6
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.h2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h2
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp2
-rw-r--r--modules/gdnative/register_types.cpp4
-rw-r--r--modules/gdnative/tests/test_string.h1980
-rw-r--r--modules/gdnative/videodecoder/register_types.cpp2
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp2
-rw-r--r--modules/gdnavigation/gd_navigation_server.h4
-rw-r--r--modules/gdnavigation/nav_rid.h2
-rw-r--r--modules/gdnavigation/register_types.cpp2
-rw-r--r--modules/gdnavigation/rvo_agent.h3
-rw-r--r--modules/gdscript/SCsub4
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml151
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp45
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp376
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h33
-rw-r--r--modules/gdscript/gdscript.cpp66
-rw-r--r--modules/gdscript/gdscript.h3
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp451
-rw-r--r--modules/gdscript/gdscript_analyzer.h17
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp739
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h277
-rw-r--r--modules/gdscript/gdscript_cache.cpp41
-rw-r--r--modules/gdscript/gdscript_cache.h12
-rw-r--r--modules/gdscript/gdscript_codegen.h160
-rw-r--r--modules/gdscript/gdscript_compiler.cpp2698
-rw-r--r--modules/gdscript/gdscript_compiler.h160
-rw-r--r--modules/gdscript/gdscript_editor.cpp89
-rw-r--r--modules/gdscript/gdscript_function.cpp550
-rw-r--r--modules/gdscript/gdscript_function.h28
-rw-r--r--modules/gdscript/gdscript_functions.cpp84
-rw-r--r--modules/gdscript/gdscript_functions.h8
-rw-r--r--modules/gdscript/gdscript_parser.cpp198
-rw-r--r--modules/gdscript/gdscript_parser.h29
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp142
-rw-r--r--modules/gdscript/gdscript_tokenizer.h36
-rw-r--r--modules/gdscript/gdscript_warning.cpp2
-rw-r--r--modules/gdscript/gdscript_warning.h4
-rw-r--r--modules/gdscript/icons/GDScript.svg6
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp8
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h2
-rw-r--r--modules/gdscript/language_server/lsp.hpp4
-rw-r--r--modules/gdscript/register_types.cpp32
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp307
-rw-r--r--modules/gdscript/tests/test_gdscript.h47
-rw-r--r--modules/glslang/SCsub6
-rw-r--r--modules/glslang/register_types.cpp111
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml6
-rw-r--r--modules/gridmap/grid_map.cpp4
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h2
-rw-r--r--modules/gridmap/icons/GridMap.svg6
-rw-r--r--modules/gridmap/register_types.cpp2
-rw-r--r--modules/hdr/image_loader_hdr.cpp2
-rw-r--r--modules/icloud/SCsub15
-rw-r--r--modules/icloud/config.py6
-rw-r--r--modules/icloud/icloud.gdip17
-rw-r--r--modules/icloud/icloud.h60
-rw-r--r--modules/icloud/icloud.mm345
-rw-r--r--modules/icloud/icloud_module.cpp48
-rw-r--r--modules/icloud/icloud_module.h32
-rw-r--r--modules/inappstore/SCsub15
-rw-r--r--modules/inappstore/config.py6
-rw-r--r--modules/inappstore/in_app_store.h77
-rw-r--r--modules/inappstore/in_app_store.mm411
-rw-r--r--modules/inappstore/in_app_store_module.cpp48
-rw-r--r--modules/inappstore/in_app_store_module.h32
-rw-r--r--modules/inappstore/inappstore.gdip17
-rw-r--r--modules/jpg/image_loader_jpegd.cpp2
-rw-r--r--modules/jsonrpc/jsonrpc.cpp4
-rw-r--r--modules/jsonrpc/jsonrpc.h4
-rw-r--r--modules/jsonrpc/register_types.cpp2
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp2
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h2
-rw-r--r--modules/lightmapper_rd/register_types.cpp2
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp25
-rw-r--r--modules/mbedtls/crypto_mbedtls.h2
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.h2
-rw-r--r--modules/mobile_vr/register_types.cpp8
-rw-r--r--modules/mono/SCsub5
-rw-r--r--modules/mono/build_scripts/godot_net_sdk_build.py45
-rw-r--r--modules/mono/build_scripts/mono_configure.py4
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py10
-rw-r--r--modules/mono/build_scripts/solution_builder.py3
-rw-r--r--modules/mono/class_db_api_json.cpp2
-rw-r--r--modules/mono/class_db_api_json.h8
-rw-r--r--modules/mono/config.py10
-rw-r--r--modules/mono/csharp_script.cpp213
-rw-r--r--modules/mono/csharp_script.h7
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt1
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs75
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs27
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj21
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs9
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs339
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs (renamed from modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs)10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs (renamed from modules/mono/editor/GodotTools/GodotTools/BuildManager.cs)143
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs (renamed from modules/mono/editor/GodotTools/GodotTools/BuildTab.cs)212
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs42
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs185
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs296
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs2
-rwxr-xr-xmodules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs17
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs101
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs12
-rw-r--r--modules/mono/editor/bindings_generator.cpp22
-rw-r--r--modules/mono/editor/bindings_generator.h6
-rw-r--r--modules/mono/editor/code_completion.cpp2
-rw-r--r--modules/mono/editor/code_completion.h4
-rw-r--r--modules/mono/editor/godotsharp_export.cpp43
-rw-r--r--modules/mono/editor/godotsharp_export.h6
-rw-r--r--modules/mono/editor/script_class_parser.cpp10
-rw-r--r--modules/mono/editor/script_class_parser.h6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs47
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs208
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs19
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs13
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs189
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs28
-rw-r--r--modules/mono/glue/base_object_glue.cpp7
-rw-r--r--modules/mono/glue/collections_glue.cpp30
-rw-r--r--modules/mono/glue/gd_glue.cpp20
-rw-r--r--modules/mono/glue/glue_header.h17
-rw-r--r--modules/mono/glue/nodepath_glue.cpp4
-rw-r--r--modules/mono/glue/rid_glue.cpp6
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp6
-rw-r--r--modules/mono/glue/string_glue.cpp6
-rw-r--r--modules/mono/glue/string_name_glue.cpp4
-rw-r--r--modules/mono/godotsharp_dirs.cpp4
-rw-r--r--modules/mono/godotsharp_dirs.h2
-rw-r--r--modules/mono/icons/CSharpScript.svg6
-rw-r--r--modules/mono/managed_callable.h4
-rw-r--r--modules/mono/mono_gc_handle.h2
-rw-r--r--modules/mono/mono_gd/android_mono_config.h2
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp14
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp6
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp25
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp46
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h28
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h25
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp2
-rwxr-xr-xmodules/mono/mono_gd/support/android_support.h2
-rwxr-xr-xmodules/mono/mono_gd/support/ios_support.h2
-rw-r--r--modules/mono/mono_gd/support/ios_support.mm3
-rw-r--r--modules/mono/register_types.cpp2
-rw-r--r--modules/mono/register_types.h5
-rw-r--r--modules/mono/signal_awaiter_utils.h2
-rw-r--r--modules/mono/utils/macros.h2
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp8
-rw-r--r--modules/mono/utils/mono_reg_utils.h2
-rw-r--r--modules/mono/utils/osx_utils.cpp2
-rw-r--r--modules/mono/utils/osx_utils.h2
-rw-r--r--modules/mono/utils/path_utils.cpp28
-rw-r--r--modules/mono/utils/path_utils.h4
-rw-r--r--modules/mono/utils/string_utils.cpp6
-rw-r--r--modules/mono/utils/string_utils.h4
-rw-r--r--modules/opensimplex/icons/NoiseTexture.svg4
-rw-r--r--modules/opensimplex/noise_texture.h4
-rw-r--r--modules/opensimplex/open_simplex_noise.h4
-rw-r--r--modules/pvr/texture_loader_pvr.cpp8
-rw-r--r--modules/regex/regex.cpp229
-rw-r--r--modules/regex/regex.h12
-rw-r--r--modules/regex/register_types.cpp2
-rw-r--r--modules/squish/image_compress_squish.h2
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp8
-rw-r--r--modules/stb_vorbis/register_types.cpp2
-rw-r--r--modules/svg/image_loader_svg.h2
-rw-r--r--modules/tga/image_loader_tga.cpp8
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml5
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/theora/video_stream_theora.h2
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp160
-rw-r--r--modules/upnp/register_types.cpp2
-rw-r--r--modules/upnp/upnp.h2
-rw-r--r--modules/upnp/upnp_device.h2
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml40
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertyGet.xml2
-rw-r--r--modules/visual_script/icons/VisualScript.svg7
-rw-r--r--modules/visual_script/register_types.cpp2
-rw-r--r--modules/visual_script/visual_script.cpp6
-rw-r--r--modules/visual_script/visual_script.h2
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp95
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h6
-rw-r--r--modules/visual_script/visual_script_editor.cpp45
-rw-r--r--modules/visual_script/visual_script_editor.h3
-rw-r--r--modules/visual_script/visual_script_expression.cpp20
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp2
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp70
-rw-r--r--modules/visual_script/visual_script_nodes.cpp79
-rw-r--r--modules/visual_script/visual_script_property_selector.cpp2
-rw-r--r--modules/webm/doc_classes/VideoStreamWebm.xml3
-rw-r--r--modules/webm/video_stream_webm.cpp2
-rw-r--r--modules/webp/image_loader_webp.cpp2
-rw-r--r--modules/webrtc/SCsub4
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml4
-rw-r--r--modules/webrtc/library_godot_webrtc.js407
-rw-r--r--modules/webrtc/register_types.cpp2
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp2
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.h8
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp245
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h18
-rw-r--r--modules/webrtc/webrtc_multiplayer.h2
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.h8
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp244
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h23
-rw-r--r--modules/websocket/SCsub8
-rw-r--r--modules/websocket/editor_debugger_server_websocket.cpp2
-rw-r--r--modules/websocket/emws_client.cpp146
-rw-r--r--modules/websocket/emws_client.h12
-rw-r--r--modules/websocket/emws_peer.cpp38
-rw-r--r--modules/websocket/emws_peer.h18
-rw-r--r--modules/websocket/emws_server.h2
-rw-r--r--modules/websocket/library_godot_websocket.js187
-rw-r--r--modules/websocket/packet_buffer.h2
-rw-r--r--modules/websocket/register_types.cpp4
-rw-r--r--modules/websocket/remote_debugger_peer_websocket.cpp2
-rw-r--r--modules/websocket/websocket_client.h2
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h4
-rw-r--r--modules/websocket/websocket_peer.h2
-rw-r--r--modules/websocket/websocket_server.h2
-rw-r--r--modules/websocket/wsl_client.cpp2
-rw-r--r--modules/websocket/wsl_client.h2
-rw-r--r--modules/websocket/wsl_peer.h4
-rw-r--r--modules/websocket/wsl_server.cpp2
-rw-r--r--modules/xatlas_unwrap/register_types.cpp2
366 files changed, 12536 insertions, 5978 deletions
diff --git a/modules/SCsub b/modules/SCsub
index edfc4ed9c6..24598f4b28 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -47,7 +47,14 @@ for name, path in env.module_list.items():
# Some modules are not linked automatically but can be enabled optionally
# on iOS, so we handle those specially.
- if env["platform"] == "iphone" and name in ["arkit", "camera"]:
+ if env["platform"] == "iphone" and name in [
+ "arkit",
+ "camera",
+ "camera_iphone",
+ "gamecenter",
+ "inappstore",
+ "icloud",
+ ]:
continue
lib = env_modules.add_library("module_%s" % name, env.modules_sources)
diff --git a/modules/arkit/arkit.gdip b/modules/arkit/arkit.gdip
new file mode 100644
index 0000000000..22c0a07e26
--- /dev/null
+++ b/modules/arkit/arkit.gdip
@@ -0,0 +1,18 @@
+[config]
+name="ARKit"
+binary="arkit_lib.a"
+
+initialization="register_arkit_types"
+deinitialization="unregister_arkit_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["AVFoundation.framework", "ARKit.framework"]
+
+capabilities=["arkit"]
+
+files=[]
+
+[plist]
+NSCameraUsageDescription="Device camera is used for some functionality"
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h
index 5a2c50e213..29e09411ff 100644
--- a/modules/arkit/arkit_interface.h
+++ b/modules/arkit/arkit_interface.h
@@ -44,6 +44,15 @@
// forward declaration for some needed objects
class ARKitShader;
+#ifdef __OBJC__
+
+typedef ARAnchor GodotARAnchor;
+
+#else
+
+typedef void GodotARAnchor;
+#endif
+
class ARKitInterface : public XRInterface {
GDCLASS(ARKitInterface, XRInterface);
@@ -115,8 +124,8 @@ public:
virtual void process() override;
// called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm)
- void _add_or_update_anchor(void *p_anchor);
- void _remove_anchor(void *p_anchor);
+ void _add_or_update_anchor(GodotARAnchor *p_anchor);
+ void _remove_anchor(GodotARAnchor *p_anchor);
ARKitInterface();
~ARKitInterface();
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm
index 3fb2cc933d..e8fa023ac7 100644
--- a/modules/arkit/arkit_interface.mm
+++ b/modules/arkit/arkit_interface.mm
@@ -306,12 +306,10 @@ void ARKitInterface::uninitialize() {
remove_all_anchors();
if (@available(iOS 11.0, *)) {
- [ar_session release];
- ar_session = NULL;
+ ar_session = nil;
}
- [ar_delegate release];
- ar_delegate = NULL;
+ ar_delegate = nil;
initialized = false;
session_was_started = false;
}
@@ -687,7 +685,7 @@ void ARKitInterface::process() {
}
}
-void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
+void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) {
// _THREAD_SAFE_METHOD_
if (@available(iOS 11.0, *)) {
@@ -749,7 +747,7 @@ void ARKitInterface::_add_or_update_anchor(void *p_anchor) {
}
}
-void ARKitInterface::_remove_anchor(void *p_anchor) {
+void ARKitInterface::_remove_anchor(GodotARAnchor *p_anchor) {
// _THREAD_SAFE_METHOD_
if (@available(iOS 11.0, *)) {
@@ -768,7 +766,7 @@ ARKitInterface::ARKitInterface() {
plane_detection_is_enabled = false;
light_estimation_is_enabled = false;
if (@available(iOS 11.0, *)) {
- ar_session = NULL;
+ ar_session = nil;
}
z_near = 0.01;
z_far = 1000.0;
diff --git a/modules/arkit/register_types.cpp b/modules/arkit/arkit_module.cpp
index 91069ab364..87ee3b87a5 100644
--- a/modules/arkit/register_types.cpp
+++ b/modules/arkit/arkit_module.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.cpp */
+/* arkit_module.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "register_types.h"
+#include "arkit_module.h"
#include "arkit_interface.h"
diff --git a/modules/arkit/arkit_module.h b/modules/arkit/arkit_module.h
new file mode 100644
index 0000000000..8aa8175ed5
--- /dev/null
+++ b/modules/arkit/arkit_module.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* arkit_module.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 ARKIT_REGISTER_TYPES_H
+#define ARKIT_REGISTER_TYPES_H
+
+void register_arkit_types();
+void unregister_arkit_types();
+
+#endif // ARKIT_REGISTER_TYPES_H
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index aedc4b690a..e5becfd559 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -441,7 +441,6 @@ EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene,
Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
skeleton->add_bone(bone_name);
skeleton->set_bone_rest(boneIdx, pform);
- skeleton->set_bone_pose(boneIdx, pform);
if (parent_node != nullptr) {
int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
@@ -612,7 +611,7 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
xform.basis.set_quat_scale(rot, scale);
xform.origin = pos;
- xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform;
+ xform = skeleton->get_bone_rest(skeleton_bone).inverse() * xform;
rot = xform.basis.get_rotation_quat();
rot.normalize();
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
index 7be80c4ad0..80bba6ad66 100644
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ b/modules/assimp/editor_scene_importer_assimp.h
@@ -32,9 +32,9 @@
#define EDITOR_SCENE_IMPORTER_ASSIMP_H
#ifdef TOOLS_ENABLED
-#include "core/bind/core_bind.h"
+#include "core/core_bind.h"
#include "core/io/resource_importer.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/project_settings_editor.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h
index ee22800ac4..a1cce6968b 100644
--- a/modules/assimp/import_state.h
+++ b/modules/assimp/import_state.h
@@ -31,9 +31,9 @@
#ifndef EDITOR_SCENE_IMPORT_STATE_H
#define EDITOR_SCENE_IMPORT_STATE_H
-#include "core/bind/core_bind.h"
+#include "core/core_bind.h"
#include "core/io/resource_importer.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "editor/import/resource_importer_scene.h"
#include "editor/project_settings_editor.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/modules/basis_universal/texture_basisu.h b/modules/basis_universal/texture_basisu.h
index 8de151ede0..20ecf15a59 100644
--- a/modules/basis_universal/texture_basisu.h
+++ b/modules/basis_universal/texture_basisu.h
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef BASIS_UNIVERSAL_TEXTURE_BASISU_H
+#define BASIS_UNIVERSAL_TEXTURE_BASISU_H
+
#include "scene/resources/texture.h"
#ifdef TOOLS_ENABLED
@@ -75,3 +78,5 @@ public:
};
#endif
+
+#endif // BASIS_UNIVERSAL_TEXTURE_BASISU_H
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index ac4e534983..b08970d110 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -30,6 +30,8 @@
#include "image_loader_bmp.h"
+#include "core/io/file_access_memory.h"
+
Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
const uint8_t *p_buffer,
const uint8_t *p_color_buffer,
@@ -51,16 +53,20 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
if (bits_per_pixel == 1) {
// Requires bit unpacking...
- ERR_FAIL_COND_V(width % 8 != 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(height % 8 != 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V_MSG(width % 8 != 0, ERR_UNAVAILABLE,
+ vformat("1-bpp BMP images must have a width that is a multiple of 8, but the imported BMP is %d pixels wide.", int(width)));
+ ERR_FAIL_COND_V_MSG(height % 8 != 0, ERR_UNAVAILABLE,
+ vformat("1-bpp BMP images must have a height that is a multiple of 8, but the imported BMP is %d pixels tall.", int(height)));
} else if (bits_per_pixel == 4) {
// Requires bit unpacking...
- ERR_FAIL_COND_V(width % 2 != 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(height % 2 != 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V_MSG(width % 2 != 0, ERR_UNAVAILABLE,
+ vformat("4-bpp BMP images must have a width that is a multiple of 2, but the imported BMP is %d pixels wide.", int(width)));
+ ERR_FAIL_COND_V_MSG(height % 2 != 0, ERR_UNAVAILABLE,
+ vformat("4-bpp BMP images must have a height that is a multiple of 2, but the imported BMP is %d pixels tall.", int(height)));
} else if (bits_per_pixel == 16) {
- ERR_FAIL_V(ERR_UNAVAILABLE);
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "16-bpp BMP images are not supported.");
}
// Image data (might be indexed)
@@ -72,7 +78,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
} else { // color
data_len = width * height * 4;
}
- ERR_FAIL_COND_V(data_len == 0, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(data_len == 0, ERR_BUG, "Couldn't parse the BMP image data.");
err = data.resize(data_len);
uint8_t *data_w = data.ptrw();
@@ -215,13 +221,15 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
// Info Header
bmp_header.bmp_info_header.bmp_header_size = f->get_32();
- ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_header_size < BITMAP_INFO_HEADER_MIN_SIZE, ERR_FILE_CORRUPT,
+ vformat("Couldn't parse the BMP info header. The file is likely corrupt: %s", f->get_path()));
bmp_header.bmp_info_header.bmp_width = f->get_32();
bmp_header.bmp_info_header.bmp_height = f->get_32();
bmp_header.bmp_info_header.bmp_planes = f->get_16();
- ERR_FAIL_COND_V(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V_MSG(bmp_header.bmp_info_header.bmp_planes != 1, ERR_FILE_CORRUPT,
+ vformat("Couldn't parse the BMP planes. The file is likely corrupt: %s", f->get_path()));
bmp_header.bmp_info_header.bmp_bit_count = f->get_16();
bmp_header.bmp_info_header.bmp_compression = f->get_32();
@@ -236,10 +244,10 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
case BI_RLE4:
case BI_CMYKRLE8:
case BI_CMYKRLE4: {
- // Stop parsing
- String bmp_path = f->get_path();
+ // Stop parsing.
f->close();
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Compressed BMP files are not supported: " + bmp_path + ".");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE,
+ vformat("Compressed BMP files are not supported: %s", f->get_path()));
} break;
}
// Don't rely on sizeof(bmp_file_header) as structure padding
@@ -255,7 +263,8 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
if (bmp_header.bmp_info_header.bmp_bit_count <= 8) {
// Support 256 colors max
color_table_size = 1 << bmp_header.bmp_info_header.bmp_bit_count;
- ERR_FAIL_COND_V(color_table_size == 0, ERR_BUG);
+ ERR_FAIL_COND_V_MSG(color_table_size == 0, ERR_BUG,
+ vformat("Couldn't parse the BMP color table: %s", f->get_path()));
}
Vector<uint8_t> bmp_color_table;
@@ -286,9 +295,21 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
return err;
}
-void ImageLoaderBMP::get_recognized_extensions(
- List<String> *p_extensions) const {
+void ImageLoaderBMP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("bmp");
}
-ImageLoaderBMP::ImageLoaderBMP() {}
+static Ref<Image> _bmp_mem_loader_func(const uint8_t *p_bmp, int p_size) {
+ FileAccessMemory memfile;
+ Error open_memfile_error = memfile.open_custom(p_bmp, p_size);
+ ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for BMP image buffer.");
+ Ref<Image> img;
+ img.instance();
+ Error load_error = ImageLoaderBMP().load_image(img, &memfile, false, 1.0f);
+ ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load BMP image.");
+ return img;
+}
+
+ImageLoaderBMP::ImageLoaderBMP() {
+ Image::_bmp_mem_loader_func = _bmp_mem_loader_func;
+}
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index b35019bea3..f8f7d79a11 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -65,11 +65,14 @@ AreaBullet::~AreaBullet() {
}
void AreaBullet::dispatch_callbacks() {
- RigidCollisionObjectBullet::dispatch_callbacks();
+ if (!isScratched) {
+ return;
+ }
+ isScratched = false;
// Reverse order because I've to remove EXIT objects
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- OverlappingObjectData &otherObj = overlappingObjects[i];
+ OverlappingObjectData &otherObj = overlappingObjects.write[i];
switch (otherObj.state) {
case OVERLAP_STATE_ENTER:
@@ -109,9 +112,10 @@ void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3
}
void AreaBullet::scratch() {
- if (space != nullptr) {
- space->add_to_pre_flush_queue(this);
+ if (isScratched) {
+ return;
}
+ isScratched = true;
}
void AreaBullet::clear_overlaps(bool p_notify) {
@@ -160,7 +164,7 @@ void AreaBullet::main_shape_changed() {
btGhost->setCollisionShape(get_main_shape());
}
-void AreaBullet::do_reload_body() {
+void AreaBullet::reload_body() {
if (space) {
space->remove_area(this);
space->add_area(this);
@@ -169,25 +173,22 @@ void AreaBullet::do_reload_body() {
void AreaBullet::set_space(SpaceBullet *p_space) {
// Clear the old space if there is one
-
if (space) {
clear_overlaps(false);
+ isScratched = false;
// Remove this object form the physics world
- space->unregister_collision_object(this);
space->remove_area(this);
}
space = p_space;
if (space) {
- space->register_collision_object(this);
- reload_body();
- scratch();
+ space->add_area(this);
}
}
-void AreaBullet::do_reload_collision_filters() {
+void AreaBullet::on_collision_filters_change() {
if (space) {
space->reload_collision_filters(this);
}
@@ -201,13 +202,13 @@ void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
void AreaBullet::put_overlap_as_exit(int p_index) {
scratch();
- overlappingObjects[p_index].state = OVERLAP_STATE_EXIT;
+ overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT;
}
void AreaBullet::put_overlap_as_inside(int p_index) {
// This check is required to be sure this body was inside
if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
- overlappingObjects[p_index].state = OVERLAP_STATE_INSIDE;
+ overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE;
}
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 51fbc1f71d..152fd785c0 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -32,7 +32,7 @@
#define AREABULLET_H
#include "collision_object_bullet.h"
-#include "core/local_vector.h"
+#include "core/templates/vector.h"
#include "servers/physics_server_3d.h"
#include "space_bullet.h"
@@ -83,7 +83,7 @@ private:
Variant *call_event_res_ptr[5];
btGhostObject *btGhost;
- LocalVector<OverlappingObjectData> overlappingObjects;
+ Vector<OverlappingObjectData> overlappingObjects;
bool monitorable = true;
PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
@@ -96,6 +96,8 @@ private:
real_t spOv_angularDump = 0.1;
int spOv_priority = 0;
+ bool isScratched = false;
+
InOutEventCallback eventsCallbacks[2];
public:
@@ -137,11 +139,11 @@ public:
_FORCE_INLINE_ void set_spOv_priority(int p_priority) { spOv_priority = p_priority; }
_FORCE_INLINE_ int get_spOv_priority() { return spOv_priority; }
- virtual void main_shape_changed() override;
- virtual void do_reload_body() override;
- virtual void set_space(SpaceBullet *p_space) override;
+ virtual void main_shape_changed();
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
- virtual void dispatch_callbacks() override;
+ virtual void dispatch_callbacks();
void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
@@ -150,9 +152,9 @@ public:
// Dispatch the callbacks and removes from overlapping list
void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
- virtual void do_reload_collision_filters() override;
- virtual void on_collision_checker_start() override {}
- virtual void on_collision_checker_end() override { isTransformChanged = false; }
+ virtual void on_collision_filters_change();
+ virtual void on_collision_checker_start() {}
+ virtual void on_collision_checker_end() { isTransformChanged = false; }
void add_overlap(CollisionObjectBullet *p_otherObject);
void put_overlap_as_exit(int p_index);
@@ -164,8 +166,8 @@ public:
void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method);
bool has_event_callback(Type p_callbackObjectType);
- virtual void on_enter_area(AreaBullet *p_area) override;
- virtual void on_exit_area(AreaBullet *p_area) override;
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
};
#endif
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 8f64c11867..663ad6e3e1 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -32,9 +32,9 @@
#include "bullet_utilities.h"
#include "cone_twist_joint_bullet.h"
-#include "core/class_db.h"
-#include "core/error_macros.h"
-#include "core/ustring.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
+#include "core/string/ustring.h"
#include "generic_6dof_joint_bullet.h"
#include "hinge_joint_bullet.h"
#include "pin_joint_bullet.h"
@@ -1553,9 +1553,6 @@ void BulletPhysicsServer3D::step(float p_deltaTime) {
}
}
-void BulletPhysicsServer3D::sync() {
-}
-
void BulletPhysicsServer3D::flush_queries() {
if (!active) {
return;
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index eb95120f74..dca9339c44 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -32,8 +32,8 @@
#define BULLET_PHYSICS_SERVER_H
#include "area_bullet.h"
-#include "core/rid.h"
-#include "core/rid_owner.h"
+#include "core/templates/rid.h"
+#include "core/templates/rid_owner.h"
#include "joint_bullet.h"
#include "rigid_body_bullet.h"
#include "servers/physics_server_3d.h"
@@ -52,7 +52,7 @@ class BulletPhysicsServer3D : public PhysicsServer3D {
bool active = true;
char active_spaces_count = 0;
- LocalVector<SpaceBullet *> active_spaces;
+ Vector<SpaceBullet *> active_spaces;
mutable RID_PtrOwner<SpaceBullet> space_owner;
mutable RID_PtrOwner<ShapeBullet> shape_owner;
@@ -397,7 +397,6 @@ public:
virtual void init() override;
virtual void step(float p_deltaTime) override;
- virtual void sync() override;
virtual void flush_queries() override;
virtual void finish() override;
diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp
index c9493d8892..09b90fe09e 100644
--- a/modules/bullet/bullet_types_converter.cpp
+++ b/modules/bullet/bullet_types_converter.cpp
@@ -95,12 +95,61 @@ void G_TO_B(Transform const &inVal, btTransform &outVal) {
}
void UNSCALE_BT_BASIS(btTransform &scaledBasis) {
- btMatrix3x3 &m(scaledBasis.getBasis());
- btVector3 column0(m[0][0], m[1][0], m[2][0]);
- btVector3 column1(m[0][1], m[1][1], m[2][1]);
- btVector3 column2(m[0][2], m[1][2], m[2][2]);
+ btMatrix3x3 &basis(scaledBasis.getBasis());
+ btVector3 column0 = basis.getColumn(0);
+ btVector3 column1 = basis.getColumn(1);
+ btVector3 column2 = basis.getColumn(2);
+
+ // Check for zero scaling.
+ if (column0.fuzzyZero()) {
+ if (column1.fuzzyZero()) {
+ if (column2.fuzzyZero()) {
+ // All dimensions are fuzzy zero. Create a default basis.
+ column0 = btVector3(1, 0, 0);
+ column1 = btVector3(0, 1, 0);
+ column2 = btVector3(0, 0, 1);
+ } else { // Column 2 scale not fuzzy zero.
+ // Create two vectors orthogonal to row 2.
+ // Ensure that a default basis is created if row 2 = <0, 0, 1>
+ column1 = btVector3(0, column2[2], -column2[1]);
+ column0 = column1.cross(column2);
+ }
+ } else { // Column 1 scale not fuzzy zero.
+ if (column2.fuzzyZero()) {
+ // Create two vectors othogonal to column 1.
+ // Ensure that a default basis is created if column 1 = <0, 1, 0>
+ column0 = btVector3(column1[1], -column1[0], 0);
+ column2 = column0.cross(column1);
+ } else { // Column 1 and column 2 scales not fuzzy zero.
+ // Create column 0 orthogonal to column 1 and column 2.
+ column0 = column1.cross(column2);
+ }
+ }
+ } else { // Column 0 scale not fuzzy zero.
+ if (column1.fuzzyZero()) {
+ if (column2.fuzzyZero()) {
+ // Create two vectors orthogonal to column 0.
+ // Ensure that a default basis is created if column 0 = <1, 0, 0>
+ column2 = btVector3(-column0[2], 0, column0[0]);
+ column1 = column2.cross(column0);
+ } else { // Column 0 and column 2 scales not fuzzy zero.
+ // Create column 1 orthogonal to column 0 and column 2.
+ column1 = column2.cross(column0);
+ }
+ } else { // Column 0 and column 1 scales not fuzzy zero.
+ if (column2.fuzzyZero()) {
+ // Create column 2 orthogonal to column 0 and column 1.
+ column2 = column0.cross(column1);
+ }
+ }
+ }
+
+ // Normalize
column0.normalize();
column1.normalize();
column2.normalize();
- m.setValue(column0[0], column1[0], column2[0], column0[1], column1[1], column2[1], column0[2], column1[2], column2[2]);
+
+ basis.setValue(column0[0], column1[0], column2[0],
+ column0[1], column1[1], column2[1],
+ column0[2], column1[2], column2[2]);
}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 660e9afc5e..a3158a15e5 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -79,7 +79,7 @@ btTransform CollisionObjectBullet::ShapeWrapper::get_adjusted_transform() const
}
void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_scale) {
- if (bt_shape == nullptr) {
+ if (!bt_shape) {
if (active) {
bt_shape = shape->create_bt_shape(scale * body_scale);
} else {
@@ -88,13 +88,6 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s
}
}
-void CollisionObjectBullet::ShapeWrapper::release_bt_shape() {
- if (bt_shape != nullptr) {
- shape->destroy_bt_shape(bt_shape);
- bt_shape = nullptr;
- }
-}
-
CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
RIDBullet(),
type(p_type) {}
@@ -165,22 +158,6 @@ bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet
return !bt_collision_object->checkCollideWith(p_otherCollisionObject->bt_collision_object);
}
-void CollisionObjectBullet::reload_body() {
- needs_body_reload = true;
-}
-
-void CollisionObjectBullet::dispatch_callbacks() {}
-
-void CollisionObjectBullet::pre_process() {
- if (needs_body_reload) {
- do_reload_body();
- } else if (needs_collision_filters_reload) {
- do_reload_collision_filters();
- }
- needs_body_reload = false;
- needs_collision_filters_reload = false;
-}
-
void CollisionObjectBullet::set_collision_enabled(bool p_enabled) {
collisionsEnabled = p_enabled;
if (collisionsEnabled) {
@@ -254,7 +231,7 @@ void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform
}
void RigidCollisionObjectBullet::set_shape(int p_index, ShapeBullet *p_shape) {
- ShapeWrapper &shp = shapes[p_index];
+ ShapeWrapper &shp = shapes.write[p_index];
shp.shape->remove_owner(this);
p_shape->add_owner(this);
shp.shape = p_shape;
@@ -316,7 +293,7 @@ void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBod
void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) {
ERR_FAIL_INDEX(p_index, get_shape_count());
- shapes[p_index].set_transform(p_transform);
+ shapes.write[p_index].set_transform(p_transform);
shape_changed(p_index);
}
@@ -334,7 +311,7 @@ void RigidCollisionObjectBullet::set_shape_disabled(int p_index, bool p_disabled
if (shapes[p_index].active != p_disabled) {
return;
}
- shapes[p_index].active = !p_disabled;
+ shapes.write[p_index].active = !p_disabled;
shape_changed(p_index);
}
@@ -342,28 +319,16 @@ bool RigidCollisionObjectBullet::is_shape_disabled(int p_index) {
return !shapes[p_index].active;
}
-void RigidCollisionObjectBullet::pre_process() {
- if (need_shape_reload) {
- do_reload_shapes();
- need_shape_reload = false;
- }
- CollisionObjectBullet::pre_process();
-}
-
void RigidCollisionObjectBullet::shape_changed(int p_shape_index) {
- ShapeWrapper &shp = shapes[p_shape_index];
+ ShapeWrapper &shp = shapes.write[p_shape_index];
if (shp.bt_shape == mainShape) {
mainShape = nullptr;
}
- shp.release_bt_shape();
+ bulletdelete(shp.bt_shape);
reload_shapes();
}
void RigidCollisionObjectBullet::reload_shapes() {
- need_shape_reload = true;
-}
-
-void RigidCollisionObjectBullet::do_reload_shapes() {
if (mainShape && mainShape->isCompound()) {
// Destroy compound
bulletdelete(mainShape);
@@ -371,38 +336,41 @@ void RigidCollisionObjectBullet::do_reload_shapes() {
mainShape = nullptr;
+ ShapeWrapper *shpWrapper;
const int shape_count = shapes.size();
- // Reset all shapes if required
+ // Reset shape if required
if (force_shape_reset) {
for (int i(0); i < shape_count; ++i) {
- shapes[i].release_bt_shape();
+ shpWrapper = &shapes.write[i];
+ bulletdelete(shpWrapper->bt_shape);
}
force_shape_reset = false;
}
const btVector3 body_scale(get_bt_body_scale());
+ // Try to optimize by not using compound
if (1 == shape_count) {
- // Is it possible to optimize by not using compound?
- btTransform transform = shapes[0].get_adjusted_transform();
+ shpWrapper = &shapes.write[0];
+ btTransform transform = shpWrapper->get_adjusted_transform();
if (transform.getOrigin().isZero() && transform.getBasis() == transform.getBasis().getIdentity()) {
- shapes[0].claim_bt_shape(body_scale);
- mainShape = shapes[0].bt_shape;
+ shpWrapper->claim_bt_shape(body_scale);
+ mainShape = shpWrapper->bt_shape;
main_shape_changed();
- // Nothing more to do
return;
}
}
- // Optimization not possible use a compound shape.
+ // Optimization not possible use a compound shape
btCompoundShape *compoundShape = bulletnew(btCompoundShape(enableDynamicAabbTree, shape_count));
for (int i(0); i < shape_count; ++i) {
- shapes[i].claim_bt_shape(body_scale);
- btTransform scaled_shape_transform(shapes[i].get_adjusted_transform());
+ shpWrapper = &shapes.write[i];
+ shpWrapper->claim_bt_shape(body_scale);
+ btTransform scaled_shape_transform(shpWrapper->get_adjusted_transform());
scaled_shape_transform.getOrigin() *= body_scale;
- compoundShape->addChildShape(scaled_shape_transform, shapes[i].bt_shape);
+ compoundShape->addChildShape(scaled_shape_transform, shpWrapper->bt_shape);
}
compoundShape->recalculateLocalAabb();
@@ -416,10 +384,10 @@ void RigidCollisionObjectBullet::body_scale_changed() {
}
void RigidCollisionObjectBullet::internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody) {
- ShapeWrapper &shp = shapes[p_index];
+ ShapeWrapper &shp = shapes.write[p_index];
shp.shape->remove_owner(this, p_permanentlyFromThisBody);
if (shp.bt_shape == mainShape) {
mainShape = nullptr;
}
- shp.release_bt_shape();
+ bulletdelete(shp.bt_shape);
}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
index 920d80af23..e2d05f2c38 100644
--- a/modules/bullet/collision_object_bullet.h
+++ b/modules/bullet/collision_object_bullet.h
@@ -31,11 +31,10 @@
#ifndef COLLISION_OBJECT_BULLET_H
#define COLLISION_OBJECT_BULLET_H
-#include "core/local_vector.h"
#include "core/math/transform.h"
#include "core/math/vector3.h"
-#include "core/object.h"
-#include "core/vset.h"
+#include "core/object/class_db.h"
+#include "core/templates/vset.h"
#include "shape_owner_bullet.h"
#include <LinearMath/btTransform.h>
@@ -71,12 +70,11 @@ public:
struct ShapeWrapper {
ShapeBullet *shape = nullptr;
+ btCollisionShape *bt_shape = nullptr;
btTransform transform;
btVector3 scale;
bool active = true;
- btCollisionShape *bt_shape = nullptr;
- public:
ShapeWrapper() {}
ShapeWrapper(ShapeBullet *p_shape, const btTransform &p_transform, bool p_active) :
@@ -109,7 +107,6 @@ public:
btTransform get_adjusted_transform() const;
void claim_bt_shape(const btVector3 &body_scale);
- void release_bt_shape();
};
protected:
@@ -127,20 +124,13 @@ protected:
VSet<RID> exceptions;
- bool needs_body_reload = true;
- bool needs_collision_filters_reload = true;
-
/// This array is used to know all areas where this Object is overlapped in
/// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea)
/// This array is used mainly to know which area hold the pointer of this object
- LocalVector<AreaBullet *> areasOverlapped;
+ Vector<AreaBullet *> areasOverlapped;
bool isTransformChanged = false;
public:
- bool is_in_world = false;
- bool is_in_flush_queue = false;
-
-public:
CollisionObjectBullet(Type p_type);
virtual ~CollisionObjectBullet();
@@ -174,7 +164,7 @@ public:
_FORCE_INLINE_ void set_collision_layer(uint32_t p_layer) {
if (collisionLayer != p_layer) {
collisionLayer = p_layer;
- needs_collision_filters_reload = true;
+ on_collision_filters_change();
}
}
_FORCE_INLINE_ uint32_t get_collision_layer() const { return collisionLayer; }
@@ -182,32 +172,25 @@ public:
_FORCE_INLINE_ void set_collision_mask(uint32_t p_mask) {
if (collisionMask != p_mask) {
collisionMask = p_mask;
- needs_collision_filters_reload = true;
+ on_collision_filters_change();
}
}
_FORCE_INLINE_ uint32_t get_collision_mask() const { return collisionMask; }
- virtual void do_reload_collision_filters() = 0;
+ virtual void on_collision_filters_change() = 0;
_FORCE_INLINE_ bool test_collision_mask(CollisionObjectBullet *p_other) const {
return collisionLayer & p_other->collisionMask || p_other->collisionLayer & collisionMask;
}
- bool need_reload_body() const {
- return needs_body_reload;
- }
-
- void reload_body();
-
- virtual void do_reload_body() = 0;
+ virtual void reload_body() = 0;
virtual void set_space(SpaceBullet *p_space) = 0;
_FORCE_INLINE_ SpaceBullet *get_space() const { return space; }
virtual void on_collision_checker_start() = 0;
virtual void on_collision_checker_end() = 0;
- virtual void dispatch_callbacks();
- virtual void pre_process();
+ virtual void dispatch_callbacks() = 0;
void set_collision_enabled(bool p_enabled);
bool is_collisions_response_enabled();
@@ -231,15 +214,14 @@ public:
class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet {
protected:
btCollisionShape *mainShape = nullptr;
- LocalVector<ShapeWrapper> shapes;
- bool need_shape_reload = true;
+ Vector<ShapeWrapper> shapes;
public:
RigidCollisionObjectBullet(Type p_type) :
CollisionObjectBullet(p_type) {}
~RigidCollisionObjectBullet();
- _FORCE_INLINE_ const LocalVector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; }
+ _FORCE_INLINE_ const Vector<ShapeWrapper> &get_shapes_wrappers() const { return shapes; }
_FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; }
@@ -250,9 +232,9 @@ public:
ShapeBullet *get_shape(int p_index) const;
btCollisionShape *get_bt_shape(int p_index) const;
- virtual int find_shape(ShapeBullet *p_shape) const override;
+ int find_shape(ShapeBullet *p_shape) const;
- virtual void remove_shape_full(ShapeBullet *p_shape) override;
+ virtual void remove_shape_full(ShapeBullet *p_shape);
void remove_shape_full(int p_index);
void remove_all_shapes(bool p_permanentlyFromThisBody = false, bool p_force_not_reload = false);
@@ -264,15 +246,11 @@ public:
void set_shape_disabled(int p_index, bool p_disabled);
bool is_shape_disabled(int p_index);
- virtual void pre_process() override;
-
- virtual void shape_changed(int p_shape_index) override;
- virtual void reload_shapes() override;
- bool need_reload_shapes() const { return need_shape_reload; }
- virtual void do_reload_shapes();
+ virtual void shape_changed(int p_shape_index);
+ virtual void reload_shapes();
virtual void main_shape_changed() = 0;
- virtual void body_scale_changed() override;
+ virtual void body_scale_changed();
private:
void internal_shape_destroy(int p_index, bool p_permanentlyFromThisBody = false);
diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h
index 5a96268ee9..13e7255abf 100644
--- a/modules/bullet/godot_collision_dispatcher.h
+++ b/modules/bullet/godot_collision_dispatcher.h
@@ -31,7 +31,7 @@
#ifndef GODOT_COLLISION_DISPATCHER_H
#define GODOT_COLLISION_DISPATCHER_H
-#include "core/int_types.h"
+#include <cstdint>
#include <btBulletDynamicsCommon.h>
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
index 009d0dff63..d29b699ecd 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -31,8 +31,8 @@
#include "register_types.h"
#include "bullet_physics_server.h"
-#include "core/class_db.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/object/class_db.h"
/**
@author AndreaCatania
diff --git a/modules/bullet/rid_bullet.h b/modules/bullet/rid_bullet.h
index 3551ca05f9..0db09b2b78 100644
--- a/modules/bullet/rid_bullet.h
+++ b/modules/bullet/rid_bullet.h
@@ -31,7 +31,7 @@
#ifndef RID_BULLET_H
#define RID_BULLET_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
/**
@author AndreaCatania
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 32c3240a35..eb599df74c 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -51,7 +51,9 @@
BulletPhysicsDirectBodyState3D *BulletPhysicsDirectBodyState3D::singleton = nullptr;
Vector3 BulletPhysicsDirectBodyState3D::get_total_gravity() const {
- return body->total_gravity;
+ Vector3 gVec;
+ B_TO_G(body->btBody->getGravity(), gVec);
+ return gVec;
}
float BulletPhysicsDirectBodyState3D::get_total_angular_damp() const {
@@ -181,7 +183,7 @@ int BulletPhysicsDirectBodyState3D::get_contact_collider_shape(int p_contact_idx
}
Vector3 BulletPhysicsDirectBodyState3D::get_contact_collider_velocity_at_position(int p_contact_idx) const {
- RigidBodyBullet::CollisionData &colDat = body->collisions[p_contact_idx];
+ RigidBodyBullet::CollisionData &colDat = body->collisions.write[p_contact_idx];
btVector3 hitLocation;
G_TO_B(colDat.hitLocalLocation, hitLocation);
@@ -211,7 +213,7 @@ void RigidBodyBullet::KinematicUtilities::setSafeMargin(btScalar p_margin) {
}
void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
- const LocalVector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers());
+ const Vector<CollisionObjectBullet::ShapeWrapper> &shapes_wrappers(owner->get_shapes_wrappers());
const int shapes_count = shapes_wrappers.size();
just_delete_shapes(shapes_count);
@@ -226,8 +228,8 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
continue;
}
- shapes[i].transform = shape_wrapper->transform;
- shapes[i].transform.getOrigin() *= owner_scale;
+ shapes.write[i].transform = shape_wrapper->transform;
+ shapes.write[i].transform.getOrigin() *= owner_scale;
switch (shape_wrapper->shape->get_type()) {
case PhysicsServer3D::SHAPE_SPHERE:
case PhysicsServer3D::SHAPE_BOX:
@@ -235,11 +237,11 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
case PhysicsServer3D::SHAPE_CYLINDER:
case PhysicsServer3D::SHAPE_CONVEX_POLYGON:
case PhysicsServer3D::SHAPE_RAY: {
- shapes[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->internal_create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
+ shapes.write[i].shape = static_cast<btConvexShape *>(shape_wrapper->shape->create_bt_shape(owner_scale * shape_wrapper->scale, safe_margin));
} break;
default:
WARN_PRINT("This shape is not supported for kinematic collision.");
- shapes[i].shape = nullptr;
+ shapes.write[i].shape = nullptr;
}
}
}
@@ -247,7 +249,7 @@ void RigidBodyBullet::KinematicUtilities::copyAllOwnerShapes() {
void RigidBodyBullet::KinematicUtilities::just_delete_shapes(int new_size) {
for (int i = shapes.size() - 1; 0 <= i; --i) {
if (shapes[i].shape) {
- bulletdelete(shapes[i].shape);
+ bulletdelete(shapes.write[i].shape);
}
}
shapes.resize(new_size);
@@ -262,6 +264,7 @@ RigidBodyBullet::RigidBodyBullet() :
btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, nullptr, localInertia);
btBody = bulletnew(btRigidBody(cInfo));
+ btBody->setFriction(1.0);
reload_shapes();
setupBulletCollisionObject(btBody);
@@ -269,8 +272,8 @@ RigidBodyBullet::RigidBodyBullet() :
reload_axis_lock();
areasWhereIam.resize(maxAreasWhereIam);
- for (uint32_t i = 0; i < areasWhereIam.size(); i += 1) {
- areasWhereIam[i] = nullptr;
+ for (int i = areasWhereIam.size() - 1; 0 <= i; --i) {
+ areasWhereIam.write[i] = nullptr;
}
btBody->setSleepingThresholds(0.2, 0.2);
@@ -305,7 +308,7 @@ void RigidBodyBullet::main_shape_changed() {
set_continuous_collision_detection(is_continuous_collision_detection_enabled()); // Reset
}
-void RigidBodyBullet::do_reload_body() {
+void RigidBodyBullet::reload_body() {
if (space) {
space->remove_rigid_body(this);
if (get_main_shape()) {
@@ -324,24 +327,23 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) {
assert_no_constraints();
// Remove this object form the physics world
- space->unregister_collision_object(this);
space->remove_rigid_body(this);
}
space = p_space;
if (space) {
- space->register_collision_object(this);
- reload_body();
- space->add_to_flush_queue(this);
+ space->add_rigid_body(this);
}
}
void RigidBodyBullet::dispatch_callbacks() {
- RigidCollisionObjectBullet::dispatch_callbacks();
-
/// The check isFirstTransformChanged is necessary in order to call integrated forces only when the first transform is sent
if ((btBody->isKinematicObject() || btBody->isActive() || previousActiveState != btBody->isActive()) && force_integration_callback && can_integrate_forces) {
+ if (omit_forces_integration) {
+ btBody->clearForces();
+ }
+
BulletPhysicsDirectBodyState3D *bodyDirect = BulletPhysicsDirectBodyState3D::get_singleton(this);
Variant variantBodyDirect = bodyDirect;
@@ -359,22 +361,16 @@ void RigidBodyBullet::dispatch_callbacks() {
}
}
- previousActiveState = btBody->isActive();
-}
-
-void RigidBodyBullet::pre_process() {
- RigidCollisionObjectBullet::pre_process();
-
if (isScratchedSpaceOverrideModificator || 0 < countGravityPointSpaces) {
isScratchedSpaceOverrideModificator = false;
reload_space_override_modificator();
}
- if (is_active()) {
- /// Lock axis
- btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor());
- btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor());
- }
+ /// Lock axis
+ btBody->setLinearVelocity(btBody->getLinearVelocity() * btBody->getLinearFactor());
+ btBody->setAngularVelocity(btBody->getAngularVelocity() * btBody->getAngularFactor());
+
+ previousActiveState = btBody->isActive();
}
void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata) {
@@ -395,7 +391,7 @@ void RigidBodyBullet::scratch_space_override_modificator() {
isScratchedSpaceOverrideModificator = true;
}
-void RigidBodyBullet::do_reload_collision_filters() {
+void RigidBodyBullet::on_collision_filters_change() {
if (space) {
space->reload_collision_filters(this);
}
@@ -408,15 +404,14 @@ void RigidBodyBullet::on_collision_checker_start() {
collisionsCount = 0;
// Swap array
- SWAP(prev_collision_traces, curr_collision_traces);
+ Vector<RigidBodyBullet *> *s = prev_collision_traces;
+ prev_collision_traces = curr_collision_traces;
+ curr_collision_traces = s;
}
void RigidBodyBullet::on_collision_checker_end() {
// Always true if active and not a static or kinematic body
isTransformChanged = btBody->isActive() && !btBody->isStaticOrKinematicObject();
- if (isTransformChanged && space != nullptr) {
- space->add_to_flush_queue(this);
- }
}
bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) {
@@ -424,7 +419,7 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const
return false;
}
- CollisionData &cd = collisions[collisionsCount];
+ CollisionData &cd = collisions.write[collisionsCount];
cd.hitLocalLocation = p_hitLocalLocation;
cd.otherObject = p_otherObject;
cd.hitWorldLocation = p_hitWorldLocation;
@@ -433,7 +428,7 @@ bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const
cd.other_object_shape = p_other_shape_index;
cd.local_shape = p_local_shape_index;
- (*curr_collision_traces)[collisionsCount] = p_otherObject;
+ curr_collision_traces->write[collisionsCount] = p_otherObject;
++collisionsCount;
return true;
@@ -468,7 +463,6 @@ bool RigidBodyBullet::is_active() const {
void RigidBodyBullet::set_omit_forces_integration(bool p_omit) {
omit_forces_integration = p_omit;
- scratch_space_override_modificator();
}
void RigidBodyBullet::set_param(PhysicsServer3D::BodyParameter p_param, real_t p_value) {
@@ -811,8 +805,8 @@ const btTransform &RigidBodyBullet::get_transform__bullet() const {
}
}
-void RigidBodyBullet::do_reload_shapes() {
- RigidCollisionObjectBullet::do_reload_shapes();
+void RigidBodyBullet::reload_shapes() {
+ RigidCollisionObjectBullet::reload_shapes();
const btScalar invMass = btBody->getInvMass();
const btScalar mass = invMass == 0 ? 0 : 1 / invMass;
@@ -844,15 +838,15 @@ void RigidBodyBullet::on_enter_area(AreaBullet *p_area) {
for (int i = 0; i < areaWhereIamCount; ++i) {
if (nullptr == areasWhereIam[i]) {
// This area has the highest priority
- areasWhereIam[i] = p_area;
+ areasWhereIam.write[i] = p_area;
break;
} else {
if (areasWhereIam[i]->get_spOv_priority() > p_area->get_spOv_priority()) {
// The position was found, just shift all elements
- for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam[j + 1] = areasWhereIam[j];
+ for (int j = areaWhereIamCount; j > i; j--) {
+ areasWhereIam.write[j] = areasWhereIam[j - 1];
}
- areasWhereIam[i] = p_area;
+ areasWhereIam.write[i] = p_area;
break;
}
}
@@ -876,7 +870,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
if (p_area == areasWhereIam[i]) {
// The area was found, just shift down all elements
for (int j = i; j < areaWhereIamCount; ++j) {
- areasWhereIam[j] = areasWhereIam[j + 1];
+ areasWhereIam.write[j] = areasWhereIam[j + 1];
}
wasTheAreaFound = true;
break;
@@ -889,7 +883,7 @@ void RigidBodyBullet::on_exit_area(AreaBullet *p_area) {
}
--areaWhereIamCount;
- areasWhereIam[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe
+ areasWhereIam.write[areaWhereIamCount] = nullptr; // Even if this is not required, I clear the last element to be safe
if (PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED != p_area->get_spOv_mode()) {
scratch_space_override_modificator();
}
@@ -901,31 +895,36 @@ void RigidBodyBullet::reload_space_override_modificator() {
return;
}
- Vector3 newGravity;
+ Vector3 newGravity(0.0, 0.0, 0.0);
real_t newLinearDamp = MAX(0.0, linearDamp);
real_t newAngularDamp = MAX(0.0, angularDamp);
+ AreaBullet *currentArea;
+ // Variable used to calculate new gravity for gravity point areas, it is pointed by currentGravity pointer
+ Vector3 support_gravity(0, 0, 0);
+
bool stopped = false;
- for (int i = 0; i < areaWhereIamCount && !stopped; i += 1) {
- AreaBullet *currentArea = areasWhereIam[i];
+ for (int i = areaWhereIamCount - 1; (0 <= i) && !stopped; --i) {
+ currentArea = areasWhereIam[i];
if (!currentArea || PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED == currentArea->get_spOv_mode()) {
continue;
}
- Vector3 support_gravity;
-
/// Here is calculated the gravity
if (currentArea->is_spOv_gravityPoint()) {
/// It calculates the direction of new gravity
support_gravity = currentArea->get_transform().xform(currentArea->get_spOv_gravityVec()) - get_transform().get_origin();
-
- const real_t distanceMag = support_gravity.length();
+ real_t distanceMag = support_gravity.length();
// Normalized in this way to avoid the double call of function "length()"
if (distanceMag == 0) {
- support_gravity = Vector3();
+ support_gravity.x = 0;
+ support_gravity.y = 0;
+ support_gravity.z = 0;
} else {
- support_gravity /= distanceMag;
+ support_gravity.x /= distanceMag;
+ support_gravity.y /= distanceMag;
+ support_gravity.z /= distanceMag;
}
/// Here is calculated the final gravity
@@ -987,17 +986,10 @@ void RigidBodyBullet::reload_space_override_modificator() {
newAngularDamp += space->get_angular_damp();
}
- total_gravity = newGravity;
-
- if (omit_forces_integration) {
- // Custom behaviour.
- btBody->setGravity(btVector3(0, 0, 0));
- } else {
- btVector3 newBtGravity;
- G_TO_B(newGravity * gravity_scale, newBtGravity);
- btBody->setGravity(newBtGravity);
- }
+ btVector3 newBtGravity;
+ G_TO_B(newGravity * gravity_scale, newBtGravity);
+ btBody->setGravity(newBtGravity);
btBody->setDamping(newLinearDamp, newAngularDamp);
}
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index 047645677b..c643611397 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -171,7 +171,7 @@ public:
struct KinematicUtilities {
RigidBodyBullet *owner;
btScalar safe_margin;
- LocalVector<KinematicShape> shapes;
+ Vector<KinematicShape> shapes;
KinematicUtilities(RigidBodyBullet *p_owner);
~KinematicUtilities();
@@ -193,7 +193,6 @@ private:
PhysicsServer3D::BodyMode mode;
GodotMotionState *godotMotionState;
btRigidBody *btBody;
- Vector3 total_gravity;
uint16_t locked_axis = 0;
real_t mass = 1;
real_t gravity_scale = 1;
@@ -203,18 +202,18 @@ private:
bool omit_forces_integration = false;
bool can_integrate_forces = false;
- LocalVector<CollisionData> collisions;
- LocalVector<RigidBodyBullet *> collision_traces_1;
- LocalVector<RigidBodyBullet *> collision_traces_2;
- LocalVector<RigidBodyBullet *> *prev_collision_traces;
- LocalVector<RigidBodyBullet *> *curr_collision_traces;
+ Vector<CollisionData> collisions;
+ Vector<RigidBodyBullet *> collision_traces_1;
+ Vector<RigidBodyBullet *> collision_traces_2;
+ Vector<RigidBodyBullet *> *prev_collision_traces;
+ Vector<RigidBodyBullet *> *curr_collision_traces;
// these parameters are used to avoid vector resize
- uint32_t maxCollisionsDetection = 0;
- uint32_t collisionsCount = 0;
- uint32_t prev_collision_count = 0;
+ int maxCollisionsDetection = 0;
+ int collisionsCount = 0;
+ int prev_collision_count = 0;
- LocalVector<AreaBullet *> areasWhereIam;
+ Vector<AreaBullet *> areasWhereIam;
// these parameters are used to avoid vector resize
int maxAreasWhereIam = 10;
int areaWhereIamCount = 0;
@@ -236,20 +235,21 @@ public:
_FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; }
- virtual void main_shape_changed() override;
- virtual void do_reload_body() override;
- virtual void set_space(SpaceBullet *p_space) override;
+ virtual void main_shape_changed();
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
- virtual void dispatch_callbacks() override;
- virtual void pre_process() override;
+ virtual void dispatch_callbacks();
void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch_space_override_modificator();
- virtual void do_reload_collision_filters() override;
- virtual void on_collision_checker_start() override;
- virtual void on_collision_checker_end() override;
+ virtual void on_collision_filters_change();
+ virtual void on_collision_checker_start();
+ virtual void on_collision_checker_end();
+
+ void set_max_collisions_detection(int p_maxCollisionsDetection) {
+ ERR_FAIL_COND(0 > p_maxCollisionsDetection);
- void set_max_collisions_detection(uint32_t p_maxCollisionsDetection) {
maxCollisionsDetection = p_maxCollisionsDetection;
collisions.resize(p_maxCollisionsDetection);
@@ -312,19 +312,19 @@ public:
void set_angular_velocity(const Vector3 &p_velocity);
Vector3 get_angular_velocity() const;
- virtual void set_transform__bullet(const btTransform &p_global_transform) override;
- virtual const btTransform &get_transform__bullet() const override;
+ virtual void set_transform__bullet(const btTransform &p_global_transform);
+ virtual const btTransform &get_transform__bullet() const;
- virtual void do_reload_shapes() override;
+ virtual void reload_shapes();
- virtual void on_enter_area(AreaBullet *p_area) override;
- virtual void on_exit_area(AreaBullet *p_area) override;
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
void reload_space_override_modificator();
/// Kinematic
void reload_kinematic_shapes();
- virtual void notify_transform_changed() override;
+ virtual void notify_transform_changed();
private:
void _internal_set_mass(real_t p_mass);
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 274493ed17..c7b761e92a 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -34,7 +34,7 @@
#include "bullet_physics_server.h"
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "shape_owner_bullet.h"
#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
@@ -46,15 +46,9 @@
@author AndreaCatania
*/
-ShapeBullet::ShapeBullet() {
-}
+ShapeBullet::ShapeBullet() {}
-ShapeBullet::~ShapeBullet() {
- if (default_shape != nullptr) {
- bulletdelete(default_shape);
- default_shape = nullptr;
- }
-}
+ShapeBullet::~ShapeBullet() {}
btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 s;
@@ -62,22 +56,6 @@ btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale,
return create_bt_shape(s, p_extra_edge);
}
-btCollisionShape *ShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
- if (p_extra_edge == 0.0 && (p_implicit_scale - btVector3(1, 1, 1)).length2() <= CMP_EPSILON) {
- return default_shape;
- }
-
- return internal_create_bt_shape(p_implicit_scale, p_extra_edge);
-}
-
-void ShapeBullet::destroy_bt_shape(btCollisionShape *p_shape) const {
- if (p_shape != default_shape && p_shape != old_default_shape) {
- if (likely(p_shape != nullptr)) {
- bulletdelete(p_shape);
- }
- }
-}
-
btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
p_btShape->setUserPointer(const_cast<ShapeBullet *>(this));
p_btShape->setMargin(margin);
@@ -85,21 +63,10 @@ btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
}
void ShapeBullet::notifyShapeChanged() {
- // Store the old shape ptr so to not lose the reference pointer.
- old_default_shape = default_shape;
- // Create the new default shape with the new data.
- default_shape = internal_create_bt_shape(btVector3(1, 1, 1));
-
for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) {
ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E->key());
owner->shape_changed(owner->find_shape(this));
}
-
- if (old_default_shape) {
- // At this point now one has the old default shape; just delete it.
- bulletdelete(old_default_shape);
- old_default_shape = nullptr;
- }
}
void ShapeBullet::add_owner(ShapeOwnerBullet *p_owner) {
@@ -219,7 +186,7 @@ void PlaneShapeBullet::setup(const Plane &p_plane) {
notifyShapeChanged();
}
-btCollisionShape *PlaneShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 btPlaneNormal;
G_TO_B(plane.normal, btPlaneNormal);
return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
@@ -247,7 +214,7 @@ void SphereShapeBullet::setup(real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *SphereShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_extra_edge));
}
@@ -274,7 +241,7 @@ void BoxShapeBullet::setup(const Vector3 &p_half_extents) {
notifyShapeChanged();
}
-btCollisionShape *BoxShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_extra_edge, p_extra_edge, p_extra_edge)));
}
@@ -307,8 +274,8 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CapsuleShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
- return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge));
+btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1]));
}
/* Cylinder */
@@ -340,7 +307,7 @@ void CylinderShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CylinderShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *CylinderShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
return prepare(ShapeBullet::create_shape_cylinder(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
}
@@ -382,7 +349,7 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) {
notifyShapeChanged();
}
-btCollisionShape *ConvexPolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
if (!vertices.size()) {
// This is necessary since 0 vertices
return prepare(ShapeBullet::create_shape_empty());
@@ -464,7 +431,7 @@ void ConcavePolygonShapeBullet::setup(Vector<Vector3> p_faces) {
notifyShapeChanged();
}
-btCollisionShape *ConcavePolygonShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape);
if (!cs) {
// This is necessary since if 0 faces the creation of concave return null
@@ -591,7 +558,7 @@ void HeightMapShapeBullet::setup(Vector<real_t> &p_heights, int p_width, int p_d
notifyShapeChanged();
}
-btCollisionShape *HeightMapShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
@@ -624,6 +591,6 @@ void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) {
notifyShapeChanged();
}
-btCollisionShape *RayShapeBullet::internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_extra_edge, slips_on_slope));
}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 6ca4d36a23..1c29dc1b1f 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -32,7 +32,7 @@
#define SHAPE_BULLET_H
#include "core/math/geometry_3d.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.h"
@@ -53,10 +53,6 @@ class ShapeBullet : public RIDBullet {
Map<ShapeOwnerBullet *, int> owners;
real_t margin = 0.04;
- // Contains the default shape.
- btCollisionShape *default_shape = nullptr;
- btCollisionShape *old_default_shape = nullptr;
-
protected:
/// return self
btCollisionShape *prepare(btCollisionShape *p_btShape) const;
@@ -67,11 +63,7 @@ public:
virtual ~ShapeBullet();
btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge = 0);
- btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
-
- void destroy_bt_shape(btCollisionShape *p_shape) const;
-
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
void add_owner(ShapeOwnerBullet *p_owner);
void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false);
@@ -110,7 +102,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Plane &p_plane);
@@ -126,7 +118,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_radius);
@@ -142,7 +134,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector3 &p_half_extents);
@@ -160,7 +152,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -178,7 +170,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -194,7 +186,7 @@ public:
void get_vertices(Vector<Vector3> &out_vertices);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector<Vector3> &p_vertices);
@@ -212,7 +204,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(Vector<Vector3> p_faces);
@@ -231,7 +223,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
@@ -247,7 +239,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer3D::ShapeType get_type() const;
- virtual btCollisionShape *internal_create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_length, bool p_slips_on_slope);
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index ee48b3c5f0..6794d6c313 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -41,7 +41,7 @@ SoftBodyBullet::SoftBodyBullet() :
SoftBodyBullet::~SoftBodyBullet() {
}
-void SoftBodyBullet::do_reload_body() {
+void SoftBodyBullet::reload_body() {
if (space) {
space->remove_soft_body(this);
space->add_soft_body(this);
@@ -51,15 +51,13 @@ void SoftBodyBullet::do_reload_body() {
void SoftBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
isScratched = false;
- space->unregister_collision_object(this);
space->remove_soft_body(this);
}
space = p_space;
if (space) {
- space->register_collision_object(this);
- reload_body();
+ space->add_soft_body(this);
}
}
@@ -346,14 +344,14 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
indices_table.push_back(Vector<int>());
}
- indices_table[vertex_id].push_back(vs_vertex_index);
+ indices_table.write[vertex_id].push_back(vs_vertex_index);
vs_indices_to_physics_table.push_back(vertex_id);
}
}
const int indices_map_size(indices_table.size());
- LocalVector<btScalar> bt_vertices;
+ Vector<btScalar> bt_vertices;
{ // Parse vertices to bullet
@@ -361,13 +359,13 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
const Vector3 *p_vertices_read = p_vertices.ptr();
for (int i = 0; i < indices_map_size; ++i) {
- bt_vertices[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
- bt_vertices[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
- bt_vertices[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
+ bt_vertices.write[3 * i + 0] = p_vertices_read[indices_table[i][0]].x;
+ bt_vertices.write[3 * i + 1] = p_vertices_read[indices_table[i][0]].y;
+ bt_vertices.write[3 * i + 2] = p_vertices_read[indices_table[i][0]].z;
}
}
- LocalVector<int> bt_triangles;
+ Vector<int> bt_triangles;
const int triangles_size(p_indices.size() / 3);
{ // Parse indices
@@ -377,9 +375,9 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
const int *p_indices_read = p_indices.ptr();
for (int i = 0; i < triangles_size; ++i) {
- bt_triangles[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
- bt_triangles[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
- bt_triangles[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
+ bt_triangles.write[3 * i + 0] = vs_indices_to_physics_table[p_indices_read[3 * i + 2]];
+ bt_triangles.write[3 * i + 1] = vs_indices_to_physics_table[p_indices_read[3 * i + 1]];
+ bt_triangles.write[3 * i + 2] = vs_indices_to_physics_table[p_indices_read[3 * i + 0]];
}
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index 229204b539..da8a2412ed 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -32,6 +32,7 @@
#define SOFT_BODY_BULLET_H
#include "collision_object_bullet.h"
+#include "scene/resources/material.h" // TODO remove this please
#ifdef None
/// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet
@@ -57,7 +58,7 @@
class SoftBodyBullet : public CollisionObjectBullet {
private:
btSoftBody *bt_soft_body = nullptr;
- LocalVector<Vector<int>> indices_table;
+ Vector<Vector<int>> indices_table;
btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
bool isScratched = false;
@@ -72,7 +73,7 @@ private:
real_t pose_matching_coefficient = 0.; // [0,1]
real_t damping_coefficient = 0.01; // [0,1]
real_t drag_coefficient = 0.; // [0,1]
- LocalVector<int> pinned_nodes;
+ Vector<int> pinned_nodes;
// Other property to add
//btScalar kVC; // Volume conversation coefficient [0,+inf]
@@ -86,14 +87,15 @@ public:
SoftBodyBullet();
~SoftBodyBullet();
- virtual void do_reload_body() override;
- virtual void set_space(SpaceBullet *p_space) override;
+ virtual void reload_body();
+ virtual void set_space(SpaceBullet *p_space);
- virtual void do_reload_collision_filters() override {}
- virtual void on_collision_checker_start() override {}
- virtual void on_collision_checker_end() override {}
- virtual void on_enter_area(AreaBullet *p_area) override;
- virtual void on_exit_area(AreaBullet *p_area) override;
+ virtual void dispatch_callbacks() {}
+ virtual void on_collision_filters_change() {}
+ virtual void on_collision_checker_start() {}
+ virtual void on_collision_checker_end() {}
+ virtual void on_enter_area(AreaBullet *p_area);
+ virtual void on_exit_area(AreaBullet *p_area);
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index d0515e7c97..abad1beacb 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -34,8 +34,8 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
#include "constraint_bullet.h"
-#include "core/project_settings.h"
-#include "core/ustring.h"
+#include "core/config/project_settings.h"
+#include "core/string/ustring.h"
#include "godot_collision_configuration.h"
#include "godot_collision_dispatcher.h"
#include "rigid_body_bullet.h"
@@ -127,7 +127,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return 0;
}
@@ -147,7 +147,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
btQuery.m_closestDistanceThreshold = 0;
space->dynamicsWorld->contactTest(&collision_object, btQuery);
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btConvex);
return btQuery.m_count;
}
@@ -163,7 +163,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
if (!btShape->isConvex()) {
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
@@ -177,7 +177,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
bt_xform_to.getOrigin() += bt_motion;
if ((bt_xform_to.getOrigin() - bt_xform_from.getOrigin()).fuzzyZero()) {
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btShape);
return false;
}
@@ -207,7 +207,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
r_closest_unsafe = 1.0f;
}
- shape->destroy_bt_shape(btShape);
+ bulletdelete(bt_convex_shape);
return true; // Mean success
}
@@ -222,7 +222,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
@@ -243,7 +243,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
space->dynamicsWorld->contactTest(&collision_object, btQuery);
r_result_count = btQuery.m_count;
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btConvex);
return btQuery.m_count;
}
@@ -254,7 +254,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btShape);
ERR_PRINT("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
return false;
}
@@ -274,7 +274,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
btQuery.m_closestDistanceThreshold = 0;
space->dynamicsWorld->contactTest(&collision_object, btQuery);
- shape->destroy_bt_shape(btShape);
+ bulletdelete(btConvex);
if (btQuery.m_collided) {
if (btCollisionObject::CO_RIGID_BODY == btQuery.m_rest_info_collision_object->getInternalType()) {
@@ -349,46 +349,14 @@ SpaceBullet::~SpaceBullet() {
destroy_world();
}
-void SpaceBullet::add_to_pre_flush_queue(CollisionObjectBullet *p_co) {
- if (p_co->is_in_flush_queue == false) {
- p_co->is_in_flush_queue = true;
- queue_pre_flush.push_back(p_co);
- }
-}
-
-void SpaceBullet::add_to_flush_queue(CollisionObjectBullet *p_co) {
- if (p_co->is_in_flush_queue == false) {
- p_co->is_in_flush_queue = true;
- queue_flush.push_back(p_co);
- }
-}
-
-void SpaceBullet::remove_from_any_queue(CollisionObjectBullet *p_co) {
- if (p_co->is_in_flush_queue) {
- p_co->is_in_flush_queue = false;
- queue_pre_flush.erase(p_co);
- queue_flush.erase(p_co);
- }
-}
-
void SpaceBullet::flush_queries() {
- for (uint32_t i = 0; i < queue_pre_flush.size(); i += 1) {
- queue_pre_flush[i]->dispatch_callbacks();
- queue_pre_flush[i]->is_in_flush_queue = false;
- }
- for (uint32_t i = 0; i < queue_flush.size(); i += 1) {
- queue_flush[i]->dispatch_callbacks();
- queue_flush[i]->is_in_flush_queue = false;
+ const btCollisionObjectArray &colObjArray = dynamicsWorld->getCollisionObjectArray();
+ for (int i = colObjArray.size() - 1; 0 <= i; --i) {
+ static_cast<CollisionObjectBullet *>(colObjArray[i]->getUserPointer())->dispatch_callbacks();
}
- queue_pre_flush.clear();
- queue_flush.clear();
}
void SpaceBullet::step(real_t p_delta_time) {
- for (uint32_t i = 0; i < collision_objects.size(); i += 1) {
- collision_objects[i]->pre_process();
- }
-
delta_time = p_delta_time;
dynamicsWorld->stepSimulation(p_delta_time, 0, 0);
}
@@ -481,30 +449,16 @@ real_t SpaceBullet::get_param(PhysicsServer3D::SpaceParameter p_param) {
}
void SpaceBullet::add_area(AreaBullet *p_area) {
-#ifdef TOOLS_ENABLED
- // This never happen, and there is no way for the user to trigger it.
- // If in future a bug is introduced into this bullet integration and this
- // function is called twice, the crash will notify the developer that will
- // fix it even before do the eventual PR.
- CRASH_COND(p_area->is_in_world);
-#endif
areas.push_back(p_area);
dynamicsWorld->addCollisionObject(p_area->get_bt_ghost(), p_area->get_collision_layer(), p_area->get_collision_mask());
- p_area->is_in_world = true;
}
void SpaceBullet::remove_area(AreaBullet *p_area) {
- if (p_area->is_in_world) {
- areas.erase(p_area);
- dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
- p_area->is_in_world = false;
- }
+ areas.erase(p_area);
+ dynamicsWorld->removeCollisionObject(p_area->get_bt_ghost());
}
void SpaceBullet::reload_collision_filters(AreaBullet *p_area) {
- if (p_area->is_in_world == false) {
- return;
- }
btGhostObject *ghost_object = p_area->get_bt_ghost();
btBroadphaseProxy *ghost_proxy = ghost_object->getBroadphaseHandle();
@@ -514,47 +468,24 @@ void SpaceBullet::reload_collision_filters(AreaBullet *p_area) {
dynamicsWorld->refreshBroadphaseProxy(ghost_object);
}
-void SpaceBullet::register_collision_object(CollisionObjectBullet *p_object) {
- collision_objects.push_back(p_object);
-}
-
-void SpaceBullet::unregister_collision_object(CollisionObjectBullet *p_object) {
- remove_from_any_queue(p_object);
- collision_objects.erase(p_object);
-}
-
void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) {
-#ifdef TOOLS_ENABLED
- // This never happen, and there is no way for the user to trigger it.
- // If in future a bug is introduced into this bullet integration and this
- // function is called twice, the crash will notify the developer that will
- // fix it even before do the eventual PR.
- CRASH_COND(p_body->is_in_world);
-#endif
if (p_body->is_static()) {
dynamicsWorld->addCollisionObject(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
} else {
dynamicsWorld->addRigidBody(p_body->get_bt_rigid_body(), p_body->get_collision_layer(), p_body->get_collision_mask());
p_body->scratch_space_override_modificator();
}
- p_body->is_in_world = true;
}
void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) {
- if (p_body->is_in_world) {
- if (p_body->is_static()) {
- dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
- } else {
- dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
- }
- p_body->is_in_world = false;
+ if (p_body->is_static()) {
+ dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
+ } else {
+ dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
}
}
void SpaceBullet::reload_collision_filters(RigidBodyBullet *p_body) {
- if (p_body->is_in_world == false) {
- return;
- }
btRigidBody *rigid_body = p_body->get_bt_rigid_body();
btBroadphaseProxy *body_proxy = rigid_body->getBroadphaseProxy();
@@ -734,7 +665,7 @@ void SpaceBullet::check_ghost_overlaps() {
/// 1. Reset all states
for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
- AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects[i];
+ AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i];
// This check prevent the overwrite of ENTER state
// if this function is called more times before dispatchCallbacks
if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) {
@@ -959,8 +890,8 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
SceneTree::get_singleton()->get_current_scene()->add_child(motionVec);
SceneTree::get_singleton()->get_current_scene()->add_child(normalLine);
- motionVec->set_as_toplevel(true);
- normalLine->set_as_toplevel(true);
+ motionVec->set_as_top_level(true);
+ normalLine->set_as_top_level(true);
red_mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
red_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 897f902fe1..e362f27d39 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -31,8 +31,8 @@
#ifndef SPACE_BULLET_H
#define SPACE_BULLET_H
-#include "core/local_vector.h"
-#include "core/variant.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "godot_result_callbacks.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.h"
@@ -110,23 +110,16 @@ class SpaceBullet : public RIDBullet {
real_t linear_damp = 0.0;
real_t angular_damp = 0.0;
- LocalVector<CollisionObjectBullet *> queue_pre_flush;
- LocalVector<CollisionObjectBullet *> queue_flush;
- LocalVector<CollisionObjectBullet *> collision_objects;
- LocalVector<AreaBullet *> areas;
+ Vector<AreaBullet *> areas;
- LocalVector<Vector3> contactDebug;
- uint32_t contactDebugCount = 0;
+ Vector<Vector3> contactDebug;
+ int contactDebugCount = 0;
real_t delta_time = 0.;
public:
SpaceBullet();
virtual ~SpaceBullet();
- void add_to_flush_queue(CollisionObjectBullet *p_co);
- void add_to_pre_flush_queue(CollisionObjectBullet *p_co);
- void remove_from_any_queue(CollisionObjectBullet *p_co);
-
void flush_queries();
real_t get_delta_time() { return delta_time; }
void step(real_t p_delta_time);
@@ -157,9 +150,6 @@ public:
void remove_area(AreaBullet *p_area);
void reload_collision_filters(AreaBullet *p_area);
- void register_collision_object(CollisionObjectBullet *p_object);
- void unregister_collision_object(CollisionObjectBullet *p_object);
-
void add_rigid_body(RigidBodyBullet *p_body);
void remove_rigid_body(RigidBodyBullet *p_body);
void reload_collision_filters(RigidBodyBullet *p_body);
@@ -183,7 +173,7 @@ public:
}
_FORCE_INLINE_ void add_debug_contact(const Vector3 &p_contact) {
if (contactDebugCount < contactDebug.size()) {
- contactDebug[contactDebugCount++] = p_contact;
+ contactDebug.write[contactDebugCount++] = p_contact;
}
}
_FORCE_INLINE_ Vector<Vector3> get_debug_contacts() { return contactDebug; }
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index 631a65bde2..de97724d09 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -5,17 +5,7 @@ Import("env_modules")
env_camera = env_modules.Clone()
-if env["platform"] == "iphone":
- # (iOS) Enable module support
- env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
-
- # (iOS) Build as separate static library
- modules_sources = []
- env_camera.add_source_files(modules_sources, "register_types.cpp")
- env_camera.add_source_files(modules_sources, "camera_ios.mm")
- mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
-
-elif env["platform"] == "windows":
+if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
diff --git a/modules/camera/config.py b/modules/camera/config.py
index 87d7542741..8a22751aa7 100644
--- a/modules/camera/config.py
+++ b/modules/camera/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return platform == "iphone" or platform == "osx" or platform == "windows"
+ return platform == "osx" or platform == "windows"
def configure(env):
diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp
index 3b6c916914..9479310a13 100644
--- a/modules/camera/register_types.cpp
+++ b/modules/camera/register_types.cpp
@@ -33,9 +33,6 @@
#if defined(WINDOWS_ENABLED)
#include "camera_win.h"
#endif
-#if defined(IPHONE_ENABLED)
-#include "camera_ios.h"
-#endif
#if defined(OSX_ENABLED)
#include "camera_osx.h"
#endif
@@ -44,9 +41,6 @@ void register_camera_types() {
#if defined(WINDOWS_ENABLED)
CameraServer::make_default<CameraWindows>();
#endif
-#if defined(IPHONE_ENABLED)
- CameraServer::make_default<CameraIOS>();
-#endif
#if defined(OSX_ENABLED)
CameraServer::make_default<CameraOSX>();
#endif
diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h
index f2753cb6d7..e34f84bf2c 100644
--- a/modules/camera/register_types.h
+++ b/modules/camera/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef CAMERA_REGISTER_TYPES_H
+#define CAMERA_REGISTER_TYPES_H
+
void register_camera_types();
void unregister_camera_types();
+
+#endif // CAMERA_REGISTER_TYPES_H
diff --git a/modules/camera_iphone/SCsub b/modules/camera_iphone/SCsub
new file mode 100644
index 0000000000..0a37d9a6f5
--- /dev/null
+++ b/modules/camera_iphone/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_camera = env_modules.Clone()
+
+# (iOS) Enable module support
+env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_camera.add_source_files(modules_sources, "*.cpp")
+env_camera.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/camera_iphone/camera.gdip b/modules/camera_iphone/camera.gdip
new file mode 100644
index 0000000000..09017b8d27
--- /dev/null
+++ b/modules/camera_iphone/camera.gdip
@@ -0,0 +1,18 @@
+[config]
+name="Camera"
+binary="camera_lib.a"
+
+initialization="register_camera_types"
+deinitialization="unregister_camera_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["AVFoundation.framework"]
+
+capabilities=[]
+
+files=[]
+
+[plist]
+NSCameraUsageDescription="Device camera is used for some functionality"
diff --git a/modules/camera/camera_ios.h b/modules/camera_iphone/camera_ios.h
index 7da43e4851..7da43e4851 100644
--- a/modules/camera/camera_ios.h
+++ b/modules/camera_iphone/camera_ios.h
diff --git a/modules/camera/camera_ios.mm b/modules/camera_iphone/camera_ios.mm
index c10b13b2af..e4cb928805 100644
--- a/modules/camera/camera_ios.mm
+++ b/modules/camera_iphone/camera_ios.mm
@@ -124,18 +124,12 @@
if (output) {
[self removeOutput:output];
[output setSampleBufferDelegate:nil queue:NULL];
- [output release];
output = nil;
}
[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..
@@ -272,7 +266,6 @@ CameraFeedIOS::CameraFeedIOS() {
void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
device = p_device;
- [device retain];
// get some info
NSString *device_name = p_device.localizedName;
@@ -286,14 +279,12 @@ void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
};
CameraFeedIOS::~CameraFeedIOS() {
- if (capture_session != NULL) {
- [capture_session release];
- capture_session = NULL;
+ if (capture_session) {
+ capture_session = nil;
};
- if (device != NULL) {
- [device release];
- device = NULL;
+ if (device) {
+ device = nil;
};
};
@@ -312,8 +303,7 @@ void CameraFeedIOS::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
- [capture_session release];
- capture_session = NULL;
+ capture_session = nil;
};
};
@@ -347,8 +337,6 @@ void CameraFeedIOS::deactivate_feed() {
// remove notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
-
- [super dealloc];
}
@end
@@ -453,5 +441,5 @@ CameraIOS::CameraIOS() {
};
CameraIOS::~CameraIOS() {
- [device_notifications release];
+ device_notifications = nil;
};
diff --git a/modules/camera_iphone/camera_module.cpp b/modules/camera_iphone/camera_module.cpp
new file mode 100644
index 0000000000..f3d00be204
--- /dev/null
+++ b/modules/camera_iphone/camera_module.cpp
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* camera_module.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "camera_module.h"
+
+#include "camera_ios.h"
+
+void register_camera_types() {
+ CameraServer::make_default<CameraIOS>();
+}
+
+void unregister_camera_types() {
+}
diff --git a/modules/arkit/register_types.h b/modules/camera_iphone/camera_module.h
index 5c697baf68..d123071a70 100644
--- a/modules/arkit/register_types.h
+++ b/modules/camera_iphone/camera_module.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.h */
+/* camera_module.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,5 +28,5 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_arkit_types();
-void unregister_arkit_types();
+void register_camera_types();
+void unregister_camera_types();
diff --git a/modules/gdnative/xr/config.py b/modules/camera_iphone/config.py
index d22f9454ed..e68603fc93 100644
--- a/modules/gdnative/xr/config.py
+++ b/modules/camera_iphone/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return platform == "iphone"
def configure(env):
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 47982d519a..04e1c4de35 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -32,7 +32,7 @@
#include "core/math/geometry_2d.h"
#include "core/math/math_funcs.h"
-#include "core/sort_array.h"
+#include "core/templates/sort_array.h"
// Static helper functions.
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 34ee239c17..ef1103e1ac 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -31,16 +31,16 @@
#ifndef CSG_H
#define CSG_H
-#include "core/list.h"
-#include "core/map.h"
#include "core/math/aabb.h"
#include "core/math/plane.h"
#include "core/math/transform.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
-#include "core/oa_hash_map.h"
-#include "core/reference.h"
-#include "core/vector.h"
+#include "core/object/reference.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
#include "scene/resources/material.h"
struct CSGBrush {
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index 43ce988c30..dac556c7f1 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -71,10 +71,10 @@
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The physics layers this area is in.
Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
- A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A.
+ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this CSG shape scans for collisions.
+ The physics layers this CSG shape scans for collisions. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
diff --git a/modules/csg/icons/CSGBox3D.svg b/modules/csg/icons/CSGBox3D.svg
index 67e34df444..ceef9196a7 100644
--- a/modules/csg/icons/CSGBox3D.svg
+++ b/modules/csg/icons/CSGBox3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-<path transform="translate(0 1036.4)" d="m8 0.94531-7 3.5v7.2227l7 3.5 0.29492-0.14844c-0.18282-0.30101-0.29492-0.64737-0.29492-1.0195v-2c0-0.72651 0.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c0.36419 0 0.70336 0.10754 1 0.2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/><path d="m8 .94531-7 3.5v7.2227l7 3.5.29492-.14844c-.18282-.30101-.29492-.64737-.29492-1.0195v-2c0-.72651.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c.36419 0 .70336.10754 1 .2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/></svg>
diff --git a/modules/csg/icons/CSGCapsule3D.svg b/modules/csg/icons/CSGCapsule3D.svg
index 92a7b5a870..14e582ee84 100644
--- a/modules/csg/icons/CSGCapsule3D.svg
+++ b/modules/csg/icons/CSGCapsule3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922 0.092943 0 0.18367-0.008623 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72887 0.41095-1.3691 1.0059-1.7188v-0.28125c0.34771-0.034464 0.68259-0.10691 1.0156-0.19922 0.10394-0.99856 0.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-0.66556-0.1047-1.2974-0.37231-1.9941-0.66211v-1.3223c0-1.3474 0.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957 0.4088 1.9941 1.5256 1.9941 2.873v1.3457c-0.68406 0.3054-1.3142 0.57292-1.9941 0.66602v-4.8848zm-4.0059 6.334c0.67836 0.2231 1.3126 0.44599 1.9941 0.52539v2.8848c-1.1957-0.4092-1.9941-1.5237-1.9941-2.8711v-0.53906z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922.092943 0 .18367-.008623.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72887.41095-1.3691 1.0059-1.7188v-.28125c.34771-.034464.68259-.10691 1.0156-.19922.10394-.99856.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-.66556-.1047-1.2974-.37231-1.9941-.66211v-1.3223c0-1.3474.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957.4088 1.9941 1.5256 1.9941 2.873v1.3457c-.68406.3054-1.3142.57292-1.9941.66602v-4.8848zm-4.0059 6.334c.67836.2231 1.3126.44599 1.9941.52539v2.8848c-1.1957-.4092-1.9941-1.5237-1.9941-2.8711v-.53906z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg>
diff --git a/modules/csg/icons/CSGCombiner3D.svg b/modules/csg/icons/CSGCombiner3D.svg
index cce2902e24..50ce4179d9 100644
--- a/modules/csg/icons/CSGCombiner3D.svg
+++ b/modules/csg/icons/CSGCombiner3D.svg
@@ -1,8 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-<g fill="#fc9c9c">
-<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-0.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046 0.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/>
-</g>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/><path d="m3 1c-1.1046 0-2 .89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/></svg>
diff --git a/modules/csg/icons/CSGCylinder3D.svg b/modules/csg/icons/CSGCylinder3D.svg
index 645a74c79b..c84594928a 100644
--- a/modules/csg/icons/CSGCylinder3D.svg
+++ b/modules/csg/icons/CSGCylinder3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path transform="scale(.9375)" d="m8 1c-1.7469 0-3.328 0.22648-4.5586 0.63672-0.61528 0.20512-1.1471 0.45187-1.5898 0.80078-0.44272 0.34891-0.85156 0.88101-0.85156 1.5625v8c0 0.68149 0.40884 1.2155 0.85156 1.5645 0.44272 0.34891 0.97457 0.59577 1.5898 0.80078 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477 0.095648 0 0.18467-0.008426 0.2793-0.009766-0.1722-0.29446-0.2793-0.62995-0.2793-0.99023v-1c-1.5668 0-2.9867-0.2195-3.9277-0.5332-0.46329-0.15435-0.90474-0.33752-1.0723-0.4668v-5.8125c0.1468 0.058667 0.2835 0.12515 0.44141 0.17773 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477s3.328-0.22453 4.5586-0.63477c0.15791-0.052267 0.29461-0.11864 0.44141-0.17773v1.8125h1c0.36396 0 0.70348 0.10774 1 0.2832v-4.2832c0-0.68149-0.40884-1.2136-0.85156-1.5625-0.44272-0.34891-0.97457-0.59566-1.5898-0.80078-1.2306-0.41024-2.8117-0.63672-4.5586-0.63672zm0 2c1.5668 0 2.9867 0.22145 3.9277 0.53516 0.46368 0.15456 0.80138 0.33741 0.96875 0.4668-0.16752 0.12928-0.50546 0.3105-0.96875 0.46484-0.94102 0.31371-2.361 0.5332-3.9277 0.5332s-2.9867-0.2195-3.9277-0.5332c-0.46329-0.15435-0.80123-0.33556-0.96875-0.46484 0.16737-0.12939 0.50507-0.31224 0.96875-0.4668 0.94102-0.31371 2.361-0.53516 3.9277-0.53516z" fill="#fc9c9c" stroke-width="1.0667"/>
-<path d="m11.25 8.4375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v0.9375h1.875v1.875h0.9375c0.51938 0 0.9375-0.41812 0.9375-0.9375v-1.875c0-0.51938-0.41812-0.9375-0.9375-0.9375zm0.9375 3.75h-1.875v-1.875h-0.9375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v1.875c0 0.51938 0.41812 0.9375 0.9375 0.9375h1.875c0.51938 0 0.9375-0.41812 0.9375-0.9375z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-1.7469 0-3.328.22648-4.5586.63672-.61528.20512-1.1471.45187-1.5898.80078-.44272.34891-.85156.88101-.85156 1.5625v8c0 .68149.40884 1.2155.85156 1.5645.44272.34891.97457.59577 1.5898.80078 1.2306.41024 2.8117.63477 4.5586.63477.095648 0 .18467-.008426.2793-.009766-.1722-.29446-.2793-.62995-.2793-.99023v-1c-1.5668 0-2.9867-.2195-3.9277-.5332-.46329-.15435-.90474-.33752-1.0723-.4668v-5.8125c.1468.058667.2835.12515.44141.17773 1.2306.41024 2.8117.63477 4.5586.63477s3.328-.22453 4.5586-.63477c.15791-.052267.29461-.11864.44141-.17773v1.8125h1c.36396 0 .70348.10774 1 .2832v-4.2832c0-.68149-.40884-1.2136-.85156-1.5625-.44272-.34891-.97457-.59566-1.5898-.80078-1.2306-.41024-2.8117-.63672-4.5586-.63672zm0 2c1.5668 0 2.9867.22145 3.9277.53516.46368.15456.80138.33741.96875.4668-.16752.12928-.50546.3105-.96875.46484-.94102.31371-2.361.5332-3.9277.5332s-2.9867-.2195-3.9277-.5332c-.46329-.15435-.80123-.33556-.96875-.46484.16737-.12939.50507-.31224.96875-.4668.94102-.31371 2.361-.53516 3.9277-.53516z" fill="#fc9c9c" stroke-width="1.0667" transform="scale(.9375)"/><path d="m11.25 8.4375c-.51938 0-.9375.41812-.9375.9375v.9375h1.875v1.875h.9375c.51938 0 .9375-.41812.9375-.9375v-1.875c0-.51938-.41812-.9375-.9375-.9375zm.9375 3.75h-1.875v-1.875h-.9375c-.51938 0-.9375.41812-.9375.9375v1.875c0 .51938.41812.9375.9375.9375h1.875c.51938 0 .9375-.41812.9375-.9375z" fill="#84c2ff"/></svg>
diff --git a/modules/csg/icons/CSGMesh3D.svg b/modules/csg/icons/CSGMesh3D.svg
index 6e940a4aa5..962e71f6ae 100644
--- a/modules/csg/icons/CSGMesh3D.svg
+++ b/modules/csg/icons/CSGMesh3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.649e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35663-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.65e-4 1.3735-0.38169 1.7305-1h3.2695v-2h-3.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l4.916 4.916c0.31428-0.20669 0.68609-0.33008 1.084-0.33008 0-0.3979 0.12338-0.76971 0.33008-1.084l-4.916-4.916h5.8574c0.17478 0.30301 0.42598 0.55488 0.72852 0.73047v3.2695h2v-3.2715c0.61771-0.35663 0.99874-1.0152 1-1.7285 0-1.1046-0.89543-2-2-2-0.71397 5.648e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35663-0.61771-1.0152-0.99874-1.7285-1z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2 .0005649.71397.38169 1.3735 1 1.7305v6.541c-.61771.35663-.99874 1.0152-1 1.7285 0 1.1046.89543 2 2 2 .71397-.000565 1.3735-.38169 1.7305-1h3.2695v-2h-3.2715c-.17478-.30301-.42598-.55488-.72852-.73047v-5.8555l4.916 4.916c.31428-.20669.68609-.33008 1.084-.33008 0-.3979.12338-.76971.33008-1.084l-4.916-4.916h5.8574c.17478.30301.42598.55488.72852.73047v3.2695h2v-3.2715c.61771-.35663.99874-1.0152 1-1.7285 0-1.1046-.89543-2-2-2-.71397.0005648-1.3735.38169-1.7305 1h-6.541c-.35663-.61771-1.0152-.99874-1.7285-1z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg>
diff --git a/modules/csg/icons/CSGPolygon3D.svg b/modules/csg/icons/CSGPolygon3D.svg
index 71b03cb8e6..1d496e5fd9 100644
--- a/modules/csg/icons/CSGPolygon3D.svg
+++ b/modules/csg/icons/CSGPolygon3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7.9629 1.002c-0.14254 0.00487-0.28238 0.04016-0.41016 0.10352l-6 3c-0.33878 0.16944-0.55276 0.51574-0.55273 0.89453v5.832c-0.105 0.61631 0.37487 1.1768 1 1.168h5v2c2.16e-5 0.67546 0.64487 1.1297 1.2617 0.95898-0.16118-0.28721-0.26172-0.61135-0.26172-0.95898v-2c0-0.72673 0.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c0.36397 0 0.70348 0.10774 1 0.2832v-3.2773c6e-6 -0.00195 6e-6 -0.0039094 0-0.0058594 2.6e-5 -0.37879-0.21395-0.72509-0.55273-0.89453l-6-3c-0.15022-0.074574-0.31679-0.11017-0.48438-0.10352zm0.037109 2.1172l3.7637 1.8809-2.7637 1.3809v-1.3809c-5.52e-5 -0.55226-0.44774-0.99994-1-1h-1.7617l1.7617-0.88086zm-5 2.8809h4v4h-4v-4z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9629 1.002c-.14254.00487-.28238.04016-.41016.10352l-6 3c-.33878.16944-.55276.51574-.55273.89453v5.832c-.105.61631.37487 1.1768 1 1.168h5v2c.0000216.67546.64487 1.1297 1.2617.95898-.16118-.28721-.26172-.61135-.26172-.95898v-2c0-.72673.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c.36397 0 .70348.10774 1 .2832v-3.2773c.000006-.00195.000006-.0039094 0-.0058594.000026-.37879-.21395-.72509-.55273-.89453l-6-3c-.15022-.074574-.31679-.11017-.48438-.10352zm.037109 2.1172 3.7637 1.8809-2.7637 1.3809v-1.3809c-.0000552-.55226-.44774-.99994-1-1h-1.7617l1.7617-.88086zm-5 2.8809h4v4h-4z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg>
diff --git a/modules/csg/icons/CSGSphere3D.svg b/modules/csg/icons/CSGSphere3D.svg
index f81b566993..639e38f49f 100644
--- a/modules/csg/icons/CSGSphere3D.svg
+++ b/modules/csg/icons/CSGSphere3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 0.093042 0 0.18321-0.01004 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72673 0.40794-1.3664 1-1.7168v-0.33398c0.34074-0.019259 0.67728-0.069097 1.0156-0.10547 0.083091-1.0187 0.94713-1.8438 1.9844-1.8438h2c0.35841 0 0.69292 0.10484 0.98633 0.27539 0.003633-0.092184 0.013672-0.18235 0.013672-0.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-0.071342-2.6061-0.29819-3.9434-0.69141 0.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549 0.41253 3.637 2.0767 3.9414 4.1699-1.3046 0.36677-2.6158 0.60259-3.9414 0.6875v-4.8574zm-5.7793 6.2988c1.2733 0.31892 2.5337 0.50215 3.7793 0.5625v2.9414c-1.8291-0.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 .093042 0 .18321-.01004.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72673.40794-1.3664 1-1.7168v-.33398c.34074-.019259.67728-.069097 1.0156-.10547.083091-1.0187.94713-1.8438 1.9844-1.8438h2c.35841 0 .69292.10484.98633.27539.003633-.092184.013672-.18235.013672-.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-.071342-2.6061-.29819-3.9434-.69141.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549.41253 3.637 2.0767 3.9414 4.1699-1.3046.36677-2.6158.60259-3.9414.6875zm-5.7793 6.2988c1.2733.31892 2.5337.50215 3.7793.5625v2.9414c-1.8291-.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg>
diff --git a/modules/csg/icons/CSGTorus3D.svg b/modules/csg/icons/CSGTorus3D.svg
index 3d30aa47b2..eb8c0f37cb 100644
--- a/modules/csg/icons/CSGTorus3D.svg
+++ b/modules/csg/icons/CSGTorus3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 3c-1.8145 0-3.4691 0.41721-4.7461 1.1621-1.277 0.745-2.2539 1.9082-2.2539 3.3379 0 1.4298 0.9769 2.5949 2.2539 3.3398 1.277 0.7449 2.9316 1.1602 4.7461 1.1602 0-1.0907 0.90931-2 2-2 0-0.080836 0.013744-0.15778 0.023438-0.23633-0.61769 0.14673-1.3008 0.23633-2.0234 0.23633-1.4992 0-2.8437-0.36687-3.7383-0.88867-0.89456-0.5219-1.2617-1.108-1.2617-1.6113 0-0.5032 0.36716-1.0876 1.2617-1.6094 0.89456-0.5219 2.2391-0.89062 3.7383-0.89062s2.8437 0.36872 3.7383 0.89062c0.89456 0.5218 1.2617 1.1062 1.2617 1.6094 0 0.15978-0.053679 0.32822-0.13281 0.5h1.1328c0.32481 0 0.62893 0.088408 0.90234 0.23047 0.057552-0.23582 0.097656-0.47718 0.097656-0.73047 0-1.4297-0.9769-2.5929-2.2539-3.3379-1.277-0.7449-2.9316-1.1621-4.7461-1.1621z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 3c-1.8145 0-3.4691.41721-4.7461 1.1621-1.277.745-2.2539 1.9082-2.2539 3.3379 0 1.4298.9769 2.5949 2.2539 3.3398s2.9316 1.1602 4.7461 1.1602c0-1.0907.90931-2 2-2 0-.080836.013744-.15778.023438-.23633-.61769.14673-1.3008.23633-2.0234.23633-1.4992 0-2.8437-.36687-3.7383-.88867-.89456-.5219-1.2617-1.108-1.2617-1.6113 0-.5032.36716-1.0876 1.2617-1.6094.89456-.5219 2.2391-.89062 3.7383-.89062s2.8437.36872 3.7383.89062c.89456.5218 1.2617 1.1062 1.2617 1.6094 0 .15978-.053679.32822-.13281.5h1.1328c.32481 0 .62893.088408.90234.23047.057552-.23582.097656-.47718.097656-.73047 0-1.4297-.9769-2.5929-2.2539-3.3379-1.277-.7449-2.9316-1.1621-4.7461-1.1621z" fill="#fc9c9c"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#84c2ff"/></svg>
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 2a4f836478..5d97164dbf 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -32,7 +32,7 @@
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <ConvectionKernels.h>
diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h
index c1772199af..483fb876a6 100644
--- a/modules/cvtt/image_compress_cvtt.h
+++ b/modules/cvtt/image_compress_cvtt.h
@@ -31,7 +31,7 @@
#ifndef IMAGE_COMPRESS_CVTT_H
#define IMAGE_COMPRESS_CVTT_H
-#include "core/image.h"
+#include "core/io/image.h"
void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels);
void image_decompress_cvtt(Image *p_image);
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
index 8472980c6a..36b5e332d6 100644
--- a/modules/cvtt/register_types.h
+++ b/modules/cvtt/register_types.h
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef TOOLS_ENABLED
-
#ifndef CVTT_REGISTER_TYPES_H
#define CVTT_REGISTER_TYPES_H
+#ifdef TOOLS_ENABLED
+
void register_cvtt_types();
void unregister_cvtt_types();
-#endif // CVTT_REGISTER_TYPES_H
-
#endif // TOOLS_ENABLED
+
+#endif // CVTT_REGISTER_TYPES_H
diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h
index d01bbd10a5..f4e4335d9b 100644
--- a/modules/denoise/lightmap_denoiser.h
+++ b/modules/denoise/lightmap_denoiser.h
@@ -31,7 +31,7 @@
#ifndef LIGHTMAP_DENOISER_H
#define LIGHTMAP_DENOISER_H
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "scene/3d/lightmapper.h"
struct OIDNDeviceImpl;
diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp
index b78734a531..552495ed87 100644
--- a/modules/denoise/register_types.cpp
+++ b/modules/denoise/register_types.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "lightmap_denoiser.h"
void register_denoise_types() {
diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h
index 2ffc36ee2c..f0f1f44bfe 100644
--- a/modules/denoise/register_types.h
+++ b/modules/denoise/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef DENOISE_REGISTER_TYPES_H
+#define DENOISE_REGISTER_TYPES_H
+
void register_denoise_types();
void unregister_denoise_types();
+
+#endif // DENOISE_REGISTER_TYPES_H
diff --git a/modules/denoise/resource_to_cpp.py b/modules/denoise/resource_to_cpp.py
index 4c0b67f701..6c83277355 100644
--- a/modules/denoise/resource_to_cpp.py
+++ b/modules/denoise/resource_to_cpp.py
@@ -17,8 +17,6 @@
## ======================================================================== ##
import os
-import sys
-import argparse
from array import array
# Generates a C++ file from the specified binary resource file
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
index c908af7479..f46ef2d812 100644
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
@@ -7,8 +7,8 @@
A PacketPeer implementation that should be passed to [member SceneTree.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
- <link>http://enet.bespin.org/usergroup0.html</link>
+ <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
+ <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<methods>
<method name="close_connection">
diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp
index 18051f756a..2683f3155b 100644
--- a/modules/enet/register_types.cpp
+++ b/modules/enet/register_types.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "networked_multiplayer_enet.h"
static bool enet_ok = false;
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
index d1ba3dc355..6cac2458f9 100644
--- a/modules/etc/image_etc.cpp
+++ b/modules/etc/image_etc.cpp
@@ -31,10 +31,10 @@
#include "image_etc.h"
#include "Etc.h"
#include "EtcFilter.h"
-#include "core/image.h"
+#include "core/io/image.h"
#include "core/os/copymem.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
static Image::Format _get_etc2_mode(Image::UsedChannels format) {
switch (format) {
diff --git a/modules/gamecenter/SCsub b/modules/gamecenter/SCsub
new file mode 100644
index 0000000000..72fbf7ab0e
--- /dev/null
+++ b/modules/gamecenter/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_gamecenter = env_modules.Clone()
+
+# (iOS) Enable module support
+env_gamecenter.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_gamecenter.add_source_files(modules_sources, "*.cpp")
+env_gamecenter.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_gamecenter_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/gamecenter/config.py b/modules/gamecenter/config.py
new file mode 100644
index 0000000000..e68603fc93
--- /dev/null
+++ b/modules/gamecenter/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return platform == "iphone"
+
+
+def configure(env):
+ pass
diff --git a/modules/gamecenter/game_center.h b/modules/gamecenter/game_center.h
new file mode 100644
index 0000000000..76fd295460
--- /dev/null
+++ b/modules/gamecenter/game_center.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* game_center.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 GAME_CENTER_H
+#define GAME_CENTER_H
+
+#include "core/object/class_db.h"
+
+class GameCenter : public Object {
+ GDCLASS(GameCenter, Object);
+
+ static GameCenter *instance;
+ static void _bind_methods();
+
+ List<Variant> pending_events;
+
+ bool authenticated;
+
+ void return_connect_error(const char *p_error_description);
+
+public:
+ Error authenticate();
+ bool is_authenticated();
+
+ Error post_score(Dictionary p_score);
+ Error award_achievement(Dictionary p_params);
+ void reset_achievements();
+ void request_achievements();
+ void request_achievement_descriptions();
+ Error show_game_center(Dictionary p_params);
+ Error request_identity_verification_signature();
+
+ void game_center_closed();
+
+ int get_pending_event_count();
+ Variant pop_pending_event();
+
+ static GameCenter *get_singleton();
+
+ GameCenter();
+ ~GameCenter();
+};
+
+#endif
diff --git a/modules/gamecenter/game_center.mm b/modules/gamecenter/game_center.mm
new file mode 100644
index 0000000000..114f639a32
--- /dev/null
+++ b/modules/gamecenter/game_center.mm
@@ -0,0 +1,380 @@
+/*************************************************************************/
+/* game_center.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "game_center.h"
+#import "platform/iphone/app_delegate.h"
+
+#import "game_center_delegate.h"
+#import "platform/iphone/view_controller.h"
+#import <GameKit/GameKit.h>
+
+GameCenter *GameCenter::instance = NULL;
+GodotGameCenterDelegate *gameCenterDelegate = nil;
+
+void GameCenter::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("authenticate"), &GameCenter::authenticate);
+ ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
+
+ ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
+ ClassDB::bind_method(D_METHOD("award_achievement", "achievement"), &GameCenter::award_achievement);
+ ClassDB::bind_method(D_METHOD("reset_achievements"), &GameCenter::reset_achievements);
+ ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
+ ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
+ ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center);
+ ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature);
+
+ ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count);
+ ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
+};
+
+Error GameCenter::authenticate() {
+ //if this class isn't available, game center isn't implemented
+ if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
+ return ERR_UNAVAILABLE;
+ }
+
+ GKLocalPlayer *player = [GKLocalPlayer localPlayer];
+ ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
+
+ UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
+ ERR_FAIL_COND_V(!root_controller, FAILED);
+
+ // This handler is called several times. First when the view needs to be shown, then again
+ // after the view is cancelled or the user logs in. Or if the user's already logged in, it's
+ // called just once to confirm they're authenticated. This is why no result needs to be specified
+ // in the presentViewController phase. In this case, more calls to this function will follow.
+ _weakify(root_controller);
+ _weakify(player);
+ player.authenticateHandler = (^(UIViewController *controller, NSError *error) {
+ _strongify(root_controller);
+ _strongify(player);
+
+ if (controller) {
+ [root_controller presentViewController:controller animated:YES completion:nil];
+ } else {
+ Dictionary ret;
+ ret["type"] = "authentication";
+ if (player.isAuthenticated) {
+ ret["result"] = "ok";
+ if (@available(iOS 13, *)) {
+ ret["player_id"] = [player.teamPlayerID UTF8String];
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ ret["player_id"] = [player.playerID UTF8String];
+#endif
+ }
+
+ GameCenter::get_singleton()->authenticated = true;
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ ret["error_description"] = [error.localizedDescription UTF8String];
+ GameCenter::get_singleton()->authenticated = false;
+ };
+
+ pending_events.push_back(ret);
+ };
+ });
+
+ return OK;
+};
+
+bool GameCenter::is_authenticated() {
+ return authenticated;
+};
+
+Error GameCenter::post_score(Dictionary p_score) {
+ ERR_FAIL_COND_V(!p_score.has("score") || !p_score.has("category"), ERR_INVALID_PARAMETER);
+ float score = p_score["score"];
+ String category = p_score["category"];
+
+ NSString *cat_str = [[NSString alloc] initWithUTF8String:category.utf8().get_data()];
+ GKScore *reporter = [[GKScore alloc] initWithLeaderboardIdentifier:cat_str];
+ reporter.value = score;
+
+ ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
+
+ [GKScore reportScores:@[ reporter ]
+ withCompletionHandler:^(NSError *error) {
+ Dictionary ret;
+ ret["type"] = "post_score";
+ if (error == nil) {
+ ret["result"] = "ok";
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ ret["error_description"] = [error.localizedDescription UTF8String];
+ };
+
+ pending_events.push_back(ret);
+ }];
+
+ return OK;
+};
+
+Error GameCenter::award_achievement(Dictionary p_params) {
+ ERR_FAIL_COND_V(!p_params.has("name") || !p_params.has("progress"), ERR_INVALID_PARAMETER);
+ String name = p_params["name"];
+ float progress = p_params["progress"];
+
+ NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
+ GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:name_str];
+ ERR_FAIL_COND_V(!achievement, FAILED);
+
+ ERR_FAIL_COND_V([GKAchievement respondsToSelector:@selector(reportAchievements)], ERR_UNAVAILABLE);
+
+ achievement.percentComplete = progress;
+ achievement.showsCompletionBanner = NO;
+ if (p_params.has("show_completion_banner")) {
+ achievement.showsCompletionBanner = p_params["show_completion_banner"] ? YES : NO;
+ }
+
+ [GKAchievement reportAchievements:@[ achievement ]
+ withCompletionHandler:^(NSError *error) {
+ Dictionary ret;
+ ret["type"] = "award_achievement";
+ if (error == nil) {
+ ret["result"] = "ok";
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+
+ return OK;
+};
+
+void GameCenter::request_achievement_descriptions() {
+ [GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
+ Dictionary ret;
+ ret["type"] = "achievement_descriptions";
+ if (error == nil) {
+ ret["result"] = "ok";
+ PackedStringArray names;
+ PackedStringArray titles;
+ PackedStringArray unachieved_descriptions;
+ PackedStringArray achieved_descriptions;
+ PackedInt32Array maximum_points;
+ Array hidden;
+ Array replayable;
+
+ for (NSUInteger i = 0; i < [descriptions count]; i++) {
+ GKAchievementDescription *description = [descriptions objectAtIndex:i];
+
+ const char *str = [description.identifier UTF8String];
+ names.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.title UTF8String];
+ titles.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.unachievedDescription UTF8String];
+ unachieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [description.achievedDescription UTF8String];
+ achieved_descriptions.push_back(String::utf8(str != NULL ? str : ""));
+
+ maximum_points.push_back(description.maximumPoints);
+
+ hidden.push_back(description.hidden == YES);
+
+ replayable.push_back(description.replayable == YES);
+ }
+
+ ret["names"] = names;
+ ret["titles"] = titles;
+ ret["unachieved_descriptions"] = unachieved_descriptions;
+ ret["achieved_descriptions"] = achieved_descriptions;
+ ret["maximum_points"] = maximum_points;
+ ret["hidden"] = hidden;
+ ret["replayable"] = replayable;
+
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+void GameCenter::request_achievements() {
+ [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
+ Dictionary ret;
+ ret["type"] = "achievements";
+ if (error == nil) {
+ ret["result"] = "ok";
+ PackedStringArray names;
+ PackedFloat32Array percentages;
+
+ for (NSUInteger i = 0; i < [achievements count]; i++) {
+ GKAchievement *achievement = [achievements objectAtIndex:i];
+ const char *str = [achievement.identifier UTF8String];
+ names.push_back(String::utf8(str != NULL ? str : ""));
+
+ percentages.push_back(achievement.percentComplete);
+ }
+
+ ret["names"] = names;
+ ret["progress"] = percentages;
+
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+void GameCenter::reset_achievements() {
+ [GKAchievement resetAchievementsWithCompletionHandler:^(NSError *error) {
+ Dictionary ret;
+ ret["type"] = "reset_achievements";
+ if (error == nil) {
+ ret["result"] = "ok";
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ };
+
+ pending_events.push_back(ret);
+ }];
+};
+
+Error GameCenter::show_game_center(Dictionary p_params) {
+ ERR_FAIL_COND_V(!NSProtocolFromString(@"GKGameCenterControllerDelegate"), FAILED);
+
+ GKGameCenterViewControllerState view_state = GKGameCenterViewControllerStateDefault;
+ if (p_params.has("view")) {
+ String view_name = p_params["view"];
+ if (view_name == "default") {
+ view_state = GKGameCenterViewControllerStateDefault;
+ } else if (view_name == "leaderboards") {
+ view_state = GKGameCenterViewControllerStateLeaderboards;
+ } else if (view_name == "achievements") {
+ view_state = GKGameCenterViewControllerStateAchievements;
+ } else if (view_name == "challenges") {
+ view_state = GKGameCenterViewControllerStateChallenges;
+ } else {
+ return ERR_INVALID_PARAMETER;
+ }
+ }
+
+ GKGameCenterViewController *controller = [[GKGameCenterViewController alloc] init];
+ ERR_FAIL_COND_V(!controller, FAILED);
+
+ UIViewController *root_controller = [[UIApplication sharedApplication] delegate].window.rootViewController;
+ ERR_FAIL_COND_V(!root_controller, FAILED);
+
+ controller.gameCenterDelegate = gameCenterDelegate;
+ controller.viewState = view_state;
+ if (view_state == GKGameCenterViewControllerStateLeaderboards) {
+ controller.leaderboardIdentifier = nil;
+ if (p_params.has("leaderboard_name")) {
+ String name = p_params["leaderboard_name"];
+ NSString *name_str = [[NSString alloc] initWithUTF8String:name.utf8().get_data()];
+ controller.leaderboardIdentifier = name_str;
+ }
+ }
+
+ [root_controller presentViewController:controller animated:YES completion:nil];
+
+ return OK;
+};
+
+Error GameCenter::request_identity_verification_signature() {
+ ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
+
+ GKLocalPlayer *player = [GKLocalPlayer localPlayer];
+ [player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
+ Dictionary ret;
+ ret["type"] = "identity_verification_signature";
+ if (error == nil) {
+ ret["result"] = "ok";
+ ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String];
+ ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
+ ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
+ ret["timestamp"] = timestamp;
+ if (@available(iOS 13, *)) {
+ ret["player_id"] = [player.teamPlayerID UTF8String];
+#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
+ } else {
+ ret["player_id"] = [player.playerID UTF8String];
+#endif
+ }
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ ret["error_description"] = [error.localizedDescription UTF8String];
+ };
+
+ pending_events.push_back(ret);
+ }];
+
+ return OK;
+};
+
+void GameCenter::game_center_closed() {
+ Dictionary ret;
+ ret["type"] = "show_game_center";
+ ret["result"] = "ok";
+ pending_events.push_back(ret);
+}
+
+int GameCenter::get_pending_event_count() {
+ return pending_events.size();
+};
+
+Variant GameCenter::pop_pending_event() {
+ Variant front = pending_events.front()->get();
+ pending_events.pop_front();
+
+ return front;
+};
+
+GameCenter *GameCenter::get_singleton() {
+ return instance;
+};
+
+GameCenter::GameCenter() {
+ ERR_FAIL_COND(instance != NULL);
+ instance = this;
+ authenticated = false;
+
+ gameCenterDelegate = [[GodotGameCenterDelegate alloc] init];
+};
+
+GameCenter::~GameCenter() {
+ if (gameCenterDelegate) {
+ gameCenterDelegate = nil;
+ }
+}
diff --git a/modules/gamecenter/game_center_delegate.h b/modules/gamecenter/game_center_delegate.h
new file mode 100644
index 0000000000..1b7025f915
--- /dev/null
+++ b/modules/gamecenter/game_center_delegate.h
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* game_center_delegate.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#import <GameKit/GameKit.h>
+
+@interface GodotGameCenterDelegate : NSObject <GKGameCenterControllerDelegate>
+
+@end
diff --git a/modules/gamecenter/game_center_delegate.mm b/modules/gamecenter/game_center_delegate.mm
new file mode 100644
index 0000000000..9a10c439c6
--- /dev/null
+++ b/modules/gamecenter/game_center_delegate.mm
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* game_center_delegate.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+#import "game_center_delegate.h"
+
+#include "game_center.h"
+
+@implementation GodotGameCenterDelegate
+
+- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController {
+ //[gameCenterViewController dismissViewControllerAnimated:YES completion:^{GameCenter::get_singleton()->game_center_closed();}];//version for signaling when overlay is completely gone
+ if (GameCenter::get_singleton()) {
+ GameCenter::get_singleton()->game_center_closed();
+ }
+ [gameCenterViewController dismissViewControllerAnimated:YES completion:nil];
+}
+
+@end
diff --git a/modules/gamecenter/game_center_module.cpp b/modules/gamecenter/game_center_module.cpp
new file mode 100644
index 0000000000..6c5157345f
--- /dev/null
+++ b/modules/gamecenter/game_center_module.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* game_center_module.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "game_center_module.h"
+
+#include "core/config/engine.h"
+
+#include "game_center.h"
+
+GameCenter *game_center;
+
+void register_gamecenter_types() {
+ game_center = memnew(GameCenter);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center));
+}
+
+void unregister_gamecenter_types() {
+ if (game_center) {
+ memdelete(game_center);
+ }
+}
diff --git a/modules/gamecenter/game_center_module.h b/modules/gamecenter/game_center_module.h
new file mode 100644
index 0000000000..8da3ae02ee
--- /dev/null
+++ b/modules/gamecenter/game_center_module.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* game_center_module.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+void register_gamecenter_types();
+void unregister_gamecenter_types();
diff --git a/modules/gamecenter/gamecenter.gdip b/modules/gamecenter/gamecenter.gdip
new file mode 100644
index 0000000000..eb44effbdd
--- /dev/null
+++ b/modules/gamecenter/gamecenter.gdip
@@ -0,0 +1,17 @@
+[config]
+name="GameCenter"
+binary="gamecenter_lib.a"
+
+initialization="register_gamecenter_types"
+deinitialization="unregister_gamecenter_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["GameKit.framework"]
+
+capabilities=["gamekit"]
+
+files=[]
+
+[plist]
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 1aab864102..05cda05f9f 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -7,8 +7,8 @@
A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
- <link>https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
+ <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
+ <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
</tutorials>
<methods>
<method name="get_current_dependencies" qualifiers="const">
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index bb2da70c3a..f397ee96c5 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -30,11 +30,11 @@
#include "gdnative.h"
-#include "core/global_constants.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/scene_tree.h"
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 6d26c2141d..bba2c04a2a 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -31,10 +31,10 @@
#ifndef GDNATIVE_H
#define GDNATIVE_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
#include "gdnative/gdnative.h"
#include "gdnative_api_struct.gen.h"
diff --git a/modules/gdnative/gdnative/aabb.cpp b/modules/gdnative/gdnative/aabb.cpp
index d5970e8004..dc1b79b9e5 100644
--- a/modules/gdnative/gdnative/aabb.cpp
+++ b/modules/gdnative/gdnative/aabb.cpp
@@ -31,7 +31,7 @@
#include "gdnative/aabb.h"
#include "core/math/aabb.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index 59953f5182..a5567f1e6b 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -30,12 +30,12 @@
#include "gdnative/array.h"
-#include "core/array.h"
#include "core/os/memory.h"
+#include "core/variant/array.h"
-#include "core/color.h"
+#include "core/math/color.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 990fd3795d..e5891562a1 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -31,7 +31,7 @@
#include "gdnative/basis.h"
#include "core/math/basis.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/callable.cpp b/modules/gdnative/gdnative/callable.cpp
index 868b324227..f200e9f171 100644
--- a/modules/gdnative/gdnative/callable.cpp
+++ b/modules/gdnative/gdnative/callable.cpp
@@ -30,9 +30,9 @@
#include "gdnative/callable.h"
-#include "core/callable.h"
-#include "core/resource.h"
-#include "core/variant.h"
+#include "core/io/resource.h"
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp
index c75e74daba..a93181e142 100644
--- a/modules/gdnative/gdnative/color.cpp
+++ b/modules/gdnative/gdnative/color.cpp
@@ -30,8 +30,8 @@
#include "gdnative/color.h"
-#include "core/color.h"
-#include "core/variant.h"
+#include "core/math/color.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
@@ -148,13 +148,6 @@ godot_color GDAPI godot_color_inverted(const godot_color *p_self) {
return dest;
}
-godot_color GDAPI godot_color_contrasted(const godot_color *p_self) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->contrasted();
- return dest;
-}
-
godot_color GDAPI godot_color_lerp(const godot_color *p_self, const godot_color *p_b, const godot_real p_t) {
godot_color dest;
const Color *self = (const Color *)p_self;
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index a126974815..b6900b28bb 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -30,10 +30,10 @@
#include "gdnative/dictionary.h"
-#include "core/variant.h"
-// core/variant.h before to avoid compile errors with MSVC
-#include "core/dictionary.h"
+#include "core/variant/variant.h"
+// core/variant/variant.h before to avoid compile errors with MSVC
#include "core/io/json.h"
+#include "core/variant/dictionary.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp
index e94190b07b..4142ea892b 100644
--- a/modules/gdnative/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative/gdnative.cpp
@@ -30,12 +30,12 @@
#include "gdnative/gdnative.h"
-#include "core/class_db.h"
-#include "core/engine.h"
-#include "core/error_macros.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "modules/gdnative/gdnative.h"
@@ -101,10 +101,10 @@ godot_dictionary GDAPI godot_get_global_constants() {
godot_dictionary constants;
godot_dictionary_new(&constants);
Dictionary *p_constants = (Dictionary *)&constants;
- const int constants_count = GlobalConstants::get_global_constant_count();
+ const int constants_count = CoreConstants::get_global_constant_count();
for (int i = 0; i < constants_count; ++i) {
- const char *name = GlobalConstants::get_global_constant_name(i);
- int value = GlobalConstants::get_global_constant_value(i);
+ const char *name = CoreConstants::get_global_constant_name(i);
+ int value = CoreConstants::get_global_constant_value(i);
(*p_constants)[name] = value;
}
return constants;
diff --git a/modules/gdnative/gdnative/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp
index 88ed650ebe..c031498612 100644
--- a/modules/gdnative/gdnative/node_path.cpp
+++ b/modules/gdnative/gdnative/node_path.cpp
@@ -30,8 +30,8 @@
#include "gdnative/node_path.h"
-#include "core/node_path.h"
-#include "core/variant.h"
+#include "core/string/node_path.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index de93c1d9b3..9139fd89a1 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -30,11 +30,11 @@
#include "gdnative/packed_arrays.h"
-#include "core/array.h"
+#include "core/variant/array.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
-#include "core/color.h"
+#include "core/math/color.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
diff --git a/modules/gdnative/gdnative/plane.cpp b/modules/gdnative/gdnative/plane.cpp
index d4ed8d00f4..99fb5ff10a 100644
--- a/modules/gdnative/gdnative/plane.cpp
+++ b/modules/gdnative/gdnative/plane.cpp
@@ -31,7 +31,7 @@
#include "gdnative/plane.h"
#include "core/math/plane.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
index de6308ad2a..a41886e780 100644
--- a/modules/gdnative/gdnative/quat.cpp
+++ b/modules/gdnative/gdnative/quat.cpp
@@ -31,7 +31,7 @@
#include "gdnative/quat.h"
#include "core/math/quat.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index 516f4d75ce..bacefced5d 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -31,7 +31,7 @@
#include "gdnative/rect2.h"
#include "core/math/transform_2d.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/rid.cpp b/modules/gdnative/gdnative/rid.cpp
index d7a63f33a7..24af04558b 100644
--- a/modules/gdnative/gdnative/rid.cpp
+++ b/modules/gdnative/gdnative/rid.cpp
@@ -30,9 +30,9 @@
#include "gdnative/rid.h"
-#include "core/resource.h"
-#include "core/rid.h"
-#include "core/variant.h"
+#include "core/io/resource.h"
+#include "core/templates/rid.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 26c40b625c..47c7f7b6e7 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -30,9 +30,9 @@
#include "gdnative/string.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
+#include "core/variant/variant.h"
#include <string.h>
@@ -40,9 +40,10 @@
extern "C" {
#endif
+static_assert(sizeof(godot_char16_string) == sizeof(Char16String), "Char16String size mismatch");
static_assert(sizeof(godot_char_string) == sizeof(CharString), "CharString size mismatch");
static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch");
-static_assert(sizeof(godot_char_type) == sizeof(CharType), "CharType size mismatch");
+static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch");
godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs) {
const CharString *cs = (const CharString *)p_cs;
@@ -62,6 +63,24 @@ void GDAPI godot_char_string_destroy(godot_char_string *p_cs) {
cs->~CharString();
}
+godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs) {
+ const Char16String *cs = (const Char16String *)p_cs;
+
+ return cs->length();
+}
+
+const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs) {
+ const Char16String *cs = (const Char16String *)p_cs;
+
+ return cs->get_data();
+}
+
+void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs) {
+ Char16String *cs = (Char16String *)p_cs;
+
+ cs->~Char16String();
+}
+
void GDAPI godot_string_new(godot_string *r_dest) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
@@ -70,27 +89,97 @@ void GDAPI godot_string_new(godot_string *r_dest) {
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src) {
String *dest = (String *)r_dest;
const String *src = (const String *)p_src;
- memnew_placement(dest, String(*src));
+ memnew_placement(dest, String);
+ *dest = String(*src);
+}
+
+void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String(p_contents);
+}
+
+void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents);
+}
+
+void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents);
+}
+
+void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+}
+
+void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents) {
+ String *dest = (String *)r_dest;
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents);
+ }
+}
+
+void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf8(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ dest->parse_utf16(p_contents, p_size);
+}
+
+void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size) {
+ String *dest = (String *)r_dest;
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
}
-void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size) {
+void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size) {
String *dest = (String *)r_dest;
- memnew_placement(dest, String(p_contents, p_size));
+ if (sizeof(wchar_t) == 2) {
+ // wchar_t is 16 bit, parse.
+ memnew_placement(dest, String);
+ dest->parse_utf16((const char16_t *)p_contents, p_size);
+ } else {
+ // wchar_t is 32 bit, copy.
+ memnew_placement(dest, String);
+ *dest = String((const char32_t *)p_contents, p_size);
+ }
}
-const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) {
+const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) {
String *self = (String *)p_self;
return &(self->operator[](p_idx));
}
-wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
+godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
const String *self = (const String *)p_self;
return self->operator[](p_idx);
}
-const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self) {
+const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->c_str();
+ return self->get_data();
}
godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b) {
@@ -162,22 +251,14 @@ godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self,
return self->begins_with(p_char_array);
}
-godot_array GDAPI godot_string_bigrams(const godot_string *p_self) {
+godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<String> return_value = self->bigrams();
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->bigrams()));
+ return ret;
};
-godot_string GDAPI godot_string_chr(wchar_t p_character) {
+godot_string GDAPI godot_string_chr(godot_char_type p_character) {
godot_string result;
memnew_placement(&result, String(String::chr(p_character)));
@@ -191,88 +272,73 @@ godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_
return self->ends_with(*string);
}
-godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+
+ return self->ends_with(p_char_array);
+}
+
+godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
+ const String *self = (const String *)p_self;
+ const String *what = (const String *)p_what;
return self->count(*what, p_from, p_to);
}
-godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to) {
+godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->countn(*what, p_from, p_to);
}
-godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->find(*what);
}
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->find(*what, p_from);
}
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys) {
+godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
- return self->findmk(keys);
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
+ return self->findmk(*keys);
}
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from) {
+godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
- return self->findmk(keys, p_from);
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
+ return self->findmk(*keys, p_from);
}
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key) {
+godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key) {
const String *self = (const String *)p_self;
-
- Vector<String> keys;
- Array *keys_proxy = (Array *)p_keys;
- keys.resize(keys_proxy->size());
- for (int i = 0; i < keys_proxy->size(); i++) {
- keys.write[i] = (*keys_proxy)[i];
- }
-
+ const Vector<String> *keys = (const Vector<String> *)p_keys;
int key;
- int ret = self->findmk(keys, p_from, &key);
+ int ret = self->findmk(*keys, p_from, &key);
if (r_key) {
*r_key = key;
}
return ret;
}
-godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->findn(*what);
}
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->findn(*what, p_from);
}
@@ -303,21 +369,9 @@ godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot
return result;
}
-godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int();
-}
-
-godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int(true);
-}
-
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string) {
+godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string) {
const String *self = (const String *)p_self;
- String *content = (String *)&p_string;
+ const String *content = (const String *)p_string;
godot_string result;
memnew_placement(&result, String(self->insert(p_at_pos, *content)));
@@ -440,58 +494,58 @@ godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int
return result;
}
-godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replace(*key, *with)));
return result;
}
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replacen(*key, *with)));
return result;
}
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfind(*what);
}
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what) {
+godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfindn(*what);
}
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfind(*what, p_from);
}
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from) {
+godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
const String *self = (const String *)p_self;
- String *what = (String *)&p_what;
+ const String *what = (const String *)p_what;
return self->rfindn(*what, p_from);
}
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with) {
+godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
const String *self = (const String *)p_self;
- String *key = (String *)&p_key;
- String *with = (String *)&p_with;
+ const String *key = (const String *)p_key;
+ const String *with = (const String *)p_with;
godot_string result;
memnew_placement(&result, String(self->replace_first(*key, *with)));
@@ -541,16 +595,16 @@ godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_f
return result;
}
-double GDAPI godot_string_to_float(const godot_string *p_self) {
+godot_int GDAPI godot_string_to_int(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->to_float();
+ return self->to_int();
}
-godot_int GDAPI godot_string_to_int(const godot_string *p_self) {
+double GDAPI godot_string_to_float(const godot_string *p_self) {
const String *self = (const String *)p_self;
- return self->to_int();
+ return self->to_float();
}
godot_string GDAPI godot_string_capitalize(const godot_string *p_self) {
@@ -581,11 +635,15 @@ double GDAPI godot_string_char_to_float(const char *p_what) {
return String::to_float(p_what);
}
+double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end) {
+ return String::to_float(p_str, r_end);
+}
+
godot_int GDAPI godot_string_char_to_int(const char *p_what) {
return String::to_int(p_what);
}
-int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str) {
+godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str) {
return String::to_int(p_str);
}
@@ -593,42 +651,32 @@ godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int
return String::to_int(p_what, p_len);
}
-int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len) {
+godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len) {
return String::to_int(p_str, p_len);
}
-int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self) {
+godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) {
const String *self = (const String *)p_self;
return self->hex_to_int(false);
}
-int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self) {
+godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self) {
const String *self = (const String *)p_self;
return self->hex_to_int();
}
-int64_t GDAPI godot_string_to_int64(const godot_string *p_self) {
+godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice) {
const String *self = (const String *)p_self;
-
- return self->to_int();
-}
-
-double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end) {
- return String::to_float(p_str, r_end);
-}
-
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice) {
- const String *self = (const String *)p_self;
- String *splitter = (String *)&p_splitter;
+ const String *splitter = (const String *)p_splitter;
godot_string result;
memnew_placement(&result, String(self->get_slice(*splitter, p_slice)));
return result;
}
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice) {
+godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice) {
const String *self = (const String *)p_self;
godot_string result;
memnew_placement(&result, String(self->get_slicec(p_splitter, p_slice)));
@@ -636,221 +684,149 @@ godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p
return result;
}
-godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, true)));
+ return ret;
+}
- return result;
+godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+ const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split(*splitter, p_allow_empty, p_maxsplit)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats_mk(splitters, false);
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, p_allow_empty, p_maxsplit)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) {
+ const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<float> return_value = self->split_floats_mk(splitters);
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, true)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints(*splitter, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_float32_array ret;
+ memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter) {
+godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
const String *splitter = (const String *)p_splitter;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints(*splitter);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, false)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
+ const String *splitter = (const String *)p_splitter;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints_mk(splitters, false);
-
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters) {
+godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- Vector<String> splitters;
- Array *splitter_proxy = (Array *)p_splitters;
- splitters.resize(splitter_proxy->size());
- for (int i = 0; i < splitter_proxy->size(); i++) {
- splitters.write[i] = (*splitter_proxy)[i];
- }
-
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<int> return_value = self->split_ints_mk(splitters);
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, false)));
+ return ret;
+}
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
+godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *splitters = (const Vector<String> *)p_splitters;
- return result;
+ godot_packed_int32_array ret;
+ memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, true)));
+ return ret;
}
-godot_array GDAPI godot_string_split_spaces(const godot_string *p_self) {
+godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self) {
const String *self = (const String *)p_self;
- godot_array result;
- memnew_placement(&result, Array);
- Array *proxy = (Array *)&result;
- Vector<String> return_value = self->split_spaces();
- proxy->resize(return_value.size());
- for (int i = 0; i < return_value.size(); i++) {
- (*proxy)[i] = return_value[i];
- }
-
- return result;
+ godot_packed_string_array ret;
+ memnew_placement(&ret, Vector<String>(self->split_spaces()));
+ return ret;
}
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter) {
+godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter) {
const String *self = (const String *)p_self;
- String *splitter = (String *)&p_splitter;
+ const String *splitter = (const String *)p_splitter;
return self->get_slice_count(*splitter);
}
-wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char) {
+godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char) {
return String::char_lowercase(p_char);
}
-wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char) {
+godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char) {
return String::char_uppercase(p_char);
}
@@ -894,7 +870,7 @@ godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos
return result;
}
-wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) {
+godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) {
const String *self = (const String *)p_self;
return self->ord_at(p_idx);
@@ -917,6 +893,14 @@ godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_po
return result;
}
+godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ memnew_placement(&result, String(self->repeat(p_count)));
+
+ return result;
+}
+
godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right) {
const String *self = (const String *)p_self;
godot_string result;
@@ -948,7 +932,7 @@ godot_char_string GDAPI godot_string_ascii(const godot_string *p_self) {
return result;
}
-godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self) {
+godot_char_string GDAPI godot_string_latin1(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_char_string result;
@@ -994,6 +978,42 @@ godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot
return result;
}
+godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+
+ godot_char16_string result;
+
+ memnew_placement(&result, Char16String(self->utf16()));
+
+ return result;
+}
+
+godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16) {
+ String *self = (String *)p_self;
+
+ return self->parse_utf16(p_utf16);
+}
+
+godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len) {
+ String *self = (String *)p_self;
+
+ return self->parse_utf16(p_utf16, p_len);
+}
+
+godot_string GDAPI godot_string_chars_to_utf16(const char16_t *p_utf16) {
+ godot_string result;
+ memnew_placement(&result, String(String::utf16(p_utf16)));
+
+ return result;
+}
+
+godot_string GDAPI godot_string_chars_to_utf16_with_len(const char16_t *p_utf16, godot_int p_len) {
+ godot_string result;
+ memnew_placement(&result, String(String::utf16(p_utf16, p_len)));
+
+ return result;
+}
+
uint32_t GDAPI godot_string_hash(const godot_string *p_self) {
const String *self = (const String *)p_self;
@@ -1014,28 +1034,18 @@ uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_
return String::hash(p_cstr, p_len);
}
-uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str) {
+uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str) {
return String::hash(p_str);
}
-uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len) {
+uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len) {
return String::hash(p_str, p_len);
}
godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<uint8_t> tmp_result = self->md5_buffer();
-
godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray);
- PackedByteArray *proxy = (PackedByteArray *)&result;
- uint8_t *proxy_writer = proxy->ptrw();
- proxy->resize(tmp_result.size());
-
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
-
+ memnew_placement(&result, PackedByteArray(self->md5_buffer()));
return result;
}
@@ -1047,23 +1057,28 @@ godot_string GDAPI godot_string_md5_text(const godot_string *p_self) {
return result;
}
-godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) {
+godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self) {
const String *self = (const String *)p_self;
- Vector<uint8_t> tmp_result = self->sha256_buffer();
-
godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray);
- PackedByteArray *proxy = (PackedByteArray *)&result;
- uint8_t *proxy_writer = proxy->ptrw();
- proxy->resize(tmp_result.size());
+ memnew_placement(&result, PackedByteArray(self->sha1_buffer()));
+ return result;
+}
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
+godot_string GDAPI godot_string_sha1_text(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_string result;
+ memnew_placement(&result, String(self->sha1_text()));
return result;
}
+godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+ godot_packed_byte_array result;
+ memnew_placement(&result, PackedByteArray(self->sha256_buffer()));
+ return result;
+}
+
godot_string GDAPI godot_string_sha256_text(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
@@ -1206,15 +1221,6 @@ godot_string GDAPI godot_string_json_escape(const godot_string *p_self) {
return result;
}
-godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->word_wrap(p_chars_per_line);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self) {
const String *self = (const String *)p_self;
godot_string result;
@@ -1260,6 +1266,22 @@ godot_string GDAPI godot_string_percent_encode(const godot_string *p_self) {
return result;
}
+godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts) {
+ const String *self = (const String *)p_self;
+ const Vector<String> *parts = (const Vector<String> *)p_parts;
+ godot_string result;
+ String return_value = self->join(*parts);
+ memnew_placement(&result, String(return_value));
+
+ return result;
+}
+
+godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self) {
+ const String *self = (const String *)p_self;
+
+ return self->is_valid_filename();
+}
+
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self) {
const String *self = (const String *)p_self;
@@ -1325,31 +1347,22 @@ godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const go
return result;
}
-godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
+godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars) {
const String *self = (const String *)p_self;
String *chars = (String *)p_chars;
godot_string result;
- String return_value = self->rstrip(*chars);
+ String return_value = self->lstrip(*chars);
memnew_placement(&result, String(return_value));
return result;
}
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor,
- const godot_bool p_allow_empty, const godot_int p_maxsplit) {
+godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
const String *self = (const String *)p_self;
- String *divisor = (String *)p_divisor;
-
- godot_packed_string_array result;
- memnew_placement(&result, PackedStringArray);
- PackedStringArray *proxy = (PackedStringArray *)&result;
- String *proxy_writer = proxy->ptrw();
- Vector<String> tmp_result = self->rsplit(*divisor, p_allow_empty, p_maxsplit);
- proxy->resize(tmp_result.size());
-
- for (int i = 0; i < tmp_result.size(); i++) {
- proxy_writer[i] = tmp_result[i];
- }
+ String *chars = (String *)p_chars;
+ godot_string result;
+ String return_value = self->rstrip(*chars);
+ memnew_placement(&result, String(return_value));
return result;
}
diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp
index 7bbaaeeaa0..a840d74e18 100644
--- a/modules/gdnative/gdnative/string_name.cpp
+++ b/modules/gdnative/gdnative/string_name.cpp
@@ -30,8 +30,8 @@
#include "gdnative/string_name.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
#include <string.h>
diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp
index d19de93e9b..b17d6f8d4c 100644
--- a/modules/gdnative/gdnative/transform.cpp
+++ b/modules/gdnative/gdnative/transform.cpp
@@ -31,7 +31,7 @@
#include "gdnative/transform.h"
#include "core/math/transform.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index c0f7878eb0..3c1105e323 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -31,7 +31,7 @@
#include "gdnative/transform2d.h"
#include "core/math/transform_2d.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp
index dac4feb0e5..417abeaad3 100644
--- a/modules/gdnative/gdnative/variant.cpp
+++ b/modules/gdnative/gdnative/variant.cpp
@@ -30,8 +30,8 @@
#include "gdnative/variant.h"
-#include "core/reference.h"
-#include "core/variant.h"
+#include "core/object/reference.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
@@ -576,7 +576,9 @@ godot_variant GDAPI godot_variant_call(godot_variant *p_self, const godot_string
godot_variant raw_dest;
Variant *dest = (Variant *)&raw_dest;
Callable::CallError error;
- memnew_placement_custom(dest, Variant, Variant(self->call(*method, args, p_argcount, error)));
+ Variant ret;
+ self->call(*method, args, p_argcount, ret, error);
+ memnew_placement_custom(dest, Variant, Variant(ret));
if (r_error) {
r_error->error = (godot_variant_call_error_error)error.error;
r_error->argument = error.argument;
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index 1ee716df86..1ba846d315 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -31,7 +31,7 @@
#include "gdnative/vector2.h"
#include "core/math/vector2.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp
index 32cad30c17..3284afdc31 100644
--- a/modules/gdnative/gdnative/vector3.cpp
+++ b/modules/gdnative/gdnative/vector3.cpp
@@ -30,8 +30,8 @@
#include "gdnative/vector3.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 8ccf44ff1a..40d0f75871 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -1255,13 +1255,6 @@
]
},
{
- "name": "godot_color_contrasted",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
"name": "godot_color_lerp",
"return_type": "godot_color",
"arguments": [
@@ -3732,6 +3725,27 @@
]
},
{
+ "name": "godot_char16_string_length",
+ "return_type": "godot_int",
+ "arguments": [
+ ["const godot_char16_string *", "p_cs"]
+ ]
+ },
+ {
+ "name": "godot_char16_string_get_data",
+ "return_type": "const char16_t *",
+ "arguments": [
+ ["const godot_char16_string *", "p_cs"]
+ ]
+ },
+ {
+ "name": "godot_char16_string_destroy",
+ "return_type": "void",
+ "arguments": [
+ ["godot_char16_string *", "p_cs"]
+ ]
+ },
+ {
"name": "godot_string_new",
"return_type": "void",
"arguments": [
@@ -3747,7 +3761,83 @@
]
},
{
- "name": "godot_string_new_with_wide_string",
+ "name": "godot_string_new_with_latin1_chars",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf8_chars",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf16_chars",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char16_t *", "p_contents"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf32_chars",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char32_t *", "p_contents"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_wide_chars",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const wchar_t *", "p_contents"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_latin1_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"],
+ ["const int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf8_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char *", "p_contents"],
+ ["const int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf16_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char16_t *", "p_contents"],
+ ["const int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf32_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ ["godot_string *", "r_dest"],
+ ["const char32_t *", "p_contents"],
+ ["const int", "p_size"]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_wide_chars_and_len",
"return_type": "void",
"arguments": [
["godot_string *", "r_dest"],
@@ -3757,7 +3847,7 @@
},
{
"name": "godot_string_operator_index",
- "return_type": "const wchar_t *",
+ "return_type": "const godot_char_type *",
"arguments": [
["godot_string *", "p_self"],
["const godot_int", "p_idx"]
@@ -3765,15 +3855,15 @@
},
{
"name": "godot_string_operator_index_const",
- "return_type": "wchar_t",
+ "return_type": "godot_char_type",
"arguments": [
["const godot_string *", "p_self"],
["const godot_int", "p_idx"]
]
},
{
- "name": "godot_string_wide_str",
- "return_type": "const wchar_t *",
+ "name": "godot_string_get_data",
+ "return_type": "const godot_char_type *",
"arguments": [
["const godot_string *", "p_self"]
]
@@ -3807,7 +3897,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"],
["godot_int", "p_to"]
]
@@ -3817,7 +3907,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"],
["godot_int", "p_to"]
]
@@ -3878,7 +3968,7 @@
},
{
"name": "godot_string_bigrams",
- "return_type": "godot_array",
+ "return_type": "godot_packed_string_array",
"arguments": [
["const godot_string *", "p_self"]
]
@@ -3887,7 +3977,7 @@
"name": "godot_string_chr",
"return_type": "godot_string",
"arguments": [
- ["wchar_t", "p_character"]
+ ["godot_char_type", "p_character"]
]
},
{
@@ -3899,11 +3989,19 @@
]
},
{
+ "name": "godot_string_ends_with_char_array",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const char *", "p_char_array"]
+ ]
+ },
+ {
"name": "godot_string_find",
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_string *", "p_what"]
]
},
{
@@ -3911,7 +4009,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"]
]
},
@@ -3920,7 +4018,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"]
+ ["const godot_packed_string_array *", "p_keys"]
]
},
{
@@ -3928,7 +4026,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"],
+ ["const godot_packed_string_array *", "p_keys"],
["godot_int", "p_from"]
]
},
@@ -3937,7 +4035,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_keys"],
+ ["const godot_packed_string_array *", "p_keys"],
["godot_int", "p_from"],
["godot_int *", "r_key"]
]
@@ -3947,7 +4045,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_string *", "p_what"]
]
},
{
@@ -3955,7 +4053,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"]
]
},
@@ -3985,26 +4083,12 @@
]
},
{
- "name": "godot_string_hex_to_int",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_hex_to_int_without_prefix",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
"name": "godot_string_insert",
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
["godot_int", "p_at_pos"],
- ["godot_string", "p_string"]
+ ["const godot_string *", "p_string"]
]
},
{
@@ -4137,8 +4221,8 @@
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
]
},
{
@@ -4146,8 +4230,8 @@
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
]
},
{
@@ -4155,8 +4239,8 @@
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_key"],
- ["godot_string", "p_with"]
+ ["const godot_string *", "p_key"],
+ ["const godot_string *", "p_with"]
]
},
{
@@ -4164,7 +4248,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_string *", "p_what"]
]
},
{
@@ -4172,7 +4256,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"]
+ ["const godot_string *", "p_what"]
]
},
{
@@ -4180,7 +4264,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"]
]
},
@@ -4189,7 +4273,7 @@
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_what"],
+ ["const godot_string *", "p_what"],
["godot_int", "p_from"]
]
},
@@ -4237,15 +4321,15 @@
]
},
{
- "name": "godot_string_to_float",
- "return_type": "double",
+ "name": "godot_string_to_int",
+ "return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"]
]
},
{
- "name": "godot_string_to_int",
- "return_type": "godot_int",
+ "name": "godot_string_to_float",
+ "return_type": "double",
"arguments": [
["const godot_string *", "p_self"]
]
@@ -4279,6 +4363,14 @@
]
},
{
+ "name": "godot_string_wchar_to_float",
+ "return_type": "double",
+ "arguments": [
+ ["const wchar_t *", "p_str"],
+ ["const wchar_t **", "r_end"]
+ ]
+ },
+ {
"name": "godot_string_char_to_int",
"return_type": "godot_int",
"arguments": [
@@ -4287,7 +4379,7 @@
},
{
"name": "godot_string_wchar_to_int",
- "return_type": "int64_t",
+ "return_type": "godot_int",
"arguments": [
["const wchar_t *", "p_str"]
]
@@ -4301,48 +4393,33 @@
]
},
{
- "name": "godot_string_char_to_int64_with_len",
- "return_type": "int64_t",
+ "name": "godot_string_wchar_to_int_with_len",
+ "return_type": "godot_int",
"arguments": [
["const wchar_t *", "p_str"],
["int", "p_len"]
]
},
{
- "name": "godot_string_hex_to_int64",
- "return_type": "int64_t",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_hex_to_int64_with_prefix",
- "return_type": "int64_t",
+ "name": "godot_string_hex_to_int",
+ "return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"]
]
},
{
- "name": "godot_string_to_int64",
- "return_type": "int64_t",
+ "name": "godot_string_hex_to_int_with_prefix",
+ "return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"]
]
},
{
- "name": "godot_string_unicode_char_to_float",
- "return_type": "double",
- "arguments": [
- ["const wchar_t *", "p_str"],
- ["const wchar_t **", "r_end"]
- ]
- },
- {
"name": "godot_string_get_slice_count",
"return_type": "godot_int",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_splitter"]
+ ["const godot_string *", "p_splitter"]
]
},
{
@@ -4350,7 +4427,7 @@
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["godot_string", "p_splitter"],
+ ["const godot_string *", "p_splitter"],
["godot_int", "p_slice"]
]
},
@@ -4359,13 +4436,13 @@
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["wchar_t", "p_splitter"],
+ ["godot_char_type", "p_splitter"],
["godot_int", "p_slice"]
]
},
{
"name": "godot_string_split",
- "return_type": "godot_array",
+ "return_type": "godot_packed_string_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
@@ -4373,23 +4450,59 @@
},
{
"name": "godot_string_split_allow_empty",
- "return_type": "godot_array",
+ "return_type": "godot_packed_string_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
]
},
{
+ "name": "godot_string_split_with_maxsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit_allow_empty",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"]
+ ]
+ },
+ {
+ "name": "godot_string_rsplit_with_maxsplit",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_string *", "p_splitter"],
+ ["const godot_bool", "p_allow_empty"],
+ ["const godot_int", "p_maxsplit"]
+ ]
+ },
+ {
"name": "godot_string_split_floats",
- "return_type": "godot_array",
+ "return_type": "godot_packed_float32_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
]
},
{
- "name": "godot_string_split_floats_allows_empty",
- "return_type": "godot_array",
+ "name": "godot_string_split_floats_allow_empty",
+ "return_type": "godot_packed_float32_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
@@ -4397,31 +4510,31 @@
},
{
"name": "godot_string_split_floats_mk",
- "return_type": "godot_array",
+ "return_type": "godot_packed_float32_array",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_packed_string_array *", "p_splitters"]
]
},
{
- "name": "godot_string_split_floats_mk_allows_empty",
- "return_type": "godot_array",
+ "name": "godot_string_split_floats_mk_allow_empty",
+ "return_type": "godot_packed_float32_array",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_packed_string_array *", "p_splitters"]
]
},
{
"name": "godot_string_split_ints",
- "return_type": "godot_array",
+ "return_type": "godot_packed_int32_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
]
},
{
- "name": "godot_string_split_ints_allows_empty",
- "return_type": "godot_array",
+ "name": "godot_string_split_ints_allow_empty",
+ "return_type": "godot_packed_int32_array",
"arguments": [
["const godot_string *", "p_self"],
["const godot_string *", "p_splitter"]
@@ -4429,29 +4542,29 @@
},
{
"name": "godot_string_split_ints_mk",
- "return_type": "godot_array",
+ "return_type": "godot_packed_int32_array",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_packed_string_array *", "p_splitters"]
]
},
{
- "name": "godot_string_split_ints_mk_allows_empty",
- "return_type": "godot_array",
+ "name": "godot_string_split_ints_mk_allow_empty",
+ "return_type": "godot_packed_int32_array",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_array *", "p_splitters"]
+ ["const godot_packed_string_array *", "p_splitters"]
]
},
{
"name": "godot_string_split_spaces",
- "return_type": "godot_array",
+ "return_type": "godot_packed_string_array",
"arguments": [
["const godot_string *", "p_self"]
]
},
{
- "name": "godot_string_rstrip",
+ "name": "godot_string_lstrip",
"return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
@@ -4459,13 +4572,11 @@
]
},
{
- "name": "godot_string_rsplit",
- "return_type": "godot_packed_string_array",
+ "name": "godot_string_rstrip",
+ "return_type": "godot_string",
"arguments": [
["const godot_string *", "p_self"],
- ["const godot_string *", "p_divisor"],
- ["const godot_bool", "p_allow_empty"],
- ["const godot_int", "p_maxsplit"]
+ ["const godot_string *", "p_chars"]
]
},
{
@@ -4486,16 +4597,16 @@
},
{
"name": "godot_string_char_lowercase",
- "return_type": "wchar_t",
+ "return_type": "godot_char_type",
"arguments": [
- ["wchar_t", "p_char"]
+ ["godot_char_type", "p_char"]
]
},
{
"name": "godot_string_char_uppercase",
- "return_type": "wchar_t",
+ "return_type": "godot_char_type",
"arguments": [
- ["wchar_t", "p_char"]
+ ["godot_char_type", "p_char"]
]
},
{
@@ -4536,7 +4647,7 @@
},
{
"name": "godot_string_ord_at",
- "return_type": "wchar_t",
+ "return_type": "godot_char_type",
"arguments": [
["const godot_string *", "p_self"],
["godot_int", "p_idx"]
@@ -4559,6 +4670,14 @@
]
},
{
+ "name": "godot_string_repeat",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["godot_int", "p_count"]
+ ]
+ },
+ {
"name": "godot_string_strip_edges",
"return_type": "godot_string",
"arguments": [
@@ -4591,7 +4710,7 @@
]
},
{
- "name": "godot_string_ascii_extended",
+ "name": "godot_string_latin1",
"return_type": "godot_char_string",
"arguments": [
["const godot_string *", "p_self"]
@@ -4622,17 +4741,26 @@
]
},
{
- "name": "godot_string_chars_to_utf8",
- "return_type": "godot_string",
+ "name": "godot_string_utf16",
+ "return_type": "godot_char16_string",
"arguments": [
- ["const char *", "p_utf8"]
+ ["const godot_string *", "p_self"]
]
},
{
- "name": "godot_string_chars_to_utf8_with_len",
- "return_type": "godot_string",
+ "name": "godot_string_parse_utf16",
+ "return_type": "godot_bool",
"arguments": [
- ["const char *", "p_utf8"],
+ ["godot_string *", "p_self"],
+ ["const char16_t *", "p_utf16"]
+ ]
+ },
+ {
+ "name": "godot_string_parse_utf16_with_len",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["godot_string *", "p_self"],
+ ["const char16_t *", "p_utf16"],
["godot_int", "p_len"]
]
},
@@ -4666,14 +4794,14 @@
]
},
{
- "name": "godot_string_hash_utf8_chars",
+ "name": "godot_string_hash_wide_chars",
"return_type": "uint32_t",
"arguments": [
["const wchar_t *", "p_str"]
]
},
{
- "name": "godot_string_hash_utf8_chars_with_len",
+ "name": "godot_string_hash_wide_chars_with_len",
"return_type": "uint32_t",
"arguments": [
["const wchar_t *", "p_str"],
@@ -4695,6 +4823,20 @@
]
},
{
+ "name": "godot_string_sha1_buffer",
+ "return_type": "godot_packed_byte_array",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
+ "name": "godot_string_sha1_text",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
"name": "godot_string_sha256_buffer",
"return_type": "godot_packed_byte_array",
"arguments": [
@@ -4823,14 +4965,6 @@
]
},
{
- "name": "godot_string_word_wrap",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_chars_per_line"]
- ]
- },
- {
"name": "godot_string_xml_escape",
"return_type": "godot_string",
"arguments": [
@@ -4866,6 +5000,21 @@
]
},
{
+ "name": "godot_string_join",
+ "return_type": "godot_string",
+ "arguments": [
+ ["const godot_string *", "p_self"],
+ ["const godot_packed_string_array *", "p_parts"]
+ ]
+ },
+ {
+ "name": "godot_string_is_valid_filename",
+ "return_type": "godot_bool",
+ "arguments": [
+ ["const godot_string *", "p_self"]
+ ]
+ },
+ {
"name": "godot_string_is_valid_float",
"return_type": "godot_bool",
"arguments": [
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index fdd755845f..f0f095ddf5 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -318,6 +318,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
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.
// Frameworks is actually a folder with files.
platform_ios.library_extension = "*.framework; Framework, *.xcframework; Binary Framework, *.a; Static Library, *.dylib; Dynamic Library";
diff --git a/modules/gdnative/icons/GDNativeLibrary.svg b/modules/gdnative/icons/GDNativeLibrary.svg
index b494c7af6e..0ddfd4e6f2 100644
--- a/modules/gdnative/icons/GDNativeLibrary.svg
+++ b/modules/gdnative/icons/GDNativeLibrary.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539v-5.2695a2 2 0 0 1 -1 -1.7305 2 2 0 0 1 1 -1.7285v-0.27148h1 4.5762a5 5 0 0 0 -0.11328 -0.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm2 7v1 5 1h5c0.55228 0 1-0.4477 1-1v-5c0-0.5523-0.44772-1-1-1v4l-1-1-1 1v-4h-3z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539v-5.2695a2 2 0 0 1 -1-1.7305 2 2 0 0 1 1-1.7285v-.27148h1 4.5762a5 5 0 0 0 -.11328-.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm2 7v1 5 1h5c.55228 0 1-.4477 1-1v-5c0-.5523-.44772-1-1-1v4l-1-1-1 1v-4z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdnative/icons/NativeScript.svg b/modules/gdnative/icons/NativeScript.svg
index fb9e135627..2224b36b29 100644
--- a/modules/gdnative/icons/NativeScript.svg
+++ b/modules/gdnative/icons/NativeScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625h3v1 1h2v-0.95117a2 2 0 0 1 0 -0.048828 2 2 0 0 1 2 -2 2 2 0 0 1 2 2v1h5v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3 -3 3 3 0 0 0 -3 -3v-2h-2zm6 0v2h2v-2h-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3 -3h-2zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v-2zm4 0v4h2v-4h-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625h3v1 1h2v-.95117a2 2 0 0 1 0-.048828 2 2 0 0 1 2-2 2 2 0 0 1 2 2v1h5v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3v-2zm6 0v2h2v-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3-3zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1zm4 0v4h2v-4z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h
index e7737bf8e1..e64097ef57 100644
--- a/modules/gdnative/include/gdnative/color.h
+++ b/modules/gdnative/include/gdnative/color.h
@@ -93,8 +93,6 @@ godot_int GDAPI godot_color_to_argb32(const godot_color *p_self);
godot_color GDAPI godot_color_inverted(const godot_color *p_self);
-godot_color GDAPI godot_color_contrasted(const godot_color *p_self);
-
godot_color GDAPI godot_color_lerp(const godot_color *p_self, const godot_color *p_b, const godot_real p_t);
godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over);
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index d89383dc1b..6043351e84 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -35,13 +35,19 @@
extern "C" {
#endif
+#include <stddef.h>
#include <stdint.h>
-#include <wchar.h>
-typedef wchar_t godot_char_type;
+#ifndef __cplusplus
+typedef uint32_t char32_t;
+typedef uint16_t char16_t;
+#endif
+
+typedef char32_t godot_char_type;
#define GODOT_STRING_SIZE sizeof(void *)
#define GODOT_CHAR_STRING_SIZE sizeof(void *)
+#define GODOT_CHAR16_STRING_SIZE sizeof(void *)
#ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
@@ -58,6 +64,13 @@ typedef struct {
} godot_char_string;
#endif
+#ifndef GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_CHAR16_STRING_SIZE];
+} godot_char16_string;
+#endif
+
// reduce extern "C" nesting for VS2013
#ifdef __cplusplus
}
@@ -75,13 +88,28 @@ godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs);
const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs);
void GDAPI godot_char_string_destroy(godot_char_string *p_cs);
+godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs);
+const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs);
+void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs);
+
void GDAPI godot_string_new(godot_string *r_dest);
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src);
-void GDAPI godot_string_new_with_wide_string(godot_string *r_dest, const wchar_t *p_contents, const int p_size);
-const wchar_t GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
-wchar_t GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
-const wchar_t GDAPI *godot_string_wide_str(const godot_string *p_self);
+void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents);
+void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents);
+void GDAPI godot_string_new_with_utf16_chars(godot_string *r_dest, const char16_t *p_contents);
+void GDAPI godot_string_new_with_utf32_chars(godot_string *r_dest, const char32_t *p_contents);
+void GDAPI godot_string_new_with_wide_chars(godot_string *r_dest, const wchar_t *p_contents);
+
+void GDAPI godot_string_new_with_latin1_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf8_chars_and_len(godot_string *r_dest, const char *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const char16_t *p_contents, const int p_size);
+void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size);
+void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size);
+
+const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
+godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
+const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self);
godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b);
godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b);
@@ -89,7 +117,7 @@ godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const
/* Standard size stuff */
-godot_int GDAPI godot_string_length(const godot_string *p_self);
+/*+++*/ godot_int GDAPI godot_string_length(const godot_string *p_self);
/* Helpers */
@@ -99,24 +127,25 @@ signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, c
godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string);
godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array);
-godot_array GDAPI godot_string_bigrams(const godot_string *p_self);
-godot_string GDAPI godot_string_chr(wchar_t p_character);
+godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self);
+godot_string GDAPI godot_string_chr(godot_char_type p_character);
godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string);
-godot_int GDAPI godot_string_count(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_countn(const godot_string *p_self, godot_string p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_find(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_array *p_keys);
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_array *p_keys, godot_int p_from);
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_array *p_keys, godot_int p_from, godot_int *r_key);
-godot_int GDAPI godot_string_findn(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
+godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array);
+godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
+godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
+godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
+godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys);
+godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from);
+godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key);
+godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values);
godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder);
godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len);
godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self);
-godot_int GDAPI godot_string_hex_to_int_without_prefix(const godot_string *p_self);
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, godot_string p_string);
+godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self);
+godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string);
godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self);
godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string);
godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string);
@@ -133,13 +162,13 @@ godot_string GDAPI godot_string_num_scientific(double p_num);
godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals);
godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits);
godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits);
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_string GDAPI godot_string_replace(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, godot_string p_key, godot_string p_with);
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, godot_string p_what);
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, godot_string p_what, godot_int p_from);
+godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
+godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what);
+godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
+godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length);
godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character);
godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string);
@@ -151,64 +180,79 @@ godot_int GDAPI godot_string_to_int(const godot_string *p_self);
godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self);
godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self);
godot_string GDAPI godot_string_capitalize(const godot_string *p_self);
+
double GDAPI godot_string_char_to_float(const char *p_what);
+double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end);
+
godot_int GDAPI godot_string_char_to_int(const char *p_what);
-int64_t GDAPI godot_string_wchar_to_int(const wchar_t *p_str);
+godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str);
+
godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len);
-int64_t GDAPI godot_string_char_to_int64_with_len(const wchar_t *p_str, int p_len);
-int64_t GDAPI godot_string_hex_to_int64(const godot_string *p_self);
-int64_t GDAPI godot_string_hex_to_int64_with_prefix(const godot_string *p_self);
-int64_t GDAPI godot_string_to_int64(const godot_string *p_self);
-double GDAPI godot_string_unicode_char_to_float(const wchar_t *p_str, const wchar_t **r_end);
-
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, godot_string p_splitter);
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, godot_string p_splitter, godot_int p_slice);
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, wchar_t p_splitter, godot_int p_slice);
-
-godot_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats_allows_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_floats_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_ints_allows_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_ints_mk_allows_empty(const godot_string *p_self, const godot_array *p_splitters);
-godot_array GDAPI godot_string_split_spaces(const godot_string *p_self);
-
-wchar_t GDAPI godot_string_char_lowercase(wchar_t p_char);
-wchar_t GDAPI godot_string_char_uppercase(wchar_t p_char);
+godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len);
+
+godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter);
+godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice);
+godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice);
+
+godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
+godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
+
+godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
+godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
+
+godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self);
+
+godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char);
+godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char);
godot_string GDAPI godot_string_to_lower(const godot_string *p_self);
godot_string GDAPI godot_string_to_upper(const godot_string *p_self);
godot_string GDAPI godot_string_get_basename(const godot_string *p_self);
godot_string GDAPI godot_string_get_extension(const godot_string *p_self);
godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos);
-wchar_t GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx);
+godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx);
godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file);
godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos);
+godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count);
godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right);
godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self);
void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars);
godot_char_string GDAPI godot_string_ascii(const godot_string *p_self);
-godot_char_string GDAPI godot_string_ascii_extended(const godot_string *p_self);
+godot_char_string GDAPI godot_string_latin1(const godot_string *p_self);
+
godot_char_string GDAPI godot_string_utf8(const godot_string *p_self);
godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8);
godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len);
-godot_string GDAPI godot_string_chars_to_utf8(const char *p_utf8);
-godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot_int p_len);
+
+godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self);
+godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16);
+godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len);
uint32_t GDAPI godot_string_hash(const godot_string *p_self);
uint64_t GDAPI godot_string_hash64(const godot_string *p_self);
+
uint32_t GDAPI godot_string_hash_chars(const char *p_cstr);
uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len);
-uint32_t GDAPI godot_string_hash_utf8_chars(const wchar_t *p_str);
-uint32_t GDAPI godot_string_hash_utf8_chars_with_len(const wchar_t *p_str, godot_int p_len);
+uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str);
+uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len);
+
godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self);
godot_string GDAPI godot_string_md5_text(const godot_string *p_self);
+godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self);
+godot_string GDAPI godot_string_sha1_text(const godot_string *p_self);
godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self);
godot_string GDAPI godot_string_sha256_text(const godot_string *p_self);
@@ -231,14 +275,15 @@ godot_string GDAPI godot_string_c_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_http_escape(const godot_string *p_self);
godot_string GDAPI godot_string_http_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_json_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_word_wrap(const godot_string *p_self, godot_int p_chars_per_line);
godot_string GDAPI godot_string_xml_escape(const godot_string *p_self);
godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self);
godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self);
godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
+godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts);
+godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self);
godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix);
godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self);
@@ -249,8 +294,8 @@ godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self);
godot_string GDAPI godot_string_dedent(const godot_string *p_self);
godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix);
godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix);
+godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars);
godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars);
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_divisor, const godot_bool p_allow_empty, const godot_int p_maxsplit);
void GDAPI godot_string_destroy(godot_string *p_self);
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 019fa0d1f8..6f2f9bfea9 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -32,11 +32,11 @@
#ifdef TOOLS_ENABLED
-#include "core/class_db.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/object/class_db.h"
#include "core/os/file_access.h"
-#include "core/pair.h"
+#include "core/templates/pair.h"
// helper stuff
@@ -173,19 +173,19 @@ List<ClassAPI> generate_c_api_classes() {
ClassDB::get_class_list(&classes);
classes.sort_custom<StringName::AlphCompare>();
- // Register global constants as a fake GlobalConstants singleton class
+ // Register global constants as a fake CoreConstants singleton class
{
ClassAPI global_constants_api;
- global_constants_api.class_name = L"GlobalConstants";
+ global_constants_api.class_name = "CoreConstants";
global_constants_api.api_type = ClassDB::API_CORE;
global_constants_api.is_singleton = true;
- global_constants_api.singleton_name = L"GlobalConstants";
+ global_constants_api.singleton_name = "CoreConstants";
global_constants_api.is_instanciable = false;
- const int constants_count = GlobalConstants::get_global_constant_count();
+ const int constants_count = CoreConstants::get_global_constant_count();
for (int i = 0; i < constants_count; ++i) {
ConstantAPI constant_api;
- constant_api.constant_name = GlobalConstants::get_global_constant_name(i);
- constant_api.constant_value = GlobalConstants::get_global_constant_value(i);
+ constant_api.constant_name = CoreConstants::get_global_constant_name(i);
+ constant_api.constant_value = CoreConstants::get_global_constant_value(i);
global_constants_api.constants.push_back(constant_api);
}
global_constants_api.constants.sort_custom<ConstantAPIComparator>();
diff --git a/modules/gdnative/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h
index edbb1d1f23..8555af5215 100644
--- a/modules/gdnative/nativescript/api_generator.h
+++ b/modules/gdnative/nativescript/api_generator.h
@@ -31,8 +31,8 @@
#ifndef API_GENERATOR_H
#define API_GENERATOR_H
+#include "core/string/ustring.h"
#include "core/typedefs.h"
-#include "core/ustring.h"
Error generate_c_api(const String &p_path);
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index e47548f3e9..411acbe1ad 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -30,11 +30,11 @@
#include "nativescript/godot_nativescript.h"
-#include "core/class_db.h"
-#include "core/error_macros.h"
-#include "core/global_constants.h"
-#include "core/project_settings.h"
-#include "core/variant.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
+#include "core/variant/variant.h"
#include "gdnative/gdnative.h"
#include <stdint.h>
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 632f4e5fee..0939cfd06a 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -34,12 +34,12 @@
#include "gdnative/gdnative.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/core_string_names.h"
-#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/resource_format_text.h"
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 145bf7dcb6..2aaa4be325 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -31,15 +31,15 @@
#ifndef NATIVE_SCRIPT_H
#define NATIVE_SCRIPT_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/oa_hash_map.h"
-#include "core/ordered_hash_map.h"
+#include "core/object/script_language.h"
#include "core/os/mutex.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/ordered_hash_map.h"
+#include "core/templates/self_list.h"
#include "scene/main/node.h"
#include "modules/gdnative/gdnative.h"
diff --git a/modules/gdnative/net/webrtc_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp
index a7355e4d12..d8c3ddc5f8 100644
--- a/modules/gdnative/net/webrtc_gdnative.cpp
+++ b/modules/gdnative/net/webrtc_gdnative.cpp
@@ -54,7 +54,7 @@ godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p
#ifdef WEBRTC_GDNATIVE_ENABLED
return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib);
#else
- return ERR_UNAVAILABLE;
+ return (godot_error)ERR_UNAVAILABLE;
#endif
}
}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index 7d17a7d5ab..0942fb40a8 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -32,7 +32,7 @@
// Godot imports
#include "core/os/os.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
// PluginScript imports
#include "pluginscript_language.h"
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 690d1a0432..76ff9f7097 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -32,7 +32,7 @@
#define PLUGINSCRIPT_INSTANCE_H
// Godot imports
-#include "core/script_language.h"
+#include "core/object/script_language.h"
// PluginScript imports
#include <pluginscript/godot_pluginscript.h>
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index bccbe95033..fc9c4ebd77 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -29,9 +29,9 @@
/*************************************************************************/
// Godot imports
+#include "core/config/project_settings.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
// PluginScript imports
#include "pluginscript_language.h"
#include "pluginscript_script.h"
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index dd6758713f..53e1e3ae9b 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -34,9 +34,9 @@
// Godot imports
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/map.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/object/script_language.h"
+#include "core/templates/map.h"
+#include "core/templates/self_list.h"
// PluginScript imports
#include "pluginscript_loader.h"
#include <pluginscript/godot_pluginscript.h>
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h
index 35fc79c2ca..7d80f4c733 100644
--- a/modules/gdnative/pluginscript/pluginscript_loader.h
+++ b/modules/gdnative/pluginscript/pluginscript_loader.h
@@ -34,7 +34,7 @@
// Godot imports
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
class PluginScriptLanguage;
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 9cd38cd4b4..150de05e74 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -32,7 +32,7 @@
#define PLUGINSCRIPT_SCRIPT_H
// Godot imports
-#include "core/script_language.h"
+#include "core/object/script_language.h"
// PluginScript imports
#include "pluginscript_language.h"
#include <pluginscript/godot_pluginscript.h>
diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
index 97a48b0e89..b354c23a9e 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -30,11 +30,11 @@
#include "register_types.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/dir_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/scene_tree.h"
#include "pluginscript_language.h"
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 3a2d0b09a3..b88bf58256 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -40,11 +40,11 @@
#include "videodecoder/register_types.h"
#include "xr/register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_export.h"
diff --git a/modules/gdnative/tests/test_string.h b/modules/gdnative/tests/test_string.h
new file mode 100644
index 0000000000..aeb855a1c4
--- /dev/null
+++ b/modules/gdnative/tests/test_string.h
@@ -0,0 +1,1980 @@
+/*************************************************************************/
+/* test_string.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 TEST_GDNATIVE_STRING_H
+#define TEST_GDNATIVE_STRING_H
+
+namespace TestGDNativeString {
+
+#include "gdnative/string.h"
+
+#include "tests/test_macros.h"
+
+int u32scmp(const char32_t *l, const char32_t *r) {
+ for (; *l == *r && *l && *r; l++, r++)
+ ;
+ return *l - *r;
+}
+
+TEST_CASE("[GDNative String] Construct from Latin-1 char string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ CHECK(u32scmp(godot_string_get_data(&s), U"Hello") == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars_and_len(&s, "Hello", 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"Hel") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from wchar_t string") {
+ godot_string s;
+
+ godot_string_new_with_wide_chars(&s, L"Give me");
+ CHECK(u32scmp(godot_string_get_data(&s), U"Give me") == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_wide_chars_and_len(&s, L"Give me", 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"Giv") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-8 char string") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
+ static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf8_chars(&s, (const char *)u8str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ godot_char_string cs = godot_string_utf8(&s);
+ godot_string_parse_utf8(&s, godot_char_string_get_data(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char_string_destroy(&cs);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ cs = godot_string_utf8(&s);
+ godot_string_parse_utf8_with_len(&s, godot_char_string_get_data(&cs), godot_char_string_length(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char_string_destroy(&cs);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-8 string with BOM") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
+ static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf8_chars(&s, (const char *)u8str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 8);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-16 string") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
+ static const char16_t u16str[] = { 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf16_chars(&s, u16str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str, 4);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ godot_char16_string cs = godot_string_utf16(&s);
+ godot_string_parse_utf16(&s, godot_char16_string_get_data(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char16_string_destroy(&cs);
+
+ godot_string_new_with_utf32_chars(&s, u32str);
+ cs = godot_string_utf16(&s);
+ godot_string_parse_utf16_with_len(&s, godot_char16_string_get_data(&cs), godot_char16_string_length(&cs));
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+ godot_char16_string_destroy(&cs);
+}
+
+TEST_CASE("[GDNative String] Construct from UTF-16 string with BOM ") {
+ static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
+ static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
+ static const char16_t u16str[] = { 0xFEFF, 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
+ static const char16_t u16str_swap[] = { 0xFFFE, 0x4500, 0x2000, 0x3CD8, 0xA4DF, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 };
+
+ godot_string s;
+
+ godot_string_new_with_utf16_chars(&s, u16str);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars(&s, u16str_swap);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_utf16_chars_and_len(&s, u16str_swap, 5);
+ CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct string copy") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ godot_string_new_copy(&t, &s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Hello") == 0);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Construct empty string") {
+ godot_string s;
+
+ godot_string_new(&s);
+ CHECK(u32scmp(godot_string_get_data(&s), U"") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] ASCII/Latin-1") {
+ godot_string s;
+ godot_string_new_with_utf32_chars(&s, U"Primero Leche");
+
+ godot_char_string cs = godot_string_ascii(&s);
+ CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
+ godot_char_string_destroy(&cs);
+
+ cs = godot_string_latin1(&s);
+ CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
+ godot_char_string_destroy(&cs);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Comparisons (equal)") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Test Compare");
+ godot_string_new_with_latin1_chars(&t, "Test Compare");
+ CHECK(godot_string_operator_equal(&s, &t));
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Comparisons (operator <)") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "Bees");
+
+ godot_string_new_with_latin1_chars(&t, "Elephant");
+ CHECK(godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Amber");
+ CHECK(!godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Beatrix");
+ CHECK(!godot_string_operator_less(&s, &t));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Concatenation (operator +)") {
+ godot_string s, t, x;
+
+ godot_string_new_with_latin1_chars(&s, "Hel");
+ godot_string_new_with_latin1_chars(&t, "lo");
+ x = godot_string_operator_plus(&s, &t);
+ CHECK(u32scmp(godot_string_get_data(&x), U"Hello") == 0);
+ godot_string_destroy(&x);
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Testing size and length of string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Mellon");
+ CHECK(godot_string_length(&s) == 6);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "Mellon1");
+ CHECK(godot_string_length(&s) == 7);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Testing for empty string") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Mellon");
+ CHECK(!godot_string_empty(&s));
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "");
+ CHECK(godot_string_empty(&s));
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Test chr") {
+ godot_string s;
+
+ s = godot_string_chr('H');
+ CHECK(u32scmp(godot_string_get_data(&s), U"H") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_chr(0x3012);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0x3012);
+ godot_string_destroy(&s);
+
+ ERR_PRINT_OFF
+ s = godot_string_chr(0xd812);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Unpaired UTF-16 surrogate
+ godot_string_destroy(&s);
+
+ s = godot_string_chr(0x20d812);
+ CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Outside UTF-32 range
+ godot_string_destroy(&s);
+ ERR_PRINT_ON
+}
+
+TEST_CASE("[GDNative String] Operator []") {
+ godot_string s;
+
+ godot_string_new_with_latin1_chars(&s, "Hello");
+ CHECK(*godot_string_operator_index(&s, 1) == 'e');
+ CHECK(godot_string_operator_index_const(&s, 0) == 'H');
+ CHECK(godot_string_ord_at(&s, 0) == 'H');
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Case function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "MoMoNgA");
+
+ t = godot_string_to_upper(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"MOMONGA") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_to_lower(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"momonga") == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Case compare function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "MoMoNgA");
+ godot_string_new_with_latin1_chars(&t, "momonga");
+
+ CHECK(godot_string_casecmp_to(&s, &t) != 0);
+ CHECK(godot_string_nocasecmp_to(&s, &t) == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] Natural compare function test") {
+ godot_string s, t;
+
+ godot_string_new_with_latin1_chars(&s, "img2.png");
+ godot_string_new_with_latin1_chars(&t, "img10.png");
+
+ CHECK(godot_string_nocasecmp_to(&s, &t) > 0);
+ CHECK(godot_string_naturalnocasecmp_to(&s, &t) < 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] hex_encode_buffer") {
+ static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 };
+ godot_string s = godot_string_hex_encode_buffer(u8str, 6);
+ CHECK(u32scmp(godot_string_get_data(&s), U"45e3818a8fe3") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Substr") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Killer Baby");
+ t = godot_string_substr(&s, 3, 4);
+ CHECK(u32scmp(godot_string_get_data(&t), U"ler ") == 0);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Pretty Woman Woman");
+
+ godot_string_new_with_latin1_chars(&t, "Revenge of the Monster Truck");
+ CHECK(godot_string_find(&s, &t) == -1);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "tty");
+ CHECK(godot_string_find(&s, &t) == 3);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Wo");
+ CHECK(godot_string_find_from(&s, &t, 9) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "man");
+ CHECK(godot_string_rfind(&s, &t) == 15);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find no case") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Pretty Whale Whale");
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_findn(&s, &t) == 7);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_findn_from(&s, &t, 9) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "WHA");
+ CHECK(godot_string_rfindn(&s, &t) == 13);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Revenge of the Monster SawFish");
+ CHECK(godot_string_findn(&s, &t) == -1);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Find MK") {
+ godot_packed_string_array keys;
+ godot_packed_string_array_new(&keys);
+
+#define PUSH_KEY(x) \
+ { \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ godot_packed_string_array_push_back(&keys, &t); \
+ godot_string_destroy(&t); \
+ }
+
+ PUSH_KEY("sty")
+ PUSH_KEY("tty")
+ PUSH_KEY("man")
+
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, "Pretty Woman");
+ godot_int key = 0;
+
+ CHECK(godot_string_findmk(&s, &keys) == 3);
+ CHECK(godot_string_findmk_from_in_place(&s, &keys, 0, &key) == 3);
+ CHECK(key == 1);
+
+ CHECK(godot_string_findmk_from(&s, &keys, 5) == 9);
+ CHECK(godot_string_findmk_from_in_place(&s, &keys, 5, &key) == 9);
+ CHECK(key == 2);
+
+ godot_string_destroy(&s);
+ godot_packed_string_array_destroy(&keys);
+
+#undef PUSH_KEY
+}
+
+TEST_CASE("[GDNative String] Find and replace") {
+ godot_string s, c, w;
+ godot_string_new_with_latin1_chars(&s, "Happy Birthday, Anna!");
+ godot_string_new_with_latin1_chars(&c, "Birthday");
+ godot_string_new_with_latin1_chars(&w, "Halloween");
+ godot_string t = godot_string_replace(&s, &c, &w);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Happy Halloween, Anna!") == 0);
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+ godot_string_destroy(&w);
+
+ godot_string_new_with_latin1_chars(&c, "H");
+ godot_string_new_with_latin1_chars(&w, "W");
+ s = godot_string_replace_first(&t, &c, &w);
+ godot_string_destroy(&t);
+ godot_string_destroy(&c);
+ godot_string_destroy(&w);
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"Wappy Halloween, Anna!") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Insertion") {
+ godot_string s, t, r, u;
+ godot_string_new_with_latin1_chars(&s, "Who is Frederic?");
+ godot_string_new_with_latin1_chars(&t, "?");
+ godot_string_new_with_latin1_chars(&r, " Chopin");
+
+ u = godot_string_insert(&s, godot_string_find(&s, &t), &r);
+ CHECK(u32scmp(godot_string_get_data(&u), U"Who is Frederic Chopin?") == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+ godot_string_destroy(&r);
+ godot_string_destroy(&u);
+}
+
+TEST_CASE("[GDNative String] Number to string") {
+ godot_string s;
+ s = godot_string_num(3.141593);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_with_decimals(3.141593, 3);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.142") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_real(3.141593);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_scientific(30000000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3e+07") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64(3141593, 10);
+ CHECK(u32scmp(godot_string_get_data(&s), U"3141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64(0xA141593, 16);
+ CHECK(u32scmp(godot_string_get_data(&s), U"a141593") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_num_int64_capitalized(0xA141593, 16, true);
+ CHECK(u32scmp(godot_string_get_data(&s), U"A141593") == 0);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] String to integer") {
+ static const wchar_t *wnums[4] = { L"1237461283", L"- 22", L"0", L" - 1123412" };
+ static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
+ static const int num[4] = { 1237461283, -22, 0, -1123412 };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK(godot_string_to_int(&s) == num[i]);
+ godot_string_destroy(&s);
+
+ CHECK(godot_string_char_to_int(nums[i]) == num[i]);
+ CHECK(godot_string_wchar_to_int(wnums[i]) == num[i]);
+ }
+}
+
+TEST_CASE("[GDNative String] Hex to integer") {
+ static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" };
+ static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD };
+ static const bool wo_prefix[4] = { false, true, true, true };
+ static const bool w_prefix[4] = { true, false, true, false };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK((godot_string_hex_to_int_with_prefix(&s) == num[i]) == w_prefix[i]);
+ CHECK((godot_string_hex_to_int(&s) == num[i]) == wo_prefix[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] String to float") {
+ static const wchar_t *wnums[4] = { L"-12348298412.2", L"0.05", L"2.0002", L" -0.0001" };
+ static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
+ static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, nums[i]);
+ CHECK(!(ABS(godot_string_to_float(&s) - num[i]) > 0.00001));
+ godot_string_destroy(&s);
+
+ CHECK(!(ABS(godot_string_char_to_float(nums[i]) - num[i]) > 0.00001));
+ CHECK(!(ABS(godot_string_wchar_to_float(wnums[i], nullptr) - num[i]) > 0.00001));
+ }
+}
+
+TEST_CASE("[GDNative String] CamelCase to underscore") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "TestTestStringGD");
+
+ t = godot_string_camelcase_to_underscore(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"Test_Test_String_GD") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_camelcase_to_underscore_lowercased(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), U"test_test_string_gd") == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Slicing") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
+ godot_string_new_with_latin1_chars(&c, ",");
+
+ const char32_t *slices[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
+ for (int i = 0; i < godot_string_get_slice_count(&s, &c); i++) {
+ godot_string t;
+ t = godot_string_get_slice(&s, &c, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_slicec(&s, U',', i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
+ godot_string_destroy(&t);
+ }
+
+ godot_string_destroy(&c);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Splitting") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
+ godot_string_new_with_latin1_chars(&c, ",");
+
+ godot_packed_string_array l;
+
+ const char32_t *slices_l[3] = { U"Mars", U"Jupiter", U"Saturn,Uranus" };
+ const char32_t *slices_r[3] = { U"Mars,Jupiter", U"Saturn", U"Uranus" };
+
+ l = godot_string_split_with_maxsplit(&s, &c, true, 2);
+ CHECK(godot_packed_string_array_size(&l) == 3);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_l[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+
+ l = godot_string_rsplit_with_maxsplit(&s, &c, true, 2);
+ CHECK(godot_packed_string_array_size(&l) == 3);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_r[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+ godot_string_destroy(&s);
+
+ godot_string_new_with_latin1_chars(&s, "Mars Jupiter Saturn Uranus");
+ const char32_t *slices_s[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
+ l = godot_string_split_spaces(&s);
+ for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
+ godot_string t = godot_packed_string_array_get(&l, i);
+ CHECK(u32scmp(godot_string_get_data(&t), slices_s[i]) == 0);
+ godot_string_destroy(&t);
+ }
+ godot_packed_string_array_destroy(&l);
+ godot_string_destroy(&s);
+
+ godot_string c1, c2;
+ godot_string_new_with_latin1_chars(&c1, ";");
+ godot_string_new_with_latin1_chars(&c2, " ");
+
+ godot_string_new_with_latin1_chars(&s, "1.2;2.3 4.5");
+ const double slices_d[3] = { 1.2, 2.3, 4.5 };
+
+ godot_packed_float32_array lf = godot_string_split_floats_allow_empty(&s, &c1);
+ CHECK(godot_packed_float32_array_size(&lf) == 2);
+ for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
+ CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
+ }
+ godot_packed_float32_array_destroy(&lf);
+
+ godot_packed_string_array keys;
+ godot_packed_string_array_new(&keys);
+ godot_packed_string_array_push_back(&keys, &c1);
+ godot_packed_string_array_push_back(&keys, &c2);
+
+ lf = godot_string_split_floats_mk_allow_empty(&s, &keys);
+ CHECK(godot_packed_float32_array_size(&lf) == 3);
+ for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
+ CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
+ }
+ godot_packed_float32_array_destroy(&lf);
+
+ godot_string_destroy(&s);
+ godot_string_new_with_latin1_chars(&s, "1;2 4");
+ const int slices_i[3] = { 1, 2, 4 };
+
+ godot_packed_int32_array li = godot_string_split_ints_allow_empty(&s, &c1);
+ CHECK(godot_packed_int32_array_size(&li) == 2);
+ for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
+ CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
+ }
+ godot_packed_int32_array_destroy(&li);
+
+ li = godot_string_split_ints_mk_allow_empty(&s, &keys);
+ CHECK(godot_packed_int32_array_size(&li) == 3);
+ for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
+ CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
+ }
+ godot_packed_int32_array_destroy(&li);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+ godot_string_destroy(&c1);
+ godot_string_destroy(&c2);
+ godot_packed_string_array_destroy(&keys);
+}
+
+TEST_CASE("[GDNative String] Erasing") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "Josephine is such a cute girl!");
+ godot_string_new_with_latin1_chars(&t, "cute ");
+
+ godot_string_erase(&s, godot_string_find(&s, &t), godot_string_length(&t));
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"Josephine is such a girl!") == 0);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+struct test_27_data {
+ char const *data;
+ char const *part;
+ bool expected;
+};
+
+TEST_CASE("[GDNative String] Begins with") {
+ test_27_data tc[] = {
+ { "res://foobar", "res://", true },
+ { "res", "res://", false },
+ { "abc", "abc", true }
+ };
+ size_t count = sizeof(tc) / sizeof(tc[0]);
+ bool state = true;
+ for (size_t i = 0; state && i < count; ++i) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, tc[i].data);
+
+ state = godot_string_begins_with_char_array(&s, tc[i].part) == tc[i].expected;
+ if (state) {
+ godot_string t;
+ godot_string_new_with_latin1_chars(&t, tc[i].part);
+ state = godot_string_begins_with(&s, &t) == tc[i].expected;
+ godot_string_destroy(&t);
+ }
+ godot_string_destroy(&s);
+
+ CHECK(state);
+ if (!state) {
+ break;
+ }
+ };
+ CHECK(state);
+}
+
+TEST_CASE("[GDNative String] Ends with") {
+ test_27_data tc[] = {
+ { "res://foobar", "foobar", true },
+ { "res", "res://", false },
+ { "abc", "abc", true }
+ };
+ size_t count = sizeof(tc) / sizeof(tc[0]);
+ bool state = true;
+ for (size_t i = 0; state && i < count; ++i) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, tc[i].data);
+
+ state = godot_string_ends_with_char_array(&s, tc[i].part) == tc[i].expected;
+ if (state) {
+ godot_string t;
+ godot_string_new_with_latin1_chars(&t, tc[i].part);
+ state = godot_string_ends_with(&s, &t) == tc[i].expected;
+ godot_string_destroy(&t);
+ }
+ godot_string_destroy(&s);
+
+ CHECK(state);
+ if (!state) {
+ break;
+ }
+ };
+ CHECK(state);
+}
+
+TEST_CASE("[GDNative String] format") {
+ godot_string value_format, t;
+ godot_string_new_with_latin1_chars(&value_format, "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"");
+
+ godot_variant key_v, val_v;
+ godot_dictionary value_dictionary;
+ godot_dictionary_new(&value_dictionary);
+
+ godot_string_new_with_latin1_chars(&t, "red");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_int(&val_v, 10);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "green");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_int(&val_v, 20);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "blue");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "bla");
+ godot_variant_new_string(&val_v, &t);
+ godot_string_destroy(&t);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_string_new_with_latin1_chars(&t, "alpha");
+ godot_variant_new_string(&key_v, &t);
+ godot_string_destroy(&t);
+ godot_variant_new_real(&val_v, 0.4);
+ godot_dictionary_set(&value_dictionary, &key_v, &val_v);
+ godot_variant_destroy(&key_v);
+ godot_variant_destroy(&val_v);
+
+ godot_variant dict_v;
+ godot_variant_new_dictionary(&dict_v, &value_dictionary);
+ godot_string s = godot_string_format_with_custom_placeholder(&value_format, &dict_v, "$_");
+
+ CHECK(u32scmp(godot_string_get_data(&s), U"red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"") == 0);
+
+ godot_dictionary_destroy(&value_dictionary);
+ godot_string_destroy(&s);
+ godot_variant_destroy(&dict_v);
+ godot_string_destroy(&value_format);
+}
+
+TEST_CASE("[GDNative String] sprintf") {
+ //godot_string GDAPI (const godot_string *p_self, const godot_array *p_values, godot_bool *p_error);
+ godot_string format, output;
+ godot_array args;
+ bool error;
+
+#define ARRAY_PUSH_STRING(x) \
+ { \
+ godot_variant v; \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ godot_variant_new_string(&v, &t); \
+ godot_string_destroy(&t); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+#define ARRAY_PUSH_INT(x) \
+ { \
+ godot_variant v; \
+ godot_variant_new_int(&v, x); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+#define ARRAY_PUSH_REAL(x) \
+ { \
+ godot_variant v; \
+ godot_variant_new_real(&v, x); \
+ godot_array_push_back(&args, &v); \
+ godot_variant_destroy(&v); \
+ }
+
+ godot_array_new(&args);
+
+ // %%
+ godot_string_new_with_latin1_chars(&format, "fish %% frog");
+ godot_array_clear(&args);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish % frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+ //////// INTS
+
+ // Int
+ godot_string_new_with_latin1_chars(&format, "fish %d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int left padded with zeroes.
+ godot_string_new_with_latin1_chars(&format, "fish %05d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 00005 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int left padded with spaces.
+ godot_string_new_with_latin1_chars(&format, "fish %5d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int right padded with spaces.
+ godot_string_new_with_latin1_chars(&format, "fish %-5d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int with sign (positive).
+ godot_string_new_with_latin1_chars(&format, "fish %+d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish +5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Negative int.
+ godot_string_new_with_latin1_chars(&format, "fish %d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(-5);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish -5 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Hex (lower)
+ godot_string_new_with_latin1_chars(&format, "fish %x frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(45);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 2d frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Hex (upper)
+ godot_string_new_with_latin1_chars(&format, "fish %X frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(45);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 2D frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Octal
+ godot_string_new_with_latin1_chars(&format, "fish %o frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 143 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+ ////// REALS
+
+ // Real
+ godot_string_new_with_latin1_chars(&format, "fish %f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real left-padded
+ godot_string_new_with_latin1_chars(&format, "fish %11f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real right-padded
+ godot_string_new_with_latin1_chars(&format, "fish %-11f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real given int.
+ godot_string_new_with_latin1_chars(&format, "fish %f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.000000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with sign (positive).
+ godot_string_new_with_latin1_chars(&format, "fish %+f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish +99.990000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with 1 decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.1f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 100.0 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with 12 decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.12f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000000000 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Real with no decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %.f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 100 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ /////// Strings.
+
+ // String
+ godot_string_new_with_latin1_chars(&format, "fish %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // String left-padded
+ godot_string_new_with_latin1_chars(&format, "fish %10s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // String right-padded
+ godot_string_new_with_latin1_chars(&format, "fish %-10s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Characters
+
+ // Character as string.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("A");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character as int.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(65);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Dynamic width
+
+ // String dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ REQUIRE(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Int dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*d frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_INT(99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ REQUIRE(u32scmp(godot_string_get_data(&output), U"fish 99 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Float dynamic width
+ godot_string_new_with_latin1_chars(&format, "fish %*.*f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_INT(10);
+ ARRAY_PUSH_INT(3);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error == false);
+ CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990 frog") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ ///// Errors
+
+ // More formats than arguments.
+ godot_string_new_with_latin1_chars(&format, "fish %s %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"not enough arguments for format string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // More arguments than formats.
+ godot_string_new_with_latin1_chars(&format, "fish %s frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("hello");
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"not all arguments converted during string formatting") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Incomplete format.
+ godot_string_new_with_latin1_chars(&format, "fish %10");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"incomplete format") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Bad character in format string
+ godot_string_new_with_latin1_chars(&format, "fish %&f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"unsupported format character") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Too many decimals.
+ godot_string_new_with_latin1_chars(&format, "fish %2.2.2f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"too many decimal points in format") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // * not a number
+ godot_string_new_with_latin1_chars(&format, "fish %*f frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("cheese");
+ ARRAY_PUSH_REAL(99.99);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"* wants number") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character too long.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ ARRAY_PUSH_STRING("sc");
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ // Character bad type.
+ godot_string_new_with_latin1_chars(&format, "fish %c frog");
+ godot_array_clear(&args);
+ godot_array t;
+ godot_array_new(&t);
+ godot_variant v;
+ godot_variant_new_array(&v, &t);
+ godot_array_destroy(&t);
+ godot_array_push_back(&args, &v);
+ godot_variant_destroy(&v);
+ output = godot_string_sprintf(&format, &args, &error);
+ REQUIRE(error);
+ CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
+ godot_string_destroy(&format);
+ godot_string_destroy(&output);
+
+ godot_array_destroy(&args);
+#undef ARRAY_PUSH_INT
+#undef ARRAY_PUSH_REAL
+#undef ARRAY_PUSH_STRING
+}
+
+TEST_CASE("[GDNative String] is_numeric") {
+#define IS_NUM_TEST(x, r) \
+ { \
+ godot_string t; \
+ godot_string_new_with_latin1_chars(&t, x); \
+ CHECK(godot_string_is_numeric(&t) == r); \
+ godot_string_destroy(&t); \
+ }
+
+ IS_NUM_TEST("12", true);
+ IS_NUM_TEST("1.2", true);
+ IS_NUM_TEST("AF", false);
+ IS_NUM_TEST("-12", true);
+ IS_NUM_TEST("-1.2", true);
+
+#undef IS_NUM_TEST
+}
+
+TEST_CASE("[GDNative String] pad") {
+ godot_string s, c;
+ godot_string_new_with_latin1_chars(&s, "test");
+ godot_string_new_with_latin1_chars(&c, "x");
+
+ godot_string l = godot_string_lpad_with_custom_character(&s, 10, &c);
+ CHECK(u32scmp(godot_string_get_data(&l), U"xxxxxxtest") == 0);
+ godot_string_destroy(&l);
+
+ godot_string r = godot_string_rpad_with_custom_character(&s, 10, &c);
+ CHECK(u32scmp(godot_string_get_data(&r), U"testxxxxxx") == 0);
+ godot_string_destroy(&r);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&c);
+
+ godot_string_new_with_latin1_chars(&s, "10.10");
+ c = godot_string_pad_decimals(&s, 4);
+ CHECK(u32scmp(godot_string_get_data(&c), U"10.1000") == 0);
+ godot_string_destroy(&c);
+ c = godot_string_pad_zeros(&s, 4);
+ CHECK(u32scmp(godot_string_get_data(&c), U"0010.10") == 0);
+ godot_string_destroy(&c);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] is_subsequence_of") {
+ godot_string a, t;
+ godot_string_new_with_latin1_chars(&a, "is subsequence of");
+
+ godot_string_new_with_latin1_chars(&t, "sub");
+ CHECK(godot_string_is_subsequence_of(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Sub");
+ CHECK(!godot_string_is_subsequence_of(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Sub");
+ CHECK(godot_string_is_subsequence_ofi(&t, &a));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&a);
+}
+
+TEST_CASE("[GDNative String] match") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "*.png");
+
+ godot_string_new_with_latin1_chars(&t, "img1.png");
+ CHECK(godot_string_match(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "img1.jpeg");
+ CHECK(!godot_string_match(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "img1.Png");
+ CHECK(!godot_string_match(&t, &s));
+ CHECK(godot_string_matchn(&t, &s));
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] IPVX address to string") {
+ godot_string ip;
+
+ godot_string_new_with_latin1_chars(&ip, "192.168.0.1");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "192.368.0.1");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85j3:0000:0000:8a2e:0370:7334");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8:85f345:0000:0000:8a2e:0370:7334");
+ CHECK(!godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "2001:0db8::0:8a2e:370:7334");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+
+ godot_string_new_with_latin1_chars(&ip, "::ffff:192.168.0.1");
+ CHECK(godot_string_is_valid_ip_address(&ip));
+ godot_string_destroy(&ip);
+}
+
+TEST_CASE("[GDNative String] Capitalize against many strings") {
+#define CAP_TEST(i, o) \
+ godot_string_new_with_latin1_chars(&input, i); \
+ godot_string_new_with_latin1_chars(&output, o); \
+ test = godot_string_capitalize(&input); \
+ CHECK(u32scmp(godot_string_get_data(&output), godot_string_get_data(&test)) == 0); \
+ godot_string_destroy(&input); \
+ godot_string_destroy(&output); \
+ godot_string_destroy(&test);
+
+ godot_string input, output, test;
+
+ CAP_TEST("bytes2var", "Bytes 2 Var");
+ CAP_TEST("linear2db", "Linear 2 Db");
+ CAP_TEST("vector3", "Vector 3");
+ CAP_TEST("sha256", "Sha 256");
+ CAP_TEST("2db", "2 Db");
+ CAP_TEST("PascalCase", "Pascal Case");
+ CAP_TEST("PascalPascalCase", "Pascal Pascal Case");
+ CAP_TEST("snake_case", "Snake Case");
+ CAP_TEST("snake_snake_case", "Snake Snake Case");
+ CAP_TEST("sha256sum", "Sha 256 Sum");
+ CAP_TEST("cat2dog", "Cat 2 Dog");
+ CAP_TEST("function(name)", "Function(name)");
+ CAP_TEST("snake_case_function(snake_case_arg)", "Snake Case Function(snake Case Arg)");
+ CAP_TEST("snake_case_function( snake_case_arg )", "Snake Case Function( Snake Case Arg )");
+
+#undef CAP_TEST
+}
+
+TEST_CASE("[GDNative String] lstrip and rstrip") {
+#define LSTRIP_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_latin1_chars(&xx, x); \
+ godot_string_new_with_latin1_chars(&yy, y); \
+ godot_string_new_with_latin1_chars(&zz, z); \
+ rr = godot_string_lstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define RSTRIP_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_latin1_chars(&xx, x); \
+ godot_string_new_with_latin1_chars(&yy, y); \
+ godot_string_new_with_latin1_chars(&zz, z); \
+ rr = godot_string_rstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define LSTRIP_UTF8_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_utf8_chars(&xx, x); \
+ godot_string_new_with_utf8_chars(&yy, y); \
+ godot_string_new_with_utf8_chars(&zz, z); \
+ rr = godot_string_lstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+#define RSTRIP_UTF8_TEST(x, y, z) \
+ { \
+ godot_string xx, yy, zz, rr; \
+ godot_string_new_with_utf8_chars(&xx, x); \
+ godot_string_new_with_utf8_chars(&yy, y); \
+ godot_string_new_with_utf8_chars(&zz, z); \
+ rr = godot_string_rstrip(&xx, &yy); \
+ state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
+ godot_string_destroy(&xx); \
+ godot_string_destroy(&yy); \
+ godot_string_destroy(&zz); \
+ godot_string_destroy(&rr); \
+ }
+
+ bool state = true;
+
+ // strip none
+ LSTRIP_TEST("abc", "", "abc");
+ RSTRIP_TEST("abc", "", "abc");
+ // strip one
+ LSTRIP_TEST("abc", "a", "bc");
+ RSTRIP_TEST("abc", "c", "ab");
+ // strip lots
+ LSTRIP_TEST("bababbababccc", "ab", "ccc");
+ RSTRIP_TEST("aaabcbcbcbbcbbc", "cb", "aaa");
+ // strip empty string
+ LSTRIP_TEST("", "", "");
+ RSTRIP_TEST("", "", "");
+ // strip to empty string
+ LSTRIP_TEST("abcabcabc", "bca", "");
+ RSTRIP_TEST("abcabcabc", "bca", "");
+ // don't strip wrong end
+ LSTRIP_TEST("abc", "c", "abc");
+ LSTRIP_TEST("abca", "a", "bca");
+ RSTRIP_TEST("abc", "a", "abc");
+ RSTRIP_TEST("abca", "a", "abc");
+ // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
+ // and the same second as "ÿ" (\u00ff)
+ LSTRIP_UTF8_TEST("¿", "µÿ", "¿");
+ RSTRIP_UTF8_TEST("¿", "µÿ", "¿");
+ LSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "¿ÿ");
+ RSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "µ¿");
+
+ // the above tests repeated with additional superfluous strip chars
+
+ // strip none
+ LSTRIP_TEST("abc", "qwjkl", "abc");
+ RSTRIP_TEST("abc", "qwjkl", "abc");
+ // strip one
+ LSTRIP_TEST("abc", "qwajkl", "bc");
+ RSTRIP_TEST("abc", "qwcjkl", "ab");
+ // strip lots
+ LSTRIP_TEST("bababbababccc", "qwabjkl", "ccc");
+ RSTRIP_TEST("aaabcbcbcbbcbbc", "qwcbjkl", "aaa");
+ // strip empty string
+ LSTRIP_TEST("", "qwjkl", "");
+ RSTRIP_TEST("", "qwjkl", "");
+ // strip to empty string
+ LSTRIP_TEST("abcabcabc", "qwbcajkl", "");
+ RSTRIP_TEST("abcabcabc", "qwbcajkl", "");
+ // don't strip wrong end
+ LSTRIP_TEST("abc", "qwcjkl", "abc");
+ LSTRIP_TEST("abca", "qwajkl", "bca");
+ RSTRIP_TEST("abc", "qwajkl", "abc");
+ RSTRIP_TEST("abca", "qwajkl", "abc");
+ // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
+ // and the same second as "ÿ" (\u00ff)
+ LSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
+ RSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
+ LSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "¿ÿ");
+ RSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "µ¿");
+
+ CHECK(state);
+
+#undef LSTRIP_TEST
+#undef RSTRIP_TEST
+#undef LSTRIP_UTF8_TEST
+#undef RSTRIP_UTF8_TEST
+}
+
+TEST_CASE("[GDNative String] Cyrillic to_lower()") {
+ godot_string upper, lower, test;
+ godot_string_new_with_utf8_chars(&upper, "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ");
+ godot_string_new_with_utf8_chars(&lower, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
+
+ test = godot_string_to_lower(&upper);
+
+ CHECK((u32scmp(godot_string_get_data(&test), godot_string_get_data(&lower)) == 0));
+
+ godot_string_destroy(&upper);
+ godot_string_destroy(&lower);
+ godot_string_destroy(&test);
+}
+
+TEST_CASE("[GDNative String] Count and countn functionality") {
+#define COUNT_TEST(x, y, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_count(&s, &t, 0, 0) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTR_TEST(x, y, a, b, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_count(&s, &t, a, b) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTN_TEST(x, y, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_countn(&s, &t, 0, 0) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+
+#define COUNTNR_TEST(x, y, a, b, r) \
+ { \
+ godot_string s, t; \
+ godot_string_new_with_latin1_chars(&s, x); \
+ godot_string_new_with_latin1_chars(&t, y); \
+ state = state && (godot_string_countn(&s, &t, a, b) == r); \
+ godot_string_destroy(&s); \
+ godot_string_destroy(&t); \
+ }
+ bool state = true;
+
+ COUNT_TEST("", "Test", 0);
+ COUNT_TEST("Test", "", 0);
+ COUNT_TEST("Test", "test", 0);
+ COUNT_TEST("Test", "TEST", 0);
+ COUNT_TEST("TEST", "TEST", 1);
+ COUNT_TEST("Test", "Test", 1);
+ COUNT_TEST("aTest", "Test", 1);
+ COUNT_TEST("Testa", "Test", 1);
+ COUNT_TEST("TestTestTest", "Test", 3);
+ COUNT_TEST("TestTestTest", "TestTest", 1);
+ COUNT_TEST("TestGodotTestGodotTestGodot", "Test", 3);
+
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 8, 1);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 12, 2);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 16, 3);
+ COUNTR_TEST("TestTestTestTest", "Test", 4, 0, 3);
+
+ COUNTN_TEST("Test", "test", 1);
+ COUNTN_TEST("Test", "TEST", 1);
+ COUNTN_TEST("testTest-Testatest", "tEst", 4);
+ COUNTNR_TEST("testTest-TeStatest", "tEsT", 4, 16, 2);
+
+ CHECK(state);
+
+#undef COUNT_TEST
+#undef COUNTR_TEST
+#undef COUNTN_TEST
+#undef COUNTNR_TEST
+}
+
+TEST_CASE("[GDNative String] Bigrams") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, "abcd");
+ godot_packed_string_array bigr = godot_string_bigrams(&s);
+ godot_string_destroy(&s);
+
+ CHECK(godot_packed_string_array_size(&bigr) == 3);
+
+ t = godot_packed_string_array_get(&bigr, 0);
+ CHECK(u32scmp(godot_string_get_data(&t), U"ab") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_packed_string_array_get(&bigr, 1);
+ CHECK(u32scmp(godot_string_get_data(&t), U"bc") == 0);
+ godot_string_destroy(&t);
+
+ t = godot_packed_string_array_get(&bigr, 2);
+ CHECK(u32scmp(godot_string_get_data(&t), U"cd") == 0);
+ godot_string_destroy(&t);
+
+ godot_packed_string_array_destroy(&bigr);
+}
+
+TEST_CASE("[GDNative String] c-escape/unescape") {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\"");
+ godot_string t = godot_string_c_escape(&s);
+ godot_string u = godot_string_c_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] dedent") {
+ godot_string s, t;
+ godot_string_new_with_latin1_chars(&s, " aaa\n bbb");
+ godot_string_new_with_latin1_chars(&t, "aaa\nbbb");
+ godot_string u = godot_string_dedent(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Path functions") {
+ static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" };
+ static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" };
+ static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" };
+ static const char *ext[4] = { "tscn", "xscn", "scn", "doc" };
+ static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" };
+ static const bool abs[4] = { true, true, false, false };
+
+ for (int i = 0; i < 4; i++) {
+ godot_string s, t, u, f;
+ godot_string_new_with_latin1_chars(&s, path[i]);
+
+ t = godot_string_get_base_dir(&s);
+ godot_string_new_with_latin1_chars(&u, base_dir[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_basename(&s);
+ godot_string_new_with_latin1_chars(&u, base_name[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_extension(&s);
+ godot_string_new_with_latin1_chars(&u, ext[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_get_file(&s);
+ godot_string_new_with_latin1_chars(&u, file[i]);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string s_simp;
+ s_simp = godot_string_simplify_path(&s);
+ t = godot_string_get_base_dir(&s_simp);
+ godot_string_new_with_latin1_chars(&u, file[i]);
+ f = godot_string_plus_file(&t, &u);
+ CHECK(u32scmp(godot_string_get_data(&f), godot_string_get_data(&s_simp)) == 0);
+ godot_string_destroy(&f);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&s_simp);
+
+ CHECK(godot_string_is_abs_path(&s) == abs[i]);
+ CHECK(godot_string_is_rel_path(&s) != abs[i]);
+
+ godot_string_destroy(&s);
+ }
+
+ static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" };
+ static const bool valid[3] = { true, false, false };
+ for (int i = 0; i < 3; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, file_name[i]);
+ CHECK(godot_string_is_valid_filename(&s) == valid[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] hash") {
+ godot_string a, b, c;
+ godot_string_new_with_latin1_chars(&a, "Test");
+ godot_string_new_with_latin1_chars(&b, "Test");
+ godot_string_new_with_latin1_chars(&c, "West");
+ CHECK(godot_string_hash(&a) == godot_string_hash(&b));
+ CHECK(godot_string_hash(&a) != godot_string_hash(&c));
+
+ CHECK(godot_string_hash64(&a) == godot_string_hash64(&b));
+ CHECK(godot_string_hash64(&a) != godot_string_hash64(&c));
+
+ godot_string_destroy(&a);
+ godot_string_destroy(&b);
+ godot_string_destroy(&c);
+}
+
+TEST_CASE("[GDNative String] http_escape/unescape") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
+ godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3A%27docs%27");
+
+ u = godot_string_http_escape(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ u = godot_string_http_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] percent_encode/decode") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
+ godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3a%27docs%27");
+
+ u = godot_string_percent_encode(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ u = godot_string_percent_decode(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] xml_escape/unescape") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\"Test\" <test@test&'test'>");
+
+ t = godot_string_xml_escape_with_quotes(&s);
+ u = godot_string_xml_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ t = godot_string_xml_escape(&s);
+ u = godot_string_xml_unescape(&t);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Strip escapes") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\t\tTest Test\r\n Test");
+ godot_string_new_with_latin1_chars(&t, "Test Test Test");
+
+ u = godot_string_strip_escapes(&s);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&t);
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Strip edges") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "\t Test Test ");
+
+ godot_string_new_with_latin1_chars(&t, "Test Test ");
+ u = godot_string_strip_edges(&s, true, false);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "\t Test Test");
+ u = godot_string_strip_edges(&s, false, true);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "Test Test");
+ u = godot_string_strip_edges(&s, true, true);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Similarity") {
+ godot_string a, b, c;
+ godot_string_new_with_latin1_chars(&a, "Test");
+ godot_string_new_with_latin1_chars(&b, "West");
+ godot_string_new_with_latin1_chars(&c, "Toad");
+
+ CHECK(godot_string_similarity(&a, &b) > godot_string_similarity(&a, &c));
+
+ godot_string_destroy(&a);
+ godot_string_destroy(&b);
+ godot_string_destroy(&c);
+}
+
+TEST_CASE("[GDNative String] Trim") {
+ godot_string s, t, u, p;
+ godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
+
+ godot_string_new_with_latin1_chars(&p, "aaa");
+ godot_string_new_with_latin1_chars(&t, "Testbbb");
+ u = godot_string_trim_prefix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&p);
+
+ godot_string_new_with_latin1_chars(&p, "bbb");
+ godot_string_new_with_latin1_chars(&t, "aaaTest");
+ u = godot_string_trim_suffix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+ godot_string_destroy(&p);
+
+ godot_string_new_with_latin1_chars(&p, "Test");
+ u = godot_string_trim_suffix(&s, &p);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&p);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Right/Left") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
+ // ^
+
+ godot_string_new_with_latin1_chars(&t, "tbbb");
+ u = godot_string_right(&s, 6);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&t, "aaaTes");
+ u = godot_string_left(&s, 6);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+}
+
+TEST_CASE("[GDNative String] Repeat") {
+ godot_string t, u;
+ godot_string_new_with_latin1_chars(&t, "ab");
+
+ u = godot_string_repeat(&t, 4);
+ CHECK(u32scmp(godot_string_get_data(&u), U"abababab") == 0);
+ godot_string_destroy(&u);
+
+ godot_string_destroy(&t);
+}
+
+TEST_CASE("[GDNative String] SHA1/SHA256/MD5") {
+ godot_string s, t, sha1, sha256, md5;
+ godot_string_new_with_latin1_chars(&s, "Godot");
+ godot_string_new_with_latin1_chars(&sha1, "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201");
+ static uint8_t sha1_buf[20] = {
+ 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3,
+ 0x9D, 0xC2, 0xD2, 0x01
+ };
+ godot_string_new_with_latin1_chars(&sha256, "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8");
+ static uint8_t sha256_buf[32] = {
+ 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF,
+ 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8
+ };
+ godot_string_new_with_latin1_chars(&md5, "4a336d087aeb0390da10ee2ea7cb87f8");
+ static uint8_t md5_buf[16] = {
+ 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8
+ };
+
+ godot_packed_byte_array buf = godot_string_sha1_buffer(&s);
+ CHECK(memcmp(sha1_buf, godot_packed_byte_array_ptr(&buf), 20) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_sha1_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha1)) == 0);
+ godot_string_destroy(&t);
+
+ buf = godot_string_sha256_buffer(&s);
+ CHECK(memcmp(sha256_buf, godot_packed_byte_array_ptr(&buf), 32) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_sha256_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha256)) == 0);
+ godot_string_destroy(&t);
+
+ buf = godot_string_md5_buffer(&s);
+ CHECK(memcmp(md5_buf, godot_packed_byte_array_ptr(&buf), 16) == 0);
+ godot_packed_byte_array_destroy(&buf);
+
+ t = godot_string_md5_text(&s);
+ CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&md5)) == 0);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+ godot_string_destroy(&sha1);
+ godot_string_destroy(&sha256);
+ godot_string_destroy(&md5);
+}
+
+TEST_CASE("[GDNative String] Join") {
+ godot_string s, t, u;
+ godot_string_new_with_latin1_chars(&s, ", ");
+
+ godot_packed_string_array parts;
+ godot_packed_string_array_new(&parts);
+ godot_string_new_with_latin1_chars(&t, "One");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "B");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+ godot_string_new_with_latin1_chars(&t, "C");
+ godot_packed_string_array_push_back(&parts, &t);
+ godot_string_destroy(&t);
+
+ godot_string_new_with_latin1_chars(&u, "One, B, C");
+ t = godot_string_join(&s, &parts);
+ CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
+ godot_string_destroy(&u);
+ godot_string_destroy(&t);
+
+ godot_string_destroy(&s);
+ godot_packed_string_array_destroy(&parts);
+}
+
+TEST_CASE("[GDNative String] Is_*") {
+ static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" };
+ static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false };
+ static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false };
+ static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false };
+ static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false };
+ static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false };
+ static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false };
+
+ for (int i = 0; i < 12; i++) {
+ godot_string s;
+ godot_string_new_with_latin1_chars(&s, data[i]);
+ CHECK(godot_string_is_numeric(&s) == isnum[i]);
+ CHECK(godot_string_is_valid_integer(&s) == isint[i]);
+ CHECK(godot_string_is_valid_hex_number(&s, false) == ishex[i]);
+ CHECK(godot_string_is_valid_hex_number(&s, true) == ishex_p[i]);
+ CHECK(godot_string_is_valid_float(&s) == isflt[i]);
+ CHECK(godot_string_is_valid_identifier(&s) == isid[i]);
+ godot_string_destroy(&s);
+ }
+}
+
+TEST_CASE("[GDNative String] humanize_size") {
+ godot_string s;
+
+ s = godot_string_humanize_size(1000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1000 B") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(1025);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1.00 KiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(1025300);
+ CHECK(u32scmp(godot_string_get_data(&s), U"1001.2 KiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(100523550);
+ CHECK(u32scmp(godot_string_get_data(&s), U"95.86 MiB") == 0);
+ godot_string_destroy(&s);
+
+ s = godot_string_humanize_size(5345555000);
+ CHECK(u32scmp(godot_string_get_data(&s), U"4.97 GiB") == 0);
+ godot_string_destroy(&s);
+}
+
+} // namespace TestGDNativeString
+
+#endif // TEST_GDNATIVE_STRING_H
diff --git a/modules/gdnative/videodecoder/register_types.cpp b/modules/gdnative/videodecoder/register_types.cpp
index 4181d8813f..8ee1c8d183 100644
--- a/modules/gdnative/videodecoder/register_types.cpp
+++ b/modules/gdnative/videodecoder/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "video_stream_gdnative.h"
static Ref<ResourceFormatLoaderVideoStreamGDNative> resource_loader_vsgdnative;
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index fe7c10cad9..61e882f2fe 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -30,7 +30,7 @@
#include "video_stream_gdnative.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "servers/audio_server.h"
VideoDecoderServer *VideoDecoderServer::instance = nullptr;
diff --git a/modules/gdnavigation/gd_navigation_server.h b/modules/gdnavigation/gd_navigation_server.h
index e3e02f3d7c..c00d60ced7 100644
--- a/modules/gdnavigation/gd_navigation_server.h
+++ b/modules/gdnavigation/gd_navigation_server.h
@@ -31,8 +31,8 @@
#ifndef GD_NAVIGATION_SERVER_H
#define GD_NAVIGATION_SERVER_H
-#include "core/rid.h"
-#include "core/rid_owner.h"
+#include "core/templates/rid.h"
+#include "core/templates/rid_owner.h"
#include "servers/navigation_server_3d.h"
#include "nav_map.h"
diff --git a/modules/gdnavigation/nav_rid.h b/modules/gdnavigation/nav_rid.h
index c119ecc5e0..b727fceb04 100644
--- a/modules/gdnavigation/nav_rid.h
+++ b/modules/gdnavigation/nav_rid.h
@@ -31,7 +31,7 @@
#ifndef NAV_RID_H
#define NAV_RID_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
/**
@author AndreaCatania
diff --git a/modules/gdnavigation/register_types.cpp b/modules/gdnavigation/register_types.cpp
index 088b26bf17..1ae19ebe47 100644
--- a/modules/gdnavigation/register_types.cpp
+++ b/modules/gdnavigation/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "gd_navigation_server.h"
#include "servers/navigation_server_3d.h"
diff --git a/modules/gdnavigation/rvo_agent.h b/modules/gdnavigation/rvo_agent.h
index f5c579ba84..de36508edb 100644
--- a/modules/gdnavigation/rvo_agent.h
+++ b/modules/gdnavigation/rvo_agent.h
@@ -31,8 +31,9 @@
#ifndef RVO_AGENT_H
#define RVO_AGENT_H
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "nav_rid.h"
+
#include <Agent.h>
/**
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index e58a1d8edc..5c8cbdf869 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -17,3 +17,7 @@ if env["tools"]:
# Using a define in the disabled case, to avoid having an extra define
# in regular builds where all modules are enabled.
env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"])
+
+if env["tests"]:
+ env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"])
+ env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp")
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 9e40a69712..d90b3e52d0 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -7,6 +7,7 @@
List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.)
</description>
<tutorials>
+ <link title="Random number generation">https://docs.godotengine.org/en/latest/tutorials/math/random_number_generation.html</link>
</tutorials>
<methods>
<method name="Color8">
@@ -93,14 +94,15 @@
<argument index="1" name="message" type="String" default="&quot;&quot;">
</argument>
<description>
- Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development.
+ Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method push_error] for reporting errors to project developers or add-on users.
+ [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
[codeblock]
- # Imagine we always want speed to be between 0 and 20
- speed = -10
+ # Imagine we always want speed to be between 0 and 20.
+ var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
- assert(speed &gt;= 0 &amp;&amp; speed &lt; 20) # You can also combine the two conditional statements in one check
+ assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
[/codeblock]
</description>
@@ -167,6 +169,7 @@
i = ceil(1.45) # i is 2
i = ceil(1.001) # i is 2
[/codeblock]
+ See also [method floor], [method round], and [method stepify].
</description>
</method>
<method name="char">
@@ -338,6 +341,7 @@
# a is -3.0
a = floor(-2.99)
[/codeblock]
+ See also [method ceil], [method round], and [method stepify].
[b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly.
</description>
</method>
@@ -367,42 +371,19 @@
<description>
Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative.
[codeblock]
- var i = -6
- while i &lt; 5:
- prints(i, fposmod(i, 3))
- i += 1
+ for i in 7:
+ var x = 0.5 * i - 1.5
+ print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)])
[/codeblock]
Produces:
[codeblock]
- -6 0
- -5 1
- -4 2
- -3 0
- -2 1
- -1 2
- 0 0
- 1 1
- 2 2
- 3 0
- 4 1
- [/codeblock]
- </description>
- </method>
- <method name="funcref">
- <return type="FuncRef">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
- <argument index="1" name="funcname" type="String">
- </argument>
- <description>
- Returns a reference to the specified function [code]funcname[/code] in the [code]instance[/code] node. As functions aren't first-class objects in GDscript, use [code]funcref[/code] to store a [FuncRef] in a variable and call it later.
- [codeblock]
- func foo():
- return("bar")
-
- a = funcref(self, "foo")
- print(a.call_func()) # Prints bar
+ -1.5 -0.0 0.0
+ -1.0 -1.0 0.5
+ -0.5 -0.5 1.0
+ 0.0 0.0 0.0
+ 0.5 0.5 0.5
+ 1.0 1.0 1.0
+ 1.5 0.0 0.0
[/codeblock]
</description>
</method>
@@ -630,6 +611,7 @@
var main = load("res://main.tscn") # main will contain a PackedScene resource.
[/codeblock]
[b]Important:[/b] The path must be absolute, a local path will just return [code]null[/code].
+ This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios.
</description>
</method>
<method name="log">
@@ -769,24 +751,18 @@
<description>
Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative.
[codeblock]
- var i = -6
- while i &lt; 5:
- prints(i, posmod(i, 3))
- i += 1
+ for i in range(-3, 4):
+ print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)])
[/codeblock]
Produces:
[codeblock]
- -6 0
- -5 1
- -4 2
- -3 0
- -2 1
- -1 2
- 0 0
- 1 1
- 2 2
- 3 0
- 4 1
+ -3 0 0
+ -2 -2 1
+ -1 -1 2
+ 0 0 0
+ 1 1 1
+ 2 2 2
+ 3 0 0
[/codeblock]
</description>
</method>
@@ -827,6 +803,7 @@
a = [1, 2, 3]
print("a", "b", a) # Prints ab[1, 2, 3]
[/codeblock]
+ [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed.
</description>
</method>
<method name="print_debug" qualifiers="vararg">
@@ -900,6 +877,7 @@
[codeblock]
push_error("test error") # Prints "test error" to debugger and terminal as error call
[/codeblock]
+ [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead.
</description>
</method>
<method name="push_warning">
@@ -926,20 +904,6 @@
[/codeblock]
</description>
</method>
- <method name="rand_range">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <description>
- Random range, any floating point value between [code]from[/code] and [code]to[/code].
- [codeblock]
- prints(rand_range(0, 1), rand_range(0, 1)) # Prints e.g. 0.135591 0.405263
- [/codeblock]
- </description>
- </method>
<method name="rand_seed">
<return type="Array">
</return>
@@ -959,6 +923,20 @@
[/codeblock]
</description>
</method>
+ <method name="randf_range">
+ <return type="float">
+ </return>
+ <argument index="0" name="from" type="float">
+ </argument>
+ <argument index="1" name="to" type="float">
+ </argument>
+ <description>
+ Random range, any floating point value between [code]from[/code] and [code]to[/code].
+ [codeblock]
+ prints(randf_range(-10, 10), randf_range(-10, 10)) # Prints e.g. -3.844535 7.45315
+ [/codeblock]
+ </description>
+ </method>
<method name="randi">
<return type="int">
</return>
@@ -972,6 +950,21 @@
[/codeblock]
</description>
</method>
+ <method name="randi_range">
+ <return type="int">
+ </return>
+ <argument index="0" name="from" type="int">
+ </argument>
+ <argument index="1" name="to" type="int">
+ </argument>
+ <description>
+ Random range, any 32-bit integer value between [code]from[/code] and [code]to[/code] (inclusive). If [code]to[/code] is lesser than [code]from[/code] they are swapped.
+ [codeblock]
+ print(randi_range(0, 1)) # Prints 0 or 1
+ print(randi_range(-10, 1000)) # Prints any number from -10 to 1000
+ [/codeblock]
+ </description>
+ </method>
<method name="randomize">
<return type="void">
</return>
@@ -989,27 +982,15 @@
<description>
Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment).
[codeblock]
- for i in range(4):
- print(i)
- for i in range(2, 5):
- print(i)
- for i in range(0, 6, 2):
- print(i)
+ print(range(4))
+ print(range(2, 5))
+ print(range(0, 6, 2))
[/codeblock]
Output:
[codeblock]
- 0
- 1
- 2
- 3
-
- 2
- 3
- 4
-
- 0
- 2
- 4
+ [0, 1, 2, 3]
+ [2, 3, 4]
+ [0, 2, 4]
[/codeblock]
</description>
</method>
@@ -1043,6 +1024,7 @@
[codeblock]
round(2.6) # Returns 3
[/codeblock]
+ See also [method floor], [method ceil], and [method stepify].
</description>
</method>
<method name="seed">
@@ -1161,6 +1143,7 @@
stepify(100, 32) # Returns 96
stepify(3.14159, 0.01) # Returns 3.14
[/codeblock]
+ See also [method ceil], [method floor], and [method round].
</description>
</method>
<method name="str" qualifiers="vararg">
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 62ccb93901..631a102130 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
+ <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
</tutorials>
<methods>
<method name="get_as_byte_code" qualifiers="const">
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ae1f2893f1..7f7410a92c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -33,15 +33,15 @@
#include "../gdscript_tokenizer.h"
#include "editor/editor_settings.h"
-static bool _is_char(CharType c) {
+static bool _is_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
-static bool _is_hex_symbol(CharType c) {
+static bool _is_hex_symbol(char32_t c) {
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
-static bool _is_bin_symbol(CharType c) {
+static bool _is_bin_symbol(char32_t c) {
return (c == '0' || c == '1');
}
@@ -73,6 +73,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
color_region_cache[p_line] = -1;
int in_region = -1;
if (p_line != 0) {
+ int prev_region_line = p_line - 1;
+ while (prev_region_line > 0 && !color_region_cache.has(prev_region_line)) {
+ prev_region_line--;
+ }
+ for (int i = prev_region_line; i < p_line - 1; i++) {
+ get_line_syntax_highlighting(i);
+ }
if (!color_region_cache.has(p_line - 1)) {
get_line_syntax_highlighting(p_line - 1);
}
@@ -119,7 +126,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
/* search the line */
bool match = true;
- const CharType *start_key = color_regions[c].start_key.c_str();
+ const char32_t *start_key = color_regions[c].start_key.get_data();
for (int k = 0; k < start_key_length; k++) {
if (start_key[k] != str[from + k]) {
match = false;
@@ -153,18 +160,16 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
/* if we are in one find the end key */
if (in_region != -1) {
- /* check there is enough room */
- int chars_left = line_length - from;
- int end_key_length = color_regions[in_region].end_key.length();
- if (chars_left < end_key_length) {
- continue;
- }
-
/* search the line */
int region_end_index = -1;
- const CharType *end_key = color_regions[in_region].start_key.c_str();
+ int end_key_length = color_regions[in_region].end_key.length();
+ const char32_t *end_key = color_regions[in_region].end_key.get_data();
for (; from < line_length; from++) {
- if (!is_a_symbol) {
+ if (line_length - from < end_key_length) {
+ break;
+ }
+
+ if (!is_symbol(str[from])) {
continue;
}
@@ -173,9 +178,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
continue;
}
+ region_end_index = from;
for (int k = 0; k < end_key_length; k++) {
- if (end_key[k] == str[from + k]) {
- region_end_index = from;
+ if (end_key[k] != str[from + k]) {
+ region_end_index = -1;
break;
}
}
@@ -192,7 +198,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
previous_type = REGION;
previous_text = "";
previous_column = j;
- j = from;
+ j = from + (end_key_length - 1);
if (region_end_index == -1) {
color_region_cache[p_line] = in_region;
}
@@ -572,8 +578,12 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons
}
}
+ int at = 0;
for (int i = 0; i < color_regions.size(); i++) {
ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+ if (p_start_key.length() < color_regions[i].start_key.length()) {
+ at++;
+ }
}
ColorRegion color_region;
@@ -581,7 +591,8 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons
color_region.start_key = p_start_key;
color_region.end_key = p_end_key;
color_region.line_only = p_line_only;
- color_regions.push_back(color_region);
+ color_regions.insert(at, color_region);
+ clear_highlighting_cache();
}
Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index 6d454e43f2..944ed859f5 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -37,9 +37,11 @@ void GDScriptEditorTranslationParserPlugin::get_recognized_extensions(List<Strin
GDScriptLanguage::get_singleton()->get_recognized_extensions(r_extensions);
}
-Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_extracted_strings) {
- // Parse and match all GDScript function API that involves translation string.
- // E.g get_node("Label").text = "something", var test = tr("something"), "something" will be matched and collected.
+Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) {
+ // Extract all translatable strings using the parsed tree from GDSriptParser.
+ // The strategy is to find all ExpressionNode and AssignmentNode from the tree and extract strings if relevant, i.e
+ // Search strings in ExpressionNode -> CallNode -> tr(), set_text(), set_placeholder() etc.
+ // Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
Error err;
RES loaded_res = ResourceLoader::load(p_path, "", false, &err);
@@ -48,108 +50,302 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
return err;
}
+ ids = r_ids;
+ ids_ctx_plural = r_ids_ctx_plural;
Ref<GDScript> gdscript = loaded_res;
String source_code = gdscript->get_source_code();
- Vector<String> parsed_strings;
-
- // Search translation strings with RegEx.
- regex.clear();
- regex.compile(String("|").join(patterns));
- Array results = regex.search_all(source_code);
- _get_captured_strings(results, &parsed_strings);
-
- // Special handling for FileDialog.
- Vector<String> temp;
- _parse_file_dialog(source_code, &temp);
- parsed_strings.append_array(temp);
-
- // Filter out / and +
- String filter = "(?:\\\\\\n|\"[\\s\\\\]*\\+\\s*\")";
- regex.clear();
- regex.compile(filter);
- for (int i = 0; i < parsed_strings.size(); i++) {
- parsed_strings.set(i, regex.sub(parsed_strings[i], "", true));
+
+ GDScriptParser parser;
+ err = parser.parse(source_code, p_path, false);
+ if (err != OK) {
+ ERR_PRINT("Failed to parse with GDScript with GDScriptParser.");
+ return err;
}
- r_extracted_strings->append_array(parsed_strings);
+ // Traverse through the parsed tree from GDScriptParser.
+ GDScriptParser::ClassNode *c = parser.get_tree();
+ _traverse_class(c);
return OK;
}
-void GDScriptEditorTranslationParserPlugin::_parse_file_dialog(const String &p_source_code, Vector<String> *r_output) {
- // FileDialog API has the form .filters = PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]).
- // First filter: Get "*.png ; PNG Images", "*.gd ; GDScript Files" from PackedStringArray.
- regex.clear();
- regex.compile(String("|").join(file_dialog_patterns));
- Array results = regex.search_all(p_source_code);
-
- Vector<String> temp;
- _get_captured_strings(results, &temp);
- String captured_strings = String(",").join(temp);
-
- // Second filter: Get the texts after semicolon from "*.png ; PNG Images","*.gd ; GDScript Files".
- String second_filter = "\"[^;]+;" + text + "\"";
- regex.clear();
- regex.compile(second_filter);
- results = regex.search_all(captured_strings);
- _get_captured_strings(results, r_output);
- for (int i = 0; i < r_output->size(); i++) {
- r_output->set(i, r_output->get(i).strip_edges());
+void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ const GDScriptParser::ClassNode::Member &m = p_class->members[i];
+ // There are 7 types of Member, but only class, function and variable can contain translatable strings.
+ switch (m.type) {
+ case GDScriptParser::ClassNode::Member::CLASS:
+ _traverse_class(m.m_class);
+ break;
+ case GDScriptParser::ClassNode::Member::FUNCTION:
+ _traverse_function(m.function);
+ break;
+ case GDScriptParser::ClassNode::Member::VARIABLE:
+ _read_variable(m.variable);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_traverse_function(const GDScriptParser::FunctionNode *p_func) {
+ _traverse_block(p_func->body);
+}
+
+void GDScriptEditorTranslationParserPlugin::_read_variable(const GDScriptParser::VariableNode *p_var) {
+ _assess_expression(p_var->initializer);
+}
+
+void GDScriptEditorTranslationParserPlugin::_traverse_block(const GDScriptParser::SuiteNode *p_suite) {
+ if (!p_suite) {
+ return;
+ }
+
+ const Vector<GDScriptParser::Node *> &statements = p_suite->statements;
+ for (int i = 0; i < statements.size(); i++) {
+ GDScriptParser::Node *statement = statements[i];
+
+ // Statements with Node type constant, break, continue, pass, breakpoint are skipped because they can't contain translatable strings.
+ switch (statement->type) {
+ case GDScriptParser::Node::VARIABLE:
+ _assess_expression(static_cast<GDScriptParser::VariableNode *>(statement)->initializer);
+ break;
+ case GDScriptParser::Node::IF: {
+ GDScriptParser::IfNode *if_node = static_cast<GDScriptParser::IfNode *>(statement);
+ _assess_expression(if_node->condition);
+ //FIXME : if the elif logic is changed in GDScriptParser, then this probably will have to change as well. See GDScriptParser::TreePrinter::print_if().
+ _traverse_block(if_node->true_block);
+ _traverse_block(if_node->false_block);
+ break;
+ }
+ case GDScriptParser::Node::FOR: {
+ GDScriptParser::ForNode *for_node = static_cast<GDScriptParser::ForNode *>(statement);
+ _assess_expression(for_node->list);
+ _traverse_block(for_node->loop);
+ break;
+ }
+ case GDScriptParser::Node::WHILE: {
+ GDScriptParser::WhileNode *while_node = static_cast<GDScriptParser::WhileNode *>(statement);
+ _assess_expression(while_node->condition);
+ _traverse_block(while_node->loop);
+ break;
+ }
+ case GDScriptParser::Node::MATCH: {
+ GDScriptParser::MatchNode *match_node = static_cast<GDScriptParser::MatchNode *>(statement);
+ _assess_expression(match_node->test);
+ for (int j = 0; j < match_node->branches.size(); j++) {
+ _traverse_block(match_node->branches[j]->block);
+ }
+ break;
+ }
+ case GDScriptParser::Node::RETURN:
+ _assess_expression(static_cast<GDScriptParser::ReturnNode *>(statement)->return_value);
+ break;
+ case GDScriptParser::Node::ASSERT:
+ _assess_expression((static_cast<GDScriptParser::AssertNode *>(statement))->condition);
+ break;
+ case GDScriptParser::Node::ASSIGNMENT:
+ _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(statement));
+ break;
+ default:
+ if (statement->is_expression()) {
+ _assess_expression(static_cast<GDScriptParser::ExpressionNode *>(statement));
+ }
+ break;
+ }
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_assess_expression(GDScriptParser::ExpressionNode *p_expression) {
+ // Explore all ExpressionNodes to find CallNodes which contain translation strings, such as tr(), set_text() etc.
+ // tr() can be embedded quite deep within multiple ExpressionNodes so need to dig down to search through all ExpressionNodes.
+ if (!p_expression) {
+ return;
+ }
+
+ // ExpressionNode of type await, cast, get_node, identifier, literal, preload, self, subscript, unary are ignored as they can't be CallNode
+ // containing translation strings.
+ switch (p_expression->type) {
+ case GDScriptParser::Node::ARRAY: {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(p_expression);
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _assess_expression(array_node->elements[i]);
+ }
+ break;
+ }
+ case GDScriptParser::Node::ASSIGNMENT:
+ _assess_assignment(static_cast<GDScriptParser::AssignmentNode *>(p_expression));
+ break;
+ case GDScriptParser::Node::BINARY_OPERATOR: {
+ GDScriptParser::BinaryOpNode *binary_op_node = static_cast<GDScriptParser::BinaryOpNode *>(p_expression);
+ _assess_expression(binary_op_node->left_operand);
+ _assess_expression(binary_op_node->right_operand);
+ break;
+ }
+ case GDScriptParser::Node::CALL: {
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_expression);
+ _extract_from_call(call_node);
+ for (int i = 0; i < call_node->arguments.size(); i++) {
+ _assess_expression(call_node->arguments[i]);
+ }
+ } break;
+ case GDScriptParser::Node::DICTIONARY: {
+ GDScriptParser::DictionaryNode *dict_node = static_cast<GDScriptParser::DictionaryNode *>(p_expression);
+ for (int i = 0; i < dict_node->elements.size(); i++) {
+ _assess_expression(dict_node->elements[i].key);
+ _assess_expression(dict_node->elements[i].value);
+ }
+ break;
+ }
+ case GDScriptParser::Node::TERNARY_OPERATOR: {
+ GDScriptParser::TernaryOpNode *ternary_op_node = static_cast<GDScriptParser::TernaryOpNode *>(p_expression);
+ _assess_expression(ternary_op_node->condition);
+ _assess_expression(ternary_op_node->true_expr);
+ _assess_expression(ternary_op_node->false_expr);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void GDScriptEditorTranslationParserPlugin::_assess_assignment(GDScriptParser::AssignmentNode *p_assignment) {
+ // Extract the translatable strings coming from assignments. For example, get_node("Label").text = "____"
+
+ StringName assignee_name;
+ if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
+ assignee_name = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee)->name;
+ } else if (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT) {
+ assignee_name = static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->attribute->name;
+ }
+
+ if (assignment_patterns.has(assignee_name) && p_assignment->assigned_value->type == GDScriptParser::Node::LITERAL) {
+ // If the assignment is towards one of the extract patterns (text, hint_tooltip etc.), and the value is a string literal, we collect the string.
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_assignment->assigned_value)->value);
+ } else if (assignee_name == fd_filters && p_assignment->assigned_value->type == GDScriptParser::Node::CALL) {
+ // FileDialog.filters accepts assignment in the form of PackedStringArray. For example,
+ // get_node("FileDialog").filters = PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]).
+
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value);
+ if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+
+ // Extract the name in "extension ; name" of PackedStringArray.
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _extract_fd_literals(array_node->elements[i]);
+ }
+ }
+ } else {
+ // If the assignee is not in extract patterns or the assigned_value is not Literal type, try to see if the assigned_value contains tr().
+ _assess_expression(p_assignment->assigned_value);
}
}
-void GDScriptEditorTranslationParserPlugin::_get_captured_strings(const Array &p_results, Vector<String> *r_output) {
- Ref<RegExMatch> result;
- for (int i = 0; i < p_results.size(); i++) {
- result = p_results[i];
- for (int j = 0; j < result->get_group_count(); j++) {
- String s = result->get_string(j + 1);
- // Prevent reading text with only spaces.
- if (!s.strip_edges().empty()) {
- r_output->push_back(s);
+void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::CallNode *p_call) {
+ // Extract the translatable strings coming from function calls. For example:
+ // tr("___"), get_node("Label").set_text("____"), get_node("LineEdit").set_placeholder("____").
+
+ StringName function_name = p_call->function_name;
+
+ // Variables for extracting tr() and tr_n().
+ Vector<String> id_ctx_plural;
+ id_ctx_plural.resize(3);
+ bool extract_id_ctx_plural = true;
+
+ if (function_name == tr_func) {
+ // Extract from tr(id, ctx).
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (p_call->arguments[i]->type == GDScriptParser::Node::LITERAL) {
+ id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[i])->value;
+ } else {
+ // Avoid adding something like tr("Flying dragon", var_context_level_1). We want to extract both id and context together.
+ extract_id_ctx_plural = false;
+ }
+ }
+ if (extract_id_ctx_plural) {
+ ids_ctx_plural->push_back(id_ctx_plural);
+ }
+ } else if (function_name == trn_func) {
+ // Extract from tr_n(id, plural, n, ctx).
+ Vector<int> indices;
+ indices.push_back(0);
+ indices.push_back(3);
+ indices.push_back(1);
+ for (int i = 0; i < indices.size(); i++) {
+ if (indices[i] >= p_call->arguments.size()) {
+ continue;
+ }
+
+ if (p_call->arguments[indices[i]]->type == GDScriptParser::Node::LITERAL) {
+ id_ctx_plural.write[i] = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[indices[i]])->value;
+ } else {
+ extract_id_ctx_plural = false;
+ }
+ }
+ if (extract_id_ctx_plural) {
+ ids_ctx_plural->push_back(id_ctx_plural);
+ }
+ } else if (first_arg_patterns.has(function_name)) {
+ // Extracting argument with only string literals. In other words, not extracting something like set_text("hello " + some_var).
+ if (p_call->arguments[0]->type == GDScriptParser::Node::LITERAL) {
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[0])->value);
+ }
+ } else if (second_arg_patterns.has(function_name)) {
+ if (p_call->arguments[1]->type == GDScriptParser::Node::LITERAL) {
+ ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[1])->value);
+ }
+ } else if (function_name == fd_add_filter) {
+ // Extract the 'JPE Images' in this example - get_node("FileDialog").add_filter("*.jpg; JPE Images").
+ _extract_fd_literals(p_call->arguments[0]);
+
+ } else if (function_name == fd_set_filter && p_call->arguments[0]->type == GDScriptParser::Node::CALL) {
+ // FileDialog.set_filters() accepts assignment in the form of PackedStringArray. For example,
+ // get_node("FileDialog").set_filters( PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"])).
+
+ GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_call->arguments[0]);
+ if (call_node->arguments[0]->type == GDScriptParser::Node::ARRAY) {
+ GDScriptParser::ArrayNode *array_node = static_cast<GDScriptParser::ArrayNode *>(call_node->arguments[0]);
+ for (int i = 0; i < array_node->elements.size(); i++) {
+ _extract_fd_literals(array_node->elements[i]);
}
}
}
}
+void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser::ExpressionNode *p_expression) {
+ // Extract the name in "extension ; name".
+
+ if (p_expression->type == GDScriptParser::Node::LITERAL) {
+ String arg_val = String(static_cast<GDScriptParser::LiteralNode *>(p_expression)->value);
+ PackedStringArray arr = arg_val.split(";", true);
+ if (arr.size() != 2) {
+ ERR_PRINT("Argument for setting FileDialog has bad format.");
+ return;
+ }
+ ids->push_back(arr[1].strip_edges());
+ }
+}
+
GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() {
- // Regex search pattern templates.
- // The extra complication in the regex pattern is to ensure that the matching works when users write over multiple lines, use tabs etc.
- const String dot = "\\.[\\s\\\\]*";
- const String str_assign_template = "[\\s\\\\]*=[\\s\\\\]*\"" + text + "\"";
- const String first_arg_template = "[\\s\\\\]*\\([\\s\\\\]*\"" + text + "\"[\\s\\S]*?\\)";
- const String second_arg_template = "[\\s\\\\]*\\([\\s\\S]+?,[\\s\\\\]*\"" + text + "\"[\\s\\S]*?\\)";
-
- // Common patterns.
- patterns.push_back("tr" + first_arg_template);
- patterns.push_back(dot + "text" + str_assign_template);
- patterns.push_back(dot + "placeholder_text" + str_assign_template);
- patterns.push_back(dot + "hint_tooltip" + str_assign_template);
- patterns.push_back(dot + "set_text" + first_arg_template);
- patterns.push_back(dot + "set_tooltip" + first_arg_template);
- patterns.push_back(dot + "set_placeholder" + first_arg_template);
-
- // Tabs and TabContainer API.
- patterns.push_back(dot + "set_tab_title" + second_arg_template);
- patterns.push_back(dot + "add_tab" + first_arg_template);
-
- // PopupMenu API.
- patterns.push_back(dot + "add_check_item" + first_arg_template);
- patterns.push_back(dot + "add_icon_check_item" + second_arg_template);
- patterns.push_back(dot + "add_icon_item" + second_arg_template);
- patterns.push_back(dot + "add_icon_radio_check_item" + second_arg_template);
- patterns.push_back(dot + "add_item" + first_arg_template);
- patterns.push_back(dot + "add_multistate_item" + first_arg_template);
- patterns.push_back(dot + "add_radio_check_item" + first_arg_template);
- patterns.push_back(dot + "add_separator" + first_arg_template);
- patterns.push_back(dot + "add_submenu_item" + first_arg_template);
- patterns.push_back(dot + "set_item_text" + second_arg_template);
- //patterns.push_back(dot + "set_item_tooltip" + second_arg_template); //no tr() behind this function. might be bug.
-
- // FileDialog API - special case.
- const String fd_text = "((?:[\\s\\\\]*\"(?:[^\"\\\\]|\\\\[\\s\\S])*(?:\"[\\s\\\\]*\\+[\\s\\\\]*\"(?:[^\"\\\\]|\\\\[\\s\\S])*)*\"[\\s\\\\]*,?)*)";
- const String packed_string_array = "[\\s\\\\]*PackedStringArray[\\s\\\\]*\\([\\s\\\\]*\\[" + fd_text + "\\][\\s\\\\]*\\)";
- file_dialog_patterns.push_back(dot + "add_filter[\\s\\\\]*\\(" + fd_text + "[\\s\\\\]*\\)");
- file_dialog_patterns.push_back(dot + "filters[\\s\\\\]*=" + packed_string_array);
- file_dialog_patterns.push_back(dot + "set_filters[\\s\\\\]*\\(" + packed_string_array + "[\\s\\\\]*\\)");
+ assignment_patterns.insert("text");
+ assignment_patterns.insert("placeholder_text");
+ assignment_patterns.insert("hint_tooltip");
+
+ first_arg_patterns.insert("set_text");
+ first_arg_patterns.insert("set_tooltip");
+ first_arg_patterns.insert("set_placeholder");
+ first_arg_patterns.insert("add_tab");
+ first_arg_patterns.insert("add_check_item");
+ first_arg_patterns.insert("add_item");
+ first_arg_patterns.insert("add_multistate_item");
+ first_arg_patterns.insert("add_radio_check_item");
+ first_arg_patterns.insert("add_separator");
+ first_arg_patterns.insert("add_submenu_item");
+
+ second_arg_patterns.insert("set_tab_title");
+ second_arg_patterns.insert("add_icon_check_item");
+ second_arg_patterns.insert("add_icon_item");
+ second_arg_patterns.insert("add_icon_radio_check_item");
+ second_arg_patterns.insert("set_item_text");
}
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 9fa4b69f01..d763df01f5 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -31,23 +31,40 @@
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
+#include "core/templates/set.h"
#include "editor/editor_translation_parser.h"
+#include "modules/gdscript/gdscript_parser.h"
#include "modules/regex/regex.h"
class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
- // Regex and search patterns that are used to match translation strings.
- const String text = "((?:[^\"\\\\]|\\\\[\\s\\S])*(?:\"[\\s\\\\]*\\+[\\s\\\\]*\"(?:[^\"\\\\]|\\\\[\\s\\S])*)*)";
- RegEx regex;
- Vector<String> patterns;
- Vector<String> file_dialog_patterns;
+ Vector<String> *ids;
+ Vector<Vector<String>> *ids_ctx_plural;
- void _parse_file_dialog(const String &p_source_code, Vector<String> *r_output);
- void _get_captured_strings(const Array &p_results, Vector<String> *r_output);
+ // List of patterns used for extracting translation strings.
+ StringName tr_func = "tr";
+ StringName trn_func = "tr_n";
+ Set<StringName> assignment_patterns;
+ Set<StringName> first_arg_patterns;
+ Set<StringName> second_arg_patterns;
+ // FileDialog patterns.
+ StringName fd_add_filter = "add_filter";
+ StringName fd_set_filter = "set_filters";
+ StringName fd_filters = "filters";
+
+ void _traverse_class(const GDScriptParser::ClassNode *p_class);
+ void _traverse_function(const GDScriptParser::FunctionNode *p_func);
+ void _traverse_block(const GDScriptParser::SuiteNode *p_suite);
+
+ void _read_variable(const GDScriptParser::VariableNode *p_var);
+ void _assess_expression(GDScriptParser::ExpressionNode *p_expression);
+ void _assess_assignment(GDScriptParser::AssignmentNode *p_assignment);
+ void _extract_from_call(GDScriptParser::CallNode *p_call);
+ void _extract_fd_literals(GDScriptParser::ExpressionNode *p_expression);
public:
- virtual Error parse_file(const String &p_path, Vector<String> *r_extracted_strings) override;
+ virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
virtual void get_recognized_extensions(List<String> *r_extensions) const override;
GDScriptEditorTranslationParserPlugin();
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 9170255c02..53602f7a9b 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -32,13 +32,13 @@
#include <stdint.h>
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
@@ -596,6 +596,19 @@ Error GDScript::reload(bool p_keep_state) {
return OK;
}
+ {
+ String source_path = path;
+ if (source_path.empty()) {
+ source_path = get_path();
+ }
+ if (!source_path.empty()) {
+ MutexLock lock(GDScriptCache::singleton->lock);
+ if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
+ GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
+ }
+ }
+ }
+
valid = false;
GDScriptParser parser;
Error err = parser.parse(source, path, false);
@@ -1031,8 +1044,10 @@ GDScript::~GDScript() {
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
- E->self()->_clear_stack();
+ // Order matters since clearing the stack may already cause
+ // the GDSCriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
+ E->self()->_clear_stack();
}
}
@@ -1040,7 +1055,9 @@ GDScript::~GDScript() {
memdelete(E->get());
}
- GDScriptCache::remove_script(get_path());
+ if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
+ GDScriptCache::remove_script(get_path());
+ }
_save_orphaned_subclasses();
@@ -1075,7 +1092,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
// Try conversion
Callable::CallError ce;
const Variant *value = &p_value;
- Variant converted = Variant::construct(member->data_type.builtin_type, &value, 1, ce);
+ Variant converted;
+ Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
if (ce.error == Callable::CallError::CALL_OK) {
members.write[member->index] = converted;
return true;
@@ -1436,8 +1454,10 @@ GDScriptInstance::~GDScriptInstance() {
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
- E->self()->_clear_stack();
+ // Order matters since clearing the stack may already cause
+ // the GDSCriptFunctionState to be destroyed and thus removed from the list.
pending_func_states.remove(E);
+ E->self()->_clear_stack();
}
if (script.is_valid() && owner) {
@@ -1481,9 +1501,9 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
void GDScriptLanguage::init() {
//populate global constants
- int gcc = GlobalConstants::get_global_constant_count();
+ int gcc = CoreConstants::get_global_constant_count();
for (int i = 0; i < gcc; i++) {
- _add_global(StaticCString::create(GlobalConstants::get_global_constant_name(i)), GlobalConstants::get_global_constant_value(i));
+ _add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i));
}
_add_global(StaticCString::create("PI"), Math_PI);
@@ -2022,7 +2042,33 @@ GDScriptLanguage::~GDScriptLanguage() {
if (_call_stack) {
memdelete_arr(_call_stack);
}
- singleton = nullptr;
+
+ // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
+ SelfList<GDScript> *s = script_list.first();
+ while (s) {
+ GDScript *script = s->self();
+ // This ensures the current script is not released before we can check what's the next one
+ // in the list (we can't get the next upfront because we don't know if the reference breaking
+ // will cause it -or any other after it, for that matter- to be released so the next one
+ // is not the same as before).
+ script->reference();
+
+ for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) {
+ GDScriptFunction *func = E->get();
+ for (int i = 0; i < func->argument_types.size(); i++) {
+ func->argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ func->return_type.script_type_ref = Ref<Script>();
+ }
+ for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
+ E->get().data_type.script_type_ref = Ref<Script>();
+ }
+
+ s = s->next();
+ script->unreference();
+ }
+
+ singleton = NULL;
}
void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 9906b4014d..b69a6e39c0 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -35,7 +35,7 @@
#include "core/debugger/script_debugger.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "gdscript_function.h"
class GDScriptNativeClass : public Reference {
@@ -69,6 +69,7 @@ class GDScript : public Script {
friend class GDScriptInstance;
friend class GDScriptFunction;
+ friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
friend class GDScriptFunctions;
friend class GDScriptLanguage;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 561cdbbda4..6b23ab1616 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -30,11 +30,12 @@
#include "gdscript_analyzer.h"
-#include "core/class_db.h"
-#include "core/hash_map.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
+#include "core/os/file_access.h"
+#include "core/templates/hash_map.h"
#include "gdscript.h"
// TODO: Move this to a central location (maybe core?).
@@ -181,7 +182,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class);
return err;
@@ -207,11 +208,12 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
+ base = parser->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
@@ -226,7 +228,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
@@ -301,6 +303,16 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
+ // Check for cyclic inheritance.
+ const GDScriptParser::ClassNode *base_class = result.class_type;
+ while (base_class) {
+ if (base_class->fqcn == p_class->fqcn) {
+ push_error("Cyclic inheritance.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ base_class = base_class->base_type.class_type;
+ }
+
p_class->base_type = result;
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
@@ -308,7 +320,10 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (p_recursive) {
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
- resolve_inheritance(p_class->members[i].m_class, true);
+ Error err = resolve_inheritance(p_class->members[i].m_class, true);
+ if (err) {
+ return err;
+ }
}
}
}
@@ -490,6 +505,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.variable->set_datatype(datatype); // Allow recursive usage.
reduce_expression(member.variable->initializer);
datatype = member.variable->initializer->get_datatype();
+ if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
+ datatype.type_source = GDScriptParser::DataType::INFERRED;
+ }
}
if (member.variable->datatype_specifier != nullptr) {
@@ -498,7 +516,13 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer != nullptr) {
if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
- 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);
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
+ 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.
+ mark_node_unsafe(member.variable->initializer);
+ }
} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED
parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
@@ -519,6 +543,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else if (datatype.builtin_type == Variant::NIL) {
push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
}
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
datatype.is_constant = false;
@@ -533,6 +558,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
break;
case GDScriptParser::DataType::NATIVE:
if (ClassDB::is_parent_class(get_real_class_name(datatype.native_type), "Resource")) {
+ member.variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
member.variable->export_info.hint_string = get_real_class_name(datatype.native_type);
} else {
push_error(R"(Export type can only be built-in or a resource.)", member.variable);
@@ -551,6 +577,12 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
GDScriptParser::DataType datatype = member.constant->get_datatype();
if (member.constant->initializer) {
+ if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer));
+ } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
+ }
+
if (!member.constant->initializer->is_constant) {
push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
}
@@ -596,17 +628,67 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
enum_type.is_meta_type = true;
enum_type.is_constant = true;
+ // Enums can't be nested, so we can safely override this.
+ current_enum = member.m_enum;
+
for (int j = 0; j < member.m_enum->values.size(); j++) {
- enum_type.enum_values[member.m_enum->values[j].identifier->name] = member.m_enum->values[j].value;
+ GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j];
+
+ if (element.custom_value) {
+ reduce_expression(element.custom_value);
+ if (!element.custom_value->is_constant) {
+ push_error(R"(Enum values must be constant.)", element.custom_value);
+ } else if (element.custom_value->reduced_value.get_type() != Variant::INT) {
+ push_error(R"(Enum values must be integers.)", element.custom_value);
+ } else {
+ element.value = element.custom_value->reduced_value;
+ element.resolved = true;
+ }
+ } else {
+ if (element.index > 0) {
+ element.value = element.parent_enum->values[element.index - 1].value + 1;
+ } else {
+ element.value = 0;
+ }
+ element.resolved = true;
+ }
+
+ enum_type.enum_values[element.identifier->name] = element.value;
}
+ current_enum = nullptr;
+
member.m_enum->set_datatype(enum_type);
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
resolve_function_signature(member.function);
break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
- break; // Nothing to do, type and value set in parser.
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ if (member.enum_value.custom_value) {
+ current_enum = member.enum_value.parent_enum;
+ reduce_expression(member.enum_value.custom_value);
+ current_enum = nullptr;
+
+ if (!member.enum_value.custom_value->is_constant) {
+ push_error(R"(Enum values must be constant.)", member.enum_value.custom_value);
+ } else if (member.enum_value.custom_value->reduced_value.get_type() != Variant::INT) {
+ push_error(R"(Enum values must be integers.)", member.enum_value.custom_value);
+ } else {
+ member.enum_value.value = member.enum_value.custom_value->reduced_value;
+ member.enum_value.resolved = true;
+ }
+ } else {
+ if (member.enum_value.index > 0) {
+ member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1;
+ } else {
+ member.enum_value.value = 0;
+ }
+ member.enum_value.resolved = true;
+ }
+ // Also update the original references.
+ member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value;
+ p_class->members.write[i].enum_value = member.enum_value;
+ } break;
case GDScriptParser::ClassNode::Member::CLASS:
break; // Done later.
case GDScriptParser::ClassNode::Member::UNDEFINED:
@@ -718,7 +800,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
resolve_match_branch(static_cast<GDScriptParser::MatchBranchNode *>(p_node), nullptr);
break;
case GDScriptParser::Node::PARAMETER:
- resolve_pararameter(static_cast<GDScriptParser::ParameterNode *>(p_node));
+ resolve_parameter(static_cast<GDScriptParser::ParameterNode *>(p_node));
break;
case GDScriptParser::Node::PATTERN:
resolve_match_pattern(static_cast<GDScriptParser::PatternNode *>(p_node), nullptr);
@@ -772,7 +854,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->current_function = p_function;
for (int i = 0; i < p_function->parameters.size(); i++) {
- resolve_pararameter(p_function->parameters[i]);
+ resolve_parameter(p_function->parameters[i]);
#ifdef DEBUG_ENABLED
if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
@@ -842,6 +924,7 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript
p_suite->datatype.type_source = GDScriptParser::DataType::UNDETECTED;
} else {
p_suite->set_datatype(p_statement->get_datatype());
+ p_suite->datatype.type_source = GDScriptParser::DataType::INFERRED;
}
break;
default:
@@ -994,7 +1077,13 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
if (p_variable->initializer != nullptr) {
if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
- 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);
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
+ 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.
+ mark_node_unsafe(p_variable->initializer);
+ }
#ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
@@ -1030,6 +1119,11 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
GDScriptParser::DataType type;
reduce_expression(p_constant->initializer);
+ if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
+ }
if (!p_constant->initializer->is_constant) {
push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
@@ -1181,14 +1275,19 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->set_datatype(result);
}
-void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_parameter) {
+void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::VARIANT;
if (p_parameter->default_value != nullptr) {
reduce_expression(p_parameter->default_value);
result = p_parameter->default_value->get_datatype();
- result.type_source = GDScriptParser::DataType::INFERRED;
+ if (p_parameter->infer_datatype) {
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ } else {
+ result.type_source = GDScriptParser::DataType::INFERRED;
+ }
+ result.is_constant = false;
}
if (p_parameter->datatype_specifier != nullptr) {
@@ -1198,7 +1297,7 @@ void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_para
if (p_parameter->default_value != nullptr) {
if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) {
- push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with paremeter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
+ push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
} else if (p_parameter->default_value->get_datatype().is_variant()) {
mark_node_unsafe(p_parameter);
}
@@ -1334,22 +1433,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
}
void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
- bool all_is_constant = true;
-
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
reduce_expression(element);
- all_is_constant = all_is_constant && element->is_constant;
- }
-
- if (all_is_constant) {
- Array array;
- array.resize(p_array->elements.size());
- for (int i = 0; i < p_array->elements.size(); i++) {
- array[i] = p_array->elements[i]->reduced_value;
- }
- p_array->is_constant = true;
- p_array->reduced_value = array;
}
// It's array in any case.
@@ -1378,16 +1464,23 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
bool compatible = true;
GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible);
+ op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
}
if (compatible) {
compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true);
if (!compatible) {
if (p_assignment->assignee->get_datatype().is_hard_type()) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) {
+ push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_assignment);
+ }
} else {
// TODO: Warning in this case.
+ mark_node_unsafe(p_assignment);
}
}
} else {
@@ -1534,7 +1627,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
ERR_PRINT("Parser bug: unknown binary operation.");
}
}
- p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value));
+ p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op));
return;
}
@@ -1548,7 +1641,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
} else {
if (p_binary_op->variant_op < Variant::OP_MAX) {
bool valid = false;
- result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid);
+ result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
if (!valid) {
push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
@@ -1618,7 +1711,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
Callable::CallError err;
- Variant value = Variant::construct(builtin_type, (const Variant **)args.ptr(), args.size(), err);
+ Variant value;
+ Variant::construct(builtin_type, value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
@@ -1634,6 +1728,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
signature += p_call->arguments[i]->get_datatype().to_string();
}
+ signature += ")";
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee);
} break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
@@ -1684,7 +1779,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
for (int i = 0; i < p_call->arguments.size(); i++) {
GDScriptParser::DataType par_type = type_from_property(info.arguments[i]);
- if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype())) {
+ if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
types_match = false;
break;
#ifdef DEBUG_ENABLED
@@ -1711,6 +1806,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
signature += p_call->arguments[i]->get_datatype().to_string();
}
+ signature += ")";
push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call);
}
}
@@ -1887,7 +1983,7 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- bool all_is_constant = true;
+ HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements;
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
@@ -1895,17 +1991,14 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
reduce_expression(element.key);
}
reduce_expression(element.value);
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
- }
- if (all_is_constant) {
- Dictionary dict;
- for (int i = 0; i < p_dictionary->elements.size(); i++) {
- const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
- dict[element.key->reduced_value] = element.value->reduced_value;
+ if (element.key->is_constant) {
+ if (elements.has(element.key->reduced_value)) {
+ push_error(vformat(R"(Key "%s" was already used in this dictionary (at line %d).)", element.key->reduced_value, elements[element.key->reduced_value]->start_line), element.key);
+ } else {
+ elements[element.key->reduced_value] = element.value;
+ }
}
- p_dictionary->is_constant = true;
- p_dictionary->reduced_value = dict;
}
// It's dictionary in any case.
@@ -1965,23 +2058,38 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = result;
- p_identifier->set_datatype(type_from_variant(result));
+ p_identifier->set_datatype(type_from_variant(result, p_identifier));
} else {
push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
}
} else {
- Callable::CallError temp;
- Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp);
- List<PropertyInfo> properties;
- dummy.get_property_list(&properties);
- for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
- const PropertyInfo &prop = E->get();
- if (prop.name == name) {
- p_identifier->set_datatype(type_from_property(prop));
+ switch (base.builtin_type) {
+ case Variant::NIL: {
+ push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier);
+ return;
+ }
+ case Variant::DICTIONARY: {
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy);
return;
}
+ default: {
+ Callable::CallError temp;
+ Variant dummy;
+ Variant::construct(base.builtin_type, dummy, nullptr, 0, temp);
+ List<PropertyInfo> properties;
+ dummy.get_property_list(&properties);
+ for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
+ const PropertyInfo &prop = E->get();
+ if (prop.name == name) {
+ p_identifier->set_datatype(type_from_property(prop));
+ return;
+ }
+ }
+ push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ }
}
- push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
}
return;
}
@@ -2096,7 +2204,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
- p_identifier->set_datatype(type_from_variant(int_constant));
+ p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
return;
}
}
@@ -2104,6 +2212,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) {
// TODO: This is opportunity to further infer types.
+
+ // Check if we are inside and enum. This allows enum values to access other elements of the same enum.
+ if (current_enum) {
+ for (int i = 0; i < current_enum->values.size(); i++) {
+ const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
+ 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.builtin_type = Variant::INT;
+ type.is_constant = true;
+ if (element.parent_enum->identifier) {
+ type.enum_type = element.parent_enum->identifier->name;
+ }
+ p_identifier->set_datatype(type);
+
+ if (element.resolved) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = element.value;
+ } else {
+ push_error(R"(Cannot use another enum element before it was declared.)", p_identifier);
+ }
+ return; // Found anyway.
+ }
+ }
+ }
+
// Check if identifier is local.
// If that's the case, the declaration already was solved before.
switch (p_identifier->source) {
@@ -2146,10 +2281,12 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
StringName name = p_identifier->name;
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
- // Check globals.
- if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
+ // Check globals. We make an exception for Variant::OBJECT because it's the base class for
+ // non-builtin types so we allow doing e.g. Object.new()
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
+ if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
if (can_be_builtin) {
- p_identifier->set_datatype(make_builtin_meta_type(GDScriptParser::get_builtin_type(name)));
+ p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
return;
} else {
push_error(R"(Builtin type cannot be used as a name on its own.)", p_identifier);
@@ -2166,10 +2303,34 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
return;
}
+ // Try singletons.
+ // Do this before globals because this might be a singleton loading another one before it's compiled.
+ if (ProjectSettings::get_singleton()->has_autoload(name)) {
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(name);
+ if (autoload.is_singleton) {
+ // Singleton exists, so it's at least a Node.
+ GDScriptParser::DataType result;
+ result.kind = GDScriptParser::DataType::NATIVE;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
+ Ref<GDScriptParserRef> parser = get_parser_for(autoload.path);
+ if (parser.is_valid()) {
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err == OK) {
+ result = type_from_metatype(parser->get_parser()->head->get_datatype());
+ }
+ }
+ }
+ result.is_constant = true;
+ p_identifier->set_datatype(result);
+ return;
+ }
+ }
+
if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[name];
Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- p_identifier->set_datatype(type_from_variant(constant));
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
p_identifier->is_constant = true;
p_identifier->reduced_value = constant;
return;
@@ -2177,7 +2338,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
- p_identifier->set_datatype(type_from_variant(constant));
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
p_identifier->is_constant = true;
p_identifier->reduced_value = constant;
return;
@@ -2199,13 +2360,44 @@ void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
p_literal->reduced_value = p_literal->value;
p_literal->is_constant = true;
- p_literal->set_datatype(type_from_variant(p_literal->reduced_value));
+ p_literal->set_datatype(type_from_variant(p_literal->reduced_value, p_literal));
}
void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
+ if (!p_preload->path) {
+ return;
+ }
+
+ reduce_expression(p_preload->path);
+
+ if (!p_preload->path->is_constant) {
+ push_error("Preloaded path must be a constant string.", p_preload->path);
+ return;
+ }
+
+ if (p_preload->path->reduced_value.get_type() != Variant::STRING) {
+ push_error("Preloaded path must be a constant string.", p_preload->path);
+ } else {
+ p_preload->resolved_path = p_preload->path->reduced_value;
+ // TODO: Save this as script dependency.
+ if (p_preload->resolved_path.is_rel_path()) {
+ p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path);
+ }
+ p_preload->resolved_path = p_preload->resolved_path.simplify_path();
+ if (!FileAccess::exists(p_preload->resolved_path)) {
+ push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ } else {
+ // TODO: Don't load if validating: use completion cache.
+ p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
+ if (p_preload->resource.is_null()) {
+ push_error(vformat(R"(Could not p_preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ }
+ }
+
p_preload->is_constant = true;
p_preload->reduced_value = p_preload->resource;
- p_preload->set_datatype(type_from_variant(p_preload->reduced_value));
+ p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload));
}
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
@@ -2248,13 +2440,13 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
if (p_subscript->base->is_constant) {
// Just try to get it.
bool valid = false;
- Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, &valid);
+ Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
if (!valid) {
push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
- result_type = type_from_variant(value);
+ result_type = type_from_variant(value, p_subscript);
}
result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
@@ -2294,7 +2486,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
- result_type = type_from_variant(value);
+ result_type = type_from_variant(value, p_subscript);
}
result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
@@ -2349,7 +2541,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
break;
// Don't support indexing, but we will check it later.
- case Variant::_RID:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2378,11 +2570,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
// Check resulting type if possible.
result_type.builtin_type = Variant::NIL;
result_type.kind = GDScriptParser::DataType::BUILTIN;
- result_type.type_source = GDScriptParser::DataType::INFERRED;
+ result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
switch (base_type.builtin_type) {
// Can't index at all.
- case Variant::_RID:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2509,16 +2701,22 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
GDScriptParser::DataType result;
+ if (p_unary_op->operand == nullptr) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ p_unary_op->set_datatype(result);
+ return;
+ }
+
if (p_unary_op->operand->is_constant) {
p_unary_op->is_constant = true;
p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant());
- result = type_from_variant(p_unary_op->reduced_value);
+ result = type_from_variant(p_unary_op->reduced_value, p_unary_op);
} else if (p_unary_op->operand->get_datatype().is_variant()) {
result.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_unary_op);
} else {
bool valid = false;
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid);
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
if (!valid) {
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
@@ -2528,7 +2726,47 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value) {
+void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ GDScriptParser::ExpressionNode *element = p_array->elements[i];
+ all_is_constant = all_is_constant && element->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Array array;
+ array.resize(p_array->elements.size());
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ array[i] = p_array->elements[i]->reduced_value;
+ }
+ p_array->is_constant = true;
+ p_array->reduced_value = array;
+}
+
+void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Dictionary dict;
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ dict[element.key->reduced_value] = element.value->reduced_value;
+ }
+ p_dictionary->is_constant = true;
+ p_dictionary->reduced_value = dict;
+}
+
+GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
result.kind = GDScriptParser::DataType::BUILTIN;
@@ -2550,19 +2788,44 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
scr = obj->get_script();
}
if (scr.is_valid()) {
- result.script_type = scr;
- result.script_path = scr->get_path();
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- result.kind = GDScriptParser::DataType::CLASS;
- Ref<GDScriptParserRef> ref = get_parser_for(gds->get_path());
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- result.class_type = ref->get_parser()->head;
- result.script_path = ref->get_parser()->script_path;
+ if (scr->is_valid()) {
+ result.script_type = scr;
+ result.script_path = scr->get_path();
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ result.kind = GDScriptParser::DataType::CLASS;
+ // This might be an inner class, so we want to get the parser for the root.
+ // But still get the inner class from that tree.
+ GDScript *current = gds.ptr();
+ List<StringName> class_chain;
+ while (current->_owner) {
+ // Push to front so it's in reverse.
+ class_chain.push_front(current->name);
+ current = current->_owner;
+ }
+
+ Ref<GDScriptParserRef> ref = get_parser_for(current->path);
+ ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+
+ GDScriptParser::ClassNode *found = ref->get_parser()->head;
+
+ // It should be okay to assume this exists, since we have a complete script already.
+ for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
+ found = found->get_member(E->get()).m_class;
+ }
+
+ result.class_type = found;
+ result.script_path = ref->get_parser()->script_path;
+ } else {
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ }
+ result.native_type = scr->get_instance_base_type();
} else {
- result.kind = GDScriptParser::DataType::SCRIPT;
+ push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
+ result.kind = GDScriptParser::DataType::VARIANT;
+ result.type_source = GDScriptParser::DataType::UNDETECTED;
+ result.is_meta_type = false;
}
- result.native_type = scr->get_instance_base_type();
} else {
result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) {
@@ -2608,7 +2871,8 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
- Variant dummy = Variant::construct(p_base_type.builtin_type, nullptr, 0, err);
+ Variant dummy;
+ Variant::construct(p_base_type.builtin_type, dummy, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Could not construct base Variant type.");
}
@@ -2753,7 +3017,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
if (arg_type.is_variant()) {
// Argument can be anything, so this is unsafe.
mark_node_unsafe(p_call->arguments[i]);
- } else if (!is_type_compatible(par_type, arg_type, true)) {
+ } else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
mark_node_unsafe(p_call);
if (!is_type_compatible(arg_type, par_type)) {
@@ -2818,7 +3082,7 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
}
#endif
-GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid) {
+GDScriptParser::DataType GDScriptAnalyzer::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) {
// This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
GDScriptParser::DataType result;
@@ -2834,7 +3098,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
a = a_ref;
} else {
Callable::CallError err;
- a = Variant::construct(a_type, nullptr, 0, err);
+ Variant::construct(a_type, a, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
r_valid = false;
ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
@@ -2847,7 +3111,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
b = b_ref;
} else {
Callable::CallError err;
- b = Variant::construct(b_type, nullptr, 0, err);
+ Variant::construct(b_type, b, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
r_valid = false;
ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
@@ -2883,7 +3147,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
// Avoid error in formatting operator (%) where it doesn't find a placeholder.
- if (a_type == Variant::STRING) {
+ if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
a = String("%s");
}
@@ -2891,7 +3155,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::evaluate(p_operation, a, b, ret, r_valid);
if (r_valid) {
- return type_from_variant(ret);
+ return type_from_variant(ret, p_source);
}
return result;
@@ -3089,6 +3353,9 @@ Error GDScriptAnalyzer::resolve_program() {
List<String> parser_keys;
depended_parsers.get_key_list(&parser_keys);
for (const List<String>::Element *E = parser_keys.front(); E != nullptr; E = E->next()) {
+ if (depended_parsers[E->get()].is_null()) {
+ return ERR_PARSE_ERROR;
+ }
depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
depended_parsers.clear();
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 06d3530cb6..0a952cc621 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -31,9 +31,9 @@
#ifndef GDSCRIPT_ANALYZER_H
#define GDSCRIPT_ANALYZER_H
-#include "core/object.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/object/object.h"
+#include "core/object/reference.h"
+#include "core/templates/set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
@@ -41,6 +41,8 @@ class GDScriptAnalyzer {
GDScriptParser *parser = nullptr;
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
+ const GDScriptParser::EnumNode *current_enum = nullptr;
+
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
@@ -65,7 +67,7 @@ class GDScriptAnalyzer {
void resolve_match(GDScriptParser::MatchNode *p_match);
void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test);
void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test);
- void resolve_pararameter(GDScriptParser::ParameterNode *p_parameter);
+ void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
void resolve_return(GDScriptParser::ReturnNode *p_return);
// Reduction functions.
@@ -87,8 +89,11 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
+ void const_fold_array(GDScriptParser::ArrayNode *p_array);
+ void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+
// Helpers.
- GDScriptParser::DataType type_from_variant(const Variant &p_value);
+ GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name);
@@ -96,7 +101,7 @@ class GDScriptAnalyzer {
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
- GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid);
+ 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);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
new file mode 100644
index 0000000000..cc9e87b882
--- /dev/null
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -0,0 +1,739 @@
+/*************************************************************************/
+/* gdscript_byte_codegen.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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_byte_codegen.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "gdscript.h"
+
+uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) {
+#ifdef TOOLS_ENABLED
+ function->arg_names.push_back(p_name);
+#endif
+ function->_argument_count++;
+ function->argument_types.push_back(p_type);
+ if (p_is_optional) {
+ if (function->_default_arg_count == 0) {
+ append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ }
+ function->default_arguments.push_back(opcodes.size());
+ function->_default_arg_count++;
+ }
+
+ return add_local(p_name, p_type);
+}
+
+uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) {
+ int stack_pos = increase_stack();
+ add_stack_identifier(p_name, stack_pos);
+ return stack_pos;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_local_constant(const StringName &p_name, const Variant &p_constant) {
+ int index = add_or_get_constant(p_constant);
+ local_constants[p_name] = index;
+ return index;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_or_get_constant(const Variant &p_constant) {
+ if (constant_map.has(p_constant)) {
+ return constant_map[p_constant];
+ }
+ int index = constant_map.size();
+ constant_map[p_constant] = index;
+ return index;
+}
+
+uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) {
+ return get_name_map_pos(p_name);
+}
+
+uint32_t GDScriptByteCodeGenerator::add_temporary() {
+ current_temporaries++;
+ return increase_stack();
+}
+
+void GDScriptByteCodeGenerator::pop_temporary() {
+ current_stack_size--;
+ current_temporaries--;
+}
+
+void GDScriptByteCodeGenerator::start_parameters() {}
+
+void GDScriptByteCodeGenerator::end_parameters() {
+ function->default_arguments.invert();
+}
+
+void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) {
+ function = memnew(GDScriptFunction);
+ debug_stack = EngineDebugger::is_active();
+
+ function->name = p_function_name;
+ function->_script = p_script;
+ function->source = p_script->get_path();
+
+#ifdef DEBUG_ENABLED
+ function->func_cname = (String(function->source) + " - " + String(p_function_name)).utf8();
+ function->_func_cname = function->func_cname.get_data();
+#endif
+
+ function->_static = p_static;
+ function->return_type = p_return_type;
+ function->rpc_mode = p_rpc_mode;
+ function->_argument_count = 0;
+}
+
+GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
+ append(GDScriptFunction::OPCODE_END);
+
+ if (constant_map.size()) {
+ function->_constant_count = constant_map.size();
+ function->constants.resize(constant_map.size());
+ function->_constants_ptr = function->constants.ptrw();
+ const Variant *K = nullptr;
+ while ((K = constant_map.next(K))) {
+ int idx = constant_map[*K];
+ function->constants.write[idx] = *K;
+ }
+ } else {
+ function->_constants_ptr = nullptr;
+ function->_constant_count = 0;
+ }
+
+ if (name_map.size()) {
+ function->global_names.resize(name_map.size());
+ function->_global_names_ptr = &function->global_names[0];
+ for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
+ function->global_names.write[E->get()] = E->key();
+ }
+ function->_global_names_count = function->global_names.size();
+
+ } else {
+ function->_global_names_ptr = nullptr;
+ function->_global_names_count = 0;
+ }
+
+ if (opcodes.size()) {
+ function->code = opcodes;
+ function->_code_ptr = &function->code[0];
+ function->_code_size = opcodes.size();
+
+ } else {
+ function->_code_ptr = nullptr;
+ function->_code_size = 0;
+ }
+
+ if (function->default_arguments.size()) {
+ function->_default_arg_count = function->default_arguments.size();
+ function->_default_arg_ptr = &function->default_arguments[0];
+ } else {
+ function->_default_arg_count = 0;
+ function->_default_arg_ptr = nullptr;
+ }
+
+ if (debug_stack) {
+ function->stack_debug = stack_debug;
+ }
+ function->_stack_size = stack_max;
+ function->_call_size = call_max;
+
+ ended = true;
+ return function;
+}
+
+#ifdef DEBUG_ENABLED
+void GDScriptByteCodeGenerator::set_signature(const String &p_signature) {
+ function->profile.signature = p_signature;
+}
+#endif
+
+void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
+ function->_initial_line = p_line;
+}
+
+void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_OPERATOR);
+ append(p_operator);
+ append(p_left_operand);
+ append(p_right_operand);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
+ append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+ append(p_source);
+ append(p_type);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
+ append(GDScriptFunction::OPCODE_IS_BUILTIN);
+ append(p_source);
+ append(p_type);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_left_operand);
+ logic_op_jump_pos1.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_right_operand);
+ logic_op_jump_pos2.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
+ // If here means both operands are true.
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+ // Jump away from the fail condition.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 3);
+ // Here it means one of operands is false.
+ patch_jump(logic_op_jump_pos1.back()->get());
+ patch_jump(logic_op_jump_pos2.back()->get());
+ logic_op_jump_pos1.pop_back();
+ logic_op_jump_pos2.pop_back();
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(p_left_operand);
+ logic_op_jump_pos1.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
+ append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(p_right_operand);
+ logic_op_jump_pos2.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
+ // If here means both operands are false.
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+ // Jump away from the success condition.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 3);
+ // Here it means one of operands is false.
+ patch_jump(logic_op_jump_pos1.back()->get());
+ patch_jump(logic_op_jump_pos2.back()->get());
+ logic_op_jump_pos1.pop_back();
+ logic_op_jump_pos2.pop_back();
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
+ ternary_result.push_back(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ ternary_jump_fail_pos.push_back(opcodes.size());
+ append(0); // Jump target, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(ternary_result.back()->get());
+ append(p_expr);
+ // Jump away from the false path.
+ append(GDScriptFunction::OPCODE_JUMP);
+ ternary_jump_skip_pos.push_back(opcodes.size());
+ append(0);
+ // Fail must jump here.
+ patch_jump(ternary_jump_fail_pos.back()->get());
+ ternary_jump_fail_pos.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(ternary_result.back()->get());
+ append(p_expr);
+}
+
+void GDScriptByteCodeGenerator::write_end_ternary() {
+ patch_jump(ternary_jump_skip_pos.back()->get());
+ ternary_jump_skip_pos.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_SET);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+}
+
+void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_GET);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_SET_NAMED);
+ append(p_target);
+ append(p_name);
+ append(p_source);
+}
+
+void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
+ append(GDScriptFunction::OPCODE_GET_NAMED);
+ append(p_source);
+ append(p_name);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
+ append(GDScriptFunction::OPCODE_SET_MEMBER);
+ append(p_name);
+ append(p_value);
+}
+
+void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
+ append(GDScriptFunction::OPCODE_GET_MEMBER);
+ append(p_name);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
+ if (p_target.type.has_type && !p_source.type.has_type) {
+ // Typed assignment.
+ switch (p_target.type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
+ append(p_target.type.builtin_type);
+ append(p_target);
+ append(p_source);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
+ append(class_idx);
+ append(p_target);
+ append(p_source);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_target.type.script_type;
+ int idx = get_constant_pos(script);
+
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
+ append(idx);
+ append(p_target);
+ append(p_source);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(p_target);
+ append(p_source);
+ }
+ }
+ } else {
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+ // Need conversion..
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
+ append(p_target.type.builtin_type);
+ append(p_target);
+ append(p_source);
+ } else {
+ // Either untyped assignment or already type-checked by the parser
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(p_target);
+ append(p_source);
+ }
+ }
+}
+
+void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+ switch (p_type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
+ append(p_type.builtin_type);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
+ class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_type.script_type;
+ int idx = get_constant_pos(script);
+
+ append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
+ append(idx);
+ } break;
+ default: {
+ return;
+ }
+ }
+
+ append(p_source);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
+ append(p_function_name);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_ASYNC);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
+ append(p_function);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_method->get_name());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_method->get_name());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_arguments.size());
+ append(p_base);
+ append(p_function_name);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT);
+ append(p_type);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ alloc_call(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
+ append(p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
+ append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
+ append(GDScriptFunction::OPCODE_AWAIT);
+ append(p_operand);
+ append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ if_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_else() {
+ append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+ int else_jmp_addr = opcodes.size();
+ append(0); // Jump destination, will be patched.
+
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+ if_jmp_addrs.push_back(else_jmp_addr);
+}
+
+void GDScriptByteCodeGenerator::write_endif() {
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) {
+ int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+
+ current_breaks_to_patch.push_back(List<int>());
+
+ // Assign container.
+ append(GDScriptFunction::OPCODE_ASSIGN);
+ append(container_pos);
+ append(p_list);
+
+ // Begin loop.
+ append(GDScriptFunction::OPCODE_ITERATE_BEGIN);
+ append(counter_pos);
+ append(container_pos);
+ for_jmp_addrs.push_back(opcodes.size());
+ append(0); // End of loop address, will be patched.
+ append(p_variable);
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(opcodes.size() + 6); // Skip over 'continue' code.
+
+ // Next iteration.
+ int continue_addr = opcodes.size();
+ continue_addrs.push_back(continue_addr);
+ append(GDScriptFunction::OPCODE_ITERATE);
+ append(counter_pos);
+ append(container_pos);
+ for_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+ append(p_variable);
+}
+
+void GDScriptByteCodeGenerator::write_endfor() {
+ // Jump back to loop check.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+ continue_addrs.pop_back();
+
+ // Patch end jumps (two of them).
+ for (int i = 0; i < 2; i++) {
+ patch_jump(for_jmp_addrs.back()->get());
+ for_jmp_addrs.pop_back();
+ }
+
+ // Patch break statements.
+ for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ current_breaks_to_patch.pop_back();
+
+ // Remove loop temporaries.
+ pop_temporary();
+ pop_temporary();
+}
+
+void GDScriptByteCodeGenerator::start_while_condition() {
+ current_breaks_to_patch.push_back(List<int>());
+ continue_addrs.push_back(opcodes.size());
+}
+
+void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
+ // Condition check.
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(p_condition);
+ while_jmp_addrs.push_back(opcodes.size());
+ append(0); // End of loop address, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_endwhile() {
+ // Jump back to loop check.
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+ continue_addrs.pop_back();
+
+ // Patch end jump.
+ patch_jump(while_jmp_addrs.back()->get());
+ while_jmp_addrs.pop_back();
+
+ // Patch break statements.
+ for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ current_breaks_to_patch.pop_back();
+}
+
+void GDScriptByteCodeGenerator::start_match() {
+ match_continues_to_patch.push_back(List<int>());
+}
+
+void GDScriptByteCodeGenerator::start_match_branch() {
+ // Patch continue statements.
+ for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ match_continues_to_patch.pop_back();
+ // Start a new list for next branch.
+ match_continues_to_patch.push_back(List<int>());
+}
+
+void GDScriptByteCodeGenerator::end_match() {
+ // Patch continue statements.
+ for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
+ patch_jump(E->get());
+ }
+ match_continues_to_patch.pop_back();
+}
+
+void GDScriptByteCodeGenerator::write_break() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ current_breaks_to_patch.back()->get().push_back(opcodes.size());
+ append(0);
+}
+
+void GDScriptByteCodeGenerator::write_continue() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ append(continue_addrs.back()->get());
+}
+
+void GDScriptByteCodeGenerator::write_continue_match() {
+ append(GDScriptFunction::OPCODE_JUMP);
+ match_continues_to_patch.back()->get().push_back(opcodes.size());
+ append(0);
+}
+
+void GDScriptByteCodeGenerator::write_breakpoint() {
+ append(GDScriptFunction::OPCODE_BREAKPOINT);
+}
+
+void GDScriptByteCodeGenerator::write_newline(int p_line) {
+ append(GDScriptFunction::OPCODE_LINE);
+ append(p_line);
+ current_line = p_line;
+}
+
+void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
+ append(GDScriptFunction::OPCODE_RETURN);
+ append(p_return_value);
+}
+
+void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
+ append(GDScriptFunction::OPCODE_ASSERT);
+ append(p_test);
+ append(p_message);
+}
+
+void GDScriptByteCodeGenerator::start_block() {
+ push_stack_identifiers();
+}
+
+void GDScriptByteCodeGenerator::end_block() {
+ pop_stack_identifiers();
+}
+
+GDScriptByteCodeGenerator::~GDScriptByteCodeGenerator() {
+ if (!ended && function != nullptr) {
+ memdelete(function);
+ }
+}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
new file mode 100644
index 0000000000..62438b6dd2
--- /dev/null
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -0,0 +1,277 @@
+/*************************************************************************/
+/* gdscript_byte_codegen.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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_BYTE_CODEGEN
+#define GDSCRIPT_BYTE_CODEGEN
+
+#include "gdscript_codegen.h"
+
+class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
+ bool ended = false;
+ GDScriptFunction *function = nullptr;
+ bool debug_stack = false;
+
+ Vector<int> opcodes;
+ List<Map<StringName, int>> stack_id_stack;
+ Map<StringName, int> stack_identifiers;
+ Map<StringName, int> local_constants;
+
+ List<GDScriptFunction::StackDebug> stack_debug;
+ List<Map<StringName, int>> block_identifier_stack;
+ Map<StringName, int> block_identifiers;
+
+ int current_stack_size = 0;
+ int current_temporaries = 0;
+
+ HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
+ Map<StringName, int> name_map;
+#ifdef TOOLS_ENABLED
+ Vector<StringName> named_globals;
+#endif
+ int current_line = 0;
+ int stack_max = 0;
+ int call_max = 0;
+
+ List<int> if_jmp_addrs; // List since this can be nested.
+ List<int> for_jmp_addrs;
+ List<int> while_jmp_addrs;
+ List<int> continue_addrs;
+
+ // Used to patch jumps with `and` and `or` operators with short-circuit.
+ List<int> logic_op_jump_pos1;
+ List<int> logic_op_jump_pos2;
+
+ List<Address> ternary_result;
+ List<int> ternary_jump_fail_pos;
+ List<int> ternary_jump_skip_pos;
+
+ List<List<int>> current_breaks_to_patch;
+ List<List<int>> match_continues_to_patch;
+
+ void add_stack_identifier(const StringName &p_id, int p_stackpos) {
+ stack_identifiers[p_id] = p_stackpos;
+ if (debug_stack) {
+ block_identifiers[p_id] = p_stackpos;
+ GDScriptFunction::StackDebug sd;
+ sd.added = true;
+ sd.line = current_line;
+ sd.identifier = p_id;
+ sd.pos = p_stackpos;
+ stack_debug.push_back(sd);
+ }
+ }
+
+ void push_stack_identifiers() {
+ stack_id_stack.push_back(stack_identifiers);
+ if (debug_stack) {
+ block_identifier_stack.push_back(block_identifiers);
+ block_identifiers.clear();
+ }
+ }
+
+ void pop_stack_identifiers() {
+ stack_identifiers = stack_id_stack.back()->get();
+ current_stack_size = stack_identifiers.size() + current_temporaries;
+ stack_id_stack.pop_back();
+
+ if (debug_stack) {
+ for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
+ GDScriptFunction::StackDebug sd;
+ sd.added = false;
+ sd.identifier = E->key();
+ sd.line = current_line;
+ sd.pos = E->get();
+ stack_debug.push_back(sd);
+ }
+ block_identifiers = block_identifier_stack.back()->get();
+ block_identifier_stack.pop_back();
+ }
+ }
+
+ int get_name_map_pos(const StringName &p_identifier) {
+ int ret;
+ if (!name_map.has(p_identifier)) {
+ ret = name_map.size();
+ name_map[p_identifier] = ret;
+ } else {
+ ret = name_map[p_identifier];
+ }
+ return ret;
+ }
+
+ int get_constant_pos(const Variant &p_constant) {
+ if (constant_map.has(p_constant))
+ return constant_map[p_constant];
+ int pos = constant_map.size();
+ constant_map[p_constant] = pos;
+ return pos;
+ }
+
+ void alloc_stack(int p_level) {
+ if (p_level >= stack_max)
+ stack_max = p_level + 1;
+ }
+
+ void alloc_call(int p_params) {
+ if (p_params >= call_max)
+ call_max = p_params;
+ }
+
+ int increase_stack() {
+ int top = current_stack_size++;
+ alloc_stack(current_stack_size);
+ return top;
+ }
+
+ int address_of(const Address &p_address) {
+ switch (p_address.mode) {
+ case Address::SELF:
+ return GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS;
+ case Address::CLASS:
+ return GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS;
+ case Address::MEMBER:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
+ case Address::CLASS_CONSTANT:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS);
+ case Address::LOCAL_CONSTANT:
+ case Address::CONSTANT:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ case Address::LOCAL_VARIABLE:
+ case Address::TEMPORARY:
+ case Address::FUNCTION_PARAMETER:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ case Address::GLOBAL:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
+ case Address::NAMED_GLOBAL:
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ case Address::NIL:
+ return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
+ }
+ return -1; // Unreachable.
+ }
+
+ void append(int code) {
+ opcodes.push_back(code);
+ }
+
+ void append(const Address &p_address) {
+ opcodes.push_back(address_of(p_address));
+ }
+
+ void append(const StringName &p_name) {
+ opcodes.push_back(get_name_map_pos(p_name));
+ }
+
+ void patch_jump(int p_address) {
+ opcodes.write[p_address] = opcodes.size();
+ }
+
+public:
+ virtual uint32_t add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) override;
+ virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) override;
+ virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) override;
+ virtual uint32_t add_or_get_constant(const Variant &p_constant) override;
+ virtual uint32_t add_or_get_name(const StringName &p_name) override;
+ virtual uint32_t add_temporary() override;
+ virtual void pop_temporary() override;
+
+ virtual void start_parameters() override;
+ virtual void end_parameters() override;
+
+ virtual void start_block() override;
+ virtual void end_block() override;
+
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) override;
+ virtual GDScriptFunction *write_end() override;
+
+#ifdef DEBUG_ENABLED
+ virtual void set_signature(const String &p_signature) override;
+#endif
+ virtual void set_initial_line(int p_line) override;
+
+ virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
+ virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override;
+ virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override;
+ virtual void write_and_left_operand(const Address &p_left_operand) override;
+ virtual void write_and_right_operand(const Address &p_right_operand) override;
+ virtual void write_end_and(const Address &p_target) override;
+ virtual void write_or_left_operand(const Address &p_left_operand) override;
+ virtual void write_or_right_operand(const Address &p_right_operand) override;
+ virtual void write_end_or(const Address &p_target) override;
+ virtual void write_start_ternary(const Address &p_target) override;
+ virtual void write_ternary_condition(const Address &p_condition) override;
+ virtual void write_ternary_true_expr(const Address &p_expr) override;
+ virtual void write_ternary_false_expr(const Address &p_expr) override;
+ virtual void write_end_ternary() override;
+ virtual void write_set(const Address &p_target, const Address &p_index, const Address &p_source) override;
+ virtual void write_get(const Address &p_target, const Address &p_index, const Address &p_source) override;
+ virtual void write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) override;
+ virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) override;
+ virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
+ virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
+ virtual void write_assign(const Address &p_target, const Address &p_source) override;
+ virtual void write_assign_true(const Address &p_target) override;
+ virtual void write_assign_false(const Address &p_target) override;
+ virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
+ virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
+ virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
+ virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
+ virtual void write_await(const Address &p_target, const Address &p_operand) override;
+ virtual void write_if(const Address &p_condition) override;
+ virtual void write_else() override;
+ virtual void write_endif() override;
+ virtual void write_for(const Address &p_variable, const Address &p_list) override;
+ virtual void write_endfor() override;
+ virtual void start_while_condition() override;
+ virtual void write_while(const Address &p_condition) override;
+ virtual void write_endwhile() override;
+ virtual void start_match() override;
+ virtual void start_match_branch() override;
+ virtual void end_match() override;
+ virtual void write_break() override;
+ virtual void write_continue() override;
+ virtual void write_continue_match() override;
+ virtual void write_breakpoint() override;
+ virtual void write_newline(int p_line) override;
+ virtual void write_return(const Address &p_return_value) override;
+ virtual void write_assert(const Address &p_test, const Address &p_message) override;
+
+ virtual ~GDScriptByteCodeGenerator();
+};
+
+#endif // GDSCRIPT_BYTE_CODEGEN
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 583283ff46..95d24a8b08 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -31,7 +31,7 @@
#include "gdscript_cache.h"
#include "core/os/file_access.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_parser.h"
@@ -85,6 +85,17 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
return result;
}
}
+ if (result != OK) {
+ if (parser != nullptr) {
+ memdelete(parser);
+ parser = nullptr;
+ }
+ if (analyzer != nullptr) {
+ memdelete(analyzer);
+ analyzer = nullptr;
+ }
+ return result;
+ }
}
return result;
@@ -97,33 +108,36 @@ GDScriptParserRef::~GDScriptParserRef() {
if (analyzer != nullptr) {
memdelete(analyzer);
}
- MutexLock(GDScriptCache::singleton->lock);
+ MutexLock lock(GDScriptCache::singleton->lock);
GDScriptCache::singleton->parser_map.erase(path);
}
GDScriptCache *GDScriptCache::singleton = nullptr;
void GDScriptCache::remove_script(const String &p_path) {
- MutexLock(singleton->lock);
+ MutexLock lock(singleton->lock);
singleton->shallow_gdscript_cache.erase(p_path);
singleton->full_gdscript_cache.erase(p_path);
}
Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) {
- MutexLock(singleton->lock);
+ MutexLock lock(singleton->lock);
Ref<GDScriptParserRef> ref;
if (p_owner != String()) {
singleton->dependencies[p_owner].insert(p_path);
}
if (singleton->parser_map.has(p_path)) {
- ref = singleton->parser_map[p_path];
+ ref = Ref<GDScriptParserRef>(singleton->parser_map[p_path]);
} else {
+ if (!FileAccess::exists(p_path)) {
+ r_error = ERR_FILE_NOT_FOUND;
+ return ref;
+ }
GDScriptParser *parser = memnew(GDScriptParser);
ref.instance();
ref->parser = parser;
ref->path = p_path;
- singleton->parser_map[p_path] = ref;
- ref->unreference();
+ singleton->parser_map[p_path] = ref.ptr();
}
r_error = ref->raise_status(p_status);
@@ -154,7 +168,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
}
Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) {
- MutexLock(singleton->lock);
+ MutexLock lock(singleton->lock);
if (p_owner != String()) {
singleton->dependencies[p_owner].insert(p_path);
}
@@ -171,15 +185,12 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
script->set_script_path(p_path);
script->load_source_code(p_path);
- singleton->shallow_gdscript_cache[p_path] = script;
- // The one in cache is not a hard reference: if the script dies somewhere else it's fine.
- // Scripts remove themselves from cache when they die.
- script->unreference();
+ singleton->shallow_gdscript_cache[p_path] = script.ptr();
return script;
}
Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner) {
- MutexLock(singleton->lock);
+ MutexLock lock(singleton->lock);
if (p_owner != String()) {
singleton->dependencies[p_owner].insert(p_path);
@@ -202,7 +213,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
return script;
}
- singleton->full_gdscript_cache[p_path] = script;
+ singleton->full_gdscript_cache[p_path] = script.ptr();
singleton->shallow_gdscript_cache.erase(p_path);
return script;
@@ -211,7 +222,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
Error GDScriptCache::finish_compiling(const String &p_owner) {
// Mark this as compiled.
Ref<GDScript> script = get_shallow_script(p_owner);
- singleton->full_gdscript_cache[p_owner] = script;
+ singleton->full_gdscript_cache[p_owner] = script.ptr();
singleton->shallow_gdscript_cache.erase(p_owner);
Set<String> depends = singleton->dependencies[p_owner];
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 770704d6eb..90c5884985 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -31,10 +31,10 @@
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
-#include "core/hash_map.h"
+#include "core/object/reference.h"
#include "core/os/mutex.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/set.h"
#include "gdscript.h"
class GDScriptAnalyzer;
@@ -70,9 +70,9 @@ public:
class GDScriptCache {
// String key is full path.
- HashMap<String, Ref<GDScriptParserRef>> parser_map;
- HashMap<String, Ref<GDScript>> shallow_gdscript_cache;
- HashMap<String, Ref<GDScript>> full_gdscript_cache;
+ HashMap<String, GDScriptParserRef *> parser_map;
+ HashMap<String, GDScript *> shallow_gdscript_cache;
+ HashMap<String, GDScript *> full_gdscript_cache;
HashMap<String, Set<String>> dependencies;
friend class GDScript;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
new file mode 100644
index 0000000000..9872a61423
--- /dev/null
+++ b/modules/gdscript/gdscript_codegen.h
@@ -0,0 +1,160 @@
+/*************************************************************************/
+/* gdscript_codegen.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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_CODEGEN
+#define GDSCRIPT_CODEGEN
+
+#include "core/io/multiplayer_api.h"
+#include "core/string/string_name.h"
+#include "core/variant/variant.h"
+#include "gdscript_function.h"
+#include "gdscript_functions.h"
+
+class GDScriptCodeGenerator {
+public:
+ struct Address {
+ enum AddressMode {
+ SELF,
+ CLASS,
+ MEMBER,
+ CONSTANT,
+ CLASS_CONSTANT,
+ LOCAL_CONSTANT,
+ LOCAL_VARIABLE,
+ FUNCTION_PARAMETER,
+ TEMPORARY,
+ GLOBAL,
+ NAMED_GLOBAL,
+ NIL,
+ };
+ AddressMode mode = NIL;
+ uint32_t address = 0;
+ GDScriptDataType type;
+
+ Address() {}
+ Address(AddressMode p_mode, const GDScriptDataType &p_type = GDScriptDataType()) {
+ mode = p_mode;
+ type = p_type;
+ }
+ Address(AddressMode p_mode, uint32_t p_address, const GDScriptDataType &p_type = GDScriptDataType()) {
+ mode = p_mode,
+ address = p_address;
+ type = p_type;
+ }
+ };
+
+ virtual uint32_t add_parameter(const StringName &p_name, bool p_is_optional, const GDScriptDataType &p_type) = 0;
+ virtual uint32_t add_local(const StringName &p_name, const GDScriptDataType &p_type) = 0;
+ virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) = 0;
+ virtual uint32_t add_or_get_constant(const Variant &p_constant) = 0;
+ virtual uint32_t add_or_get_name(const StringName &p_name) = 0;
+ virtual uint32_t add_temporary() = 0;
+ virtual void pop_temporary() = 0;
+
+ virtual void start_parameters() = 0;
+ virtual void end_parameters() = 0;
+
+ virtual void start_block() = 0;
+ virtual void end_block() = 0;
+
+ // virtual int get_max_stack_level() = 0;
+ // virtual int get_max_function_arguments() = 0;
+
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, const GDScriptDataType &p_return_type) = 0;
+ virtual GDScriptFunction *write_end() = 0;
+
+#ifdef DEBUG_ENABLED
+ virtual void set_signature(const String &p_signature) = 0;
+#endif
+ virtual void set_initial_line(int p_line) = 0;
+
+ // virtual void alloc_stack(int p_level) = 0; // Is this needed?
+ // virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions.
+
+ virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
+ virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0;
+ virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0;
+ virtual void write_and_left_operand(const Address &p_left_operand) = 0;
+ virtual void write_and_right_operand(const Address &p_right_operand) = 0;
+ virtual void write_end_and(const Address &p_target) = 0;
+ virtual void write_or_left_operand(const Address &p_left_operand) = 0;
+ virtual void write_or_right_operand(const Address &p_right_operand) = 0;
+ virtual void write_end_or(const Address &p_target) = 0;
+ virtual void write_start_ternary(const Address &p_target) = 0;
+ virtual void write_ternary_condition(const Address &p_condition) = 0;
+ virtual void write_ternary_true_expr(const Address &p_expr) = 0;
+ virtual void write_ternary_false_expr(const Address &p_expr) = 0;
+ virtual void write_end_ternary() = 0;
+ virtual void write_set(const Address &p_target, const Address &p_index, const Address &p_source) = 0;
+ virtual void write_get(const Address &p_target, const Address &p_index, const Address &p_source) = 0;
+ virtual void write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) = 0;
+ virtual void write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) = 0;
+ virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
+ virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
+ virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
+ virtual void write_assign_true(const Address &p_target) = 0;
+ virtual void write_assign_false(const Address &p_target) = 0;
+ virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
+ virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+ virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
+ virtual void write_if(const Address &p_condition) = 0;
+ // virtual void write_elseif(const Address &p_condition) = 0; This kind of makes things more difficult for no real benefit.
+ virtual void write_else() = 0;
+ virtual void write_endif() = 0;
+ virtual void write_for(const Address &p_variable, const Address &p_list) = 0;
+ virtual void write_endfor() = 0;
+ virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
+ virtual void write_while(const Address &p_condition) = 0;
+ virtual void write_endwhile() = 0;
+ virtual void start_match() = 0;
+ virtual void start_match_branch() = 0;
+ virtual void end_match() = 0;
+ virtual void write_break() = 0;
+ virtual void write_continue() = 0;
+ virtual void write_continue_match() = 0;
+ virtual void write_breakpoint() = 0;
+ virtual void write_newline(int p_line) = 0;
+ virtual void write_return(const Address &p_return_value) = 0;
+ virtual void write_assert(const Address &p_test, const Address &p_message) = 0;
+
+ virtual ~GDScriptCodeGenerator() {}
+};
+
+#endif // GDSCRIPT_CODEGEN
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index e34d87f5cc..a64a05fcba 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -31,6 +31,7 @@
#include "gdscript_compiler.h"
#include "gdscript.h"
+#include "gdscript_byte_codegen.h"
#include "gdscript_cache.h"
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
@@ -38,7 +39,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.stack_identifiers.has(p_name)) {
+ if (codegen.locals.has(p_name)) {
return false; //shadowed
}
@@ -75,46 +76,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
}
}
-bool GDScriptCompiler::_create_unary_operator(CodeGen &codegen, const GDScriptParser::UnaryOpNode *on, Variant::Operator op, int p_stack_level) {
- int src_address_a = _parse_expression(codegen, on->operand, p_stack_level);
- if (src_address_a < 0) {
- return false;
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR); // perform operator
- codegen.opcodes.push_back(op); //which operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_a); // argument 2 (repeated)
- //codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter)
- return true;
-}
-
-bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
- int src_address_a = _parse_expression(codegen, p_left_operand, p_stack_level, false, p_initializer, p_index_addr);
- if (src_address_a < 0) {
- return false;
- }
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- p_stack_level++; //uses stack for return, increase stack
- }
-
- int src_address_b = _parse_expression(codegen, p_right_operand, p_stack_level, false, p_initializer);
- if (src_address_b < 0) {
- return false;
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR); // perform operator
- codegen.opcodes.push_back(op); //which operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
- return true;
-}
-
-bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, int p_stack_level, bool p_initializer, int p_index_addr) {
- return _create_binary_operator(codegen, on->left_operand, on->right_operand, op, p_stack_level, p_initializer, p_index_addr);
-}
-
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) const {
if (!p_datatype.is_set() || !p_datatype.is_hard_type()) {
return GDScriptDataType();
}
@@ -136,7 +98,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type = p_datatype.script_type;
+ result.script_type = Ref<Script>(p_datatype.script_type).ptr();
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
@@ -162,11 +124,11 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
names.pop_back();
}
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = script;
+ result.script_type = script.ptr();
result.native_type = script->get_instance_base_type();
} else {
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr();
result.native_type = p_datatype.native_type;
}
}
@@ -187,202 +149,73 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
}
- return result;
-}
-
-int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::AssignmentNode *p_assignment, int p_stack_level, int p_index_addr) {
- Variant::Operator var_op = Variant::OP_MAX;
-
- switch (p_assignment->operation) {
- case GDScriptParser::AssignmentNode::OP_ADDITION:
- var_op = Variant::OP_ADD;
- break;
- case GDScriptParser::AssignmentNode::OP_SUBTRACTION:
- var_op = Variant::OP_SUBTRACT;
- break;
- case GDScriptParser::AssignmentNode::OP_MULTIPLICATION:
- var_op = Variant::OP_MULTIPLY;
- break;
- case GDScriptParser::AssignmentNode::OP_DIVISION:
- var_op = Variant::OP_DIVIDE;
- break;
- case GDScriptParser::AssignmentNode::OP_MODULO:
- var_op = Variant::OP_MODULE;
- break;
- case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_LEFT:
- var_op = Variant::OP_SHIFT_LEFT;
- break;
- case GDScriptParser::AssignmentNode::OP_BIT_SHIFT_RIGHT:
- var_op = Variant::OP_SHIFT_RIGHT;
- break;
- case GDScriptParser::AssignmentNode::OP_BIT_AND:
- var_op = Variant::OP_BIT_AND;
- break;
- case GDScriptParser::AssignmentNode::OP_BIT_OR:
- var_op = Variant::OP_BIT_OR;
- break;
- case GDScriptParser::AssignmentNode::OP_BIT_XOR:
- var_op = Variant::OP_BIT_XOR;
- break;
- case GDScriptParser::AssignmentNode::OP_NONE: {
- //none
- } break;
- default: {
- ERR_FAIL_V(-1);
- }
- }
-
- // bool initializer = p_expression->op == GDScriptParser::OperatorNode::OP_INIT_ASSIGN;
-
- if (var_op == Variant::OP_MAX) {
- return _parse_expression(codegen, p_assignment->assigned_value, p_stack_level, false, false);
+ // Only hold strong reference to the script if it's not the owner of the
+ // element qualified with this type, to avoid cyclic references (leaks).
+ if (result.script_type && result.script_type != p_owner) {
+ result.script_type_ref = Ref<Script>(result.script_type);
}
- if (!_create_binary_operator(codegen, p_assignment->assignee, p_assignment->assigned_value, var_op, p_stack_level, false, p_index_addr)) {
- return -1;
- }
-
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-}
-
-bool GDScriptCompiler::_generate_typed_assign(CodeGen &codegen, int p_src_address, int p_dst_address, const GDScriptDataType &p_datatype, const GDScriptParser::DataType &p_value_type) {
- if (p_datatype.has_type && p_value_type.is_variant()) {
- // Typed assignment
- switch (p_datatype.kind) {
- case GDScriptDataType::BUILTIN: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
- codegen.opcodes.push_back(p_datatype.builtin_type); // variable type
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx;
- if (GDScriptLanguage::get_singleton()->get_global_map().has(p_datatype.native_type)) {
- class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_datatype.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
- } else {
- // _set_error("Invalid native class type '" + String(p_datatype.native_type) + "'.", on->arguments[0]);
- return false;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
- codegen.opcodes.push_back(class_idx); // variable type
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = p_datatype.script_type;
- int idx = codegen.get_constant_pos(script); //make it a local constant (faster access)
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2 (unary only takes one parameter)
- }
- }
- } else {
- if (p_datatype.kind == GDScriptDataType::BUILTIN && p_value_type.kind == GDScriptParser::DataType::BUILTIN && p_datatype.builtin_type != p_value_type.builtin_type) {
- // Need conversion.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
- codegen.opcodes.push_back(p_datatype.builtin_type); // variable type
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2
- } else {
- // Either untyped assignment or already type-checked by the parser
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
- codegen.opcodes.push_back(p_dst_address); // argument 1
- codegen.opcodes.push_back(p_src_address); // argument 2 (unary only takes one parameter)
- }
- }
- return true;
+ return result;
}
-int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_expression, int p_stack_level, bool p_root, bool p_initializer, int p_index_addr) {
+GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
if (p_expression->is_constant) {
- return codegen.get_constant_pos(p_expression->reduced_value);
+ return codegen.add_constant(p_expression->reduced_value);
}
+ GDScriptCodeGenerator *gen = codegen.generator;
+
switch (p_expression->type) {
- //should parse variable declaration and adjust stack accordingly...
case GDScriptParser::Node::IDENTIFIER: {
- //return identifier
- //wait, identifier could be a local variable or something else... careful here, must reference properly
- //as stack may be more interesting to work with
-
- //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases performance a lot.
-
+ // Look for identifiers in current scope.
const GDScriptParser::IdentifierNode *in = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
StringName identifier = in->name;
- // TRY STACK!
- if (!p_initializer && codegen.stack_identifiers.has(identifier)) {
- int pos = codegen.stack_identifiers[identifier];
- return pos | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
+ // Try function parameters.
+ if (codegen.parameters.has(identifier)) {
+ return codegen.parameters[identifier];
}
- // TRY LOCAL CONSTANTS!
- if (codegen.local_named_constants.has(identifier)) {
- return codegen.local_named_constants[identifier] | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ // Try local variables and constants.
+ if (!p_initializer && codegen.locals.has(identifier)) {
+ return codegen.locals[identifier];
}
- // TRY CLASS MEMBER
+ // Try class members.
if (_is_class_member_property(codegen, identifier)) {
- //get property
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_MEMBER); // perform operator
- codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter)
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
+ // Get property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Could get the type of the class member here.
+ gen->write_get_member(temp, identifier);
+ return temp;
}
- //TRY MEMBERS!
+ // Try members.
if (!codegen.function_node || !codegen.function_node->is_static) {
- // TRY MEMBER VARIABLES!
- //static function
+ // Try member variables.
if (codegen.script->member_indices.has(identifier)) {
if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
// Perform getter.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_RETURN);
- codegen.opcodes.push_back(0); // Argument count.
- codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // Base (self).
- codegen.opcodes.push_back(codegen.get_name_map_pos(codegen.script->member_indices[identifier].getter)); // Method name.
- // Destination.
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary();
+ Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
+ gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
+ return temp;
} else {
- // No getter or inside getter: direct member access.
+ // No getter or inside getter: direct member access.,
int idx = codegen.script->member_indices[identifier].index;
- return idx | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
}
}
}
- //TRY CLASS CONSTANTS
-
+ // Try class constants.
GDScript *owner = codegen.script;
while (owner) {
GDScript *scr = owner;
GDScriptNativeClass *nc = nullptr;
while (scr) {
if (scr->constants.has(identifier)) {
- //int idx=scr->constants[identifier];
- int idx = codegen.get_name_map_pos(identifier);
- return idx | (GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
}
if (scr->native.is_valid()) {
nc = scr->native.ptr();
@@ -390,52 +223,37 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
scr = scr->_base;
}
- // CLASS C++ Integer Constant
-
+ // Class C++ integer constant.
if (nc) {
bool success = false;
int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
- Variant key = constant;
- int idx;
-
- if (!codegen.constant_map.has(key)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[key] = idx;
-
- } else {
- idx = codegen.constant_map[key];
- }
-
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ return codegen.add_constant(constant);
}
}
owner = owner->_owner;
}
- // TRY SIGNALS AND METHODS (can be made callables)
+ // Try signals and methods (can be made callables);
if (codegen.class_node->members_indices.has(identifier)) {
const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
// Get like it was a property.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET_NAMED); // perform operator
- codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // Self.
- codegen.opcodes.push_back(codegen.get_name_map_pos(identifier)); // argument 2 (unary only takes one parameter)
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
+
+ gen->write_get_named(temp, identifier, self);
+ return temp;
}
}
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- return idx | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::GLOBAL, idx); // TODO: Get type.
}
- /* TRY GLOBAL CLASSES */
-
+ // Try global classes.
if (ScriptServer::is_global_class(identifier)) {
const GDScriptParser::ClassNode *class_node = codegen.class_node;
while (class_node->outer) {
@@ -450,356 +268,209 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
if (res.is_null()) {
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- return -1;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
}
- Variant key = res;
- int idx;
-
- if (!codegen.constant_map.has(key)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[key] = idx;
-
- } else {
- idx = codegen.constant_map[key];
- }
-
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
+ return codegen.add_constant(res);
}
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- int idx = codegen.named_globals.find(identifier);
- if (idx == -1) {
- idx = codegen.named_globals.size();
- codegen.named_globals.push_back(identifier);
- }
- return idx | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NAMED_GLOBAL, gen->add_or_get_name(identifier)); // TODO: Get type.
}
#endif
- //not found, error
-
+ // Not found, error.
_set_error("Identifier not found: " + String(identifier), p_expression);
-
- return -1;
-
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
} break;
case GDScriptParser::Node::LITERAL: {
- //return constant
+ // Return constant.
const GDScriptParser::LiteralNode *cn = static_cast<const GDScriptParser::LiteralNode *>(p_expression);
- int idx;
-
- if (!codegen.constant_map.has(cn->value)) {
- idx = codegen.constant_map.size();
- codegen.constant_map[cn->value] = idx;
-
- } else {
- idx = codegen.constant_map[cn->value];
- }
-
- return idx | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS); //argument (stack root)
-
+ return codegen.add_constant(cn->value);
} break;
case GDScriptParser::Node::SELF: {
//return constant
if (codegen.function_node && codegen.function_node->is_static) {
_set_error("'self' not present in static function!", p_expression);
- return -1;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
- return (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF);
} break;
case GDScriptParser::Node::ARRAY: {
const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);
- Vector<int> values;
+ Vector<GDScriptCodeGenerator::Address> values;
- int slevel = p_stack_level;
+ // Create the result temporary first since it's the last to be killed.
+ GDScriptDataType array_type;
+ array_type.has_type = true;
+ array_type.kind = GDScriptDataType::BUILTIN;
+ array_type.builtin_type = Variant::ARRAY;
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
- int ret = _parse_expression(codegen, an->elements[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ GDScriptCodeGenerator::Address val = _parse_expression(codegen, r_error, an->elements[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
-
- values.push_back(ret);
+ values.push_back(val);
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
- codegen.opcodes.push_back(values.size());
+ gen->write_construct_array(result, values);
+
for (int i = 0; i < values.size(); i++) {
- codegen.opcodes.push_back(values[i]);
+ if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-
+ return result;
} break;
case GDScriptParser::Node::DICTIONARY: {
const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);
- Vector<int> elements;
+ Vector<GDScriptCodeGenerator::Address> elements;
- int slevel = p_stack_level;
+ // Create the result temporary first since it's the last to be killed.
+ GDScriptDataType dict_type;
+ dict_type.has_type = true;
+ dict_type.kind = GDScriptDataType::BUILTIN;
+ dict_type.builtin_type = Variant::DICTIONARY;
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(dict_type);
for (int i = 0; i < dn->elements.size(); i++) {
// Key.
- int ret = -1;
+ GDScriptCodeGenerator::Address element;
switch (dn->style) {
case GDScriptParser::DictionaryNode::PYTHON_DICT:
// Python-style: key is any expression.
- ret = _parse_expression(codegen, dn->elements[i].key, slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ element = _parse_expression(codegen, r_error, dn->elements[i].key);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
break;
case GDScriptParser::DictionaryNode::LUA_TABLE:
// Lua-style: key is an identifier interpreted as string.
String key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name;
- ret = codegen.get_constant_pos(key);
+ element = codegen.add_constant(key);
break;
}
- elements.push_back(ret);
+ elements.push_back(element);
- ret = _parse_expression(codegen, dn->elements[i].value, slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ element = _parse_expression(codegen, r_error, dn->elements[i].value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- elements.push_back(ret);
+ elements.push_back(element);
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
- codegen.opcodes.push_back(dn->elements.size());
+ gen->write_construct_dictionary(result, elements);
+
for (int i = 0; i < elements.size(); i++) {
- codegen.opcodes.push_back(elements[i]);
+ if (elements[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-
+ return result;
} 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());
- int slevel = p_stack_level;
- int src_addr = _parse_expression(codegen, cn->operand, slevel);
- if (src_addr < 0) {
- return src_addr;
- }
- if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ // Create temporary for result first since it will be deleted last.
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
+ GDScriptCodeGenerator::Address source = _parse_expression(codegen, r_error, cn->operand);
- switch (cast_type.kind) {
- case GDScriptDataType::BUILTIN: {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
- codegen.opcodes.push_back(cast_type.builtin_type);
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx;
- if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) {
- class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
- } else {
- _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn);
- return -1;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
- codegen.opcodes.push_back(class_idx); // variable type
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = cast_type.script_type;
- int idx = codegen.get_constant_pos(script); //make it a local constant (faster access)
+ gen->write_cast(result, source, cast_type);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- } break;
- default: {
- _set_error("Parser bug: unresolved data type.", cn);
- return -1;
- }
+ if (source.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(src_addr); // source address
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
- codegen.alloc_stack(p_stack_level);
- return dst_addr;
-
+ return source;
} break;
- //hell breaks loose
-
-#define OPERATOR_RETURN \
- int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); \
- codegen.opcodes.push_back(dst_addr); \
- codegen.alloc_stack(p_stack_level); \
- return dst_addr
-
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
- if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
- //construct a basic type
+ GDScriptDataType type = _gdtype_from_datatype(call->get_datatype());
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
- Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 0; i < call->arguments.size(); i++) {
- int ret = _parse_expression(codegen, call->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
+ Vector<GDScriptCodeGenerator::Address> arguments;
+ for (int i = 0; i < call->arguments.size(); i++) {
+ GDScriptCodeGenerator::Address arg = _parse_expression(codegen, r_error, call->arguments[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
+ arguments.push_back(arg);
+ }
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CONSTRUCT); // basic type constructor
- codegen.opcodes.push_back(vtype); //instance
- codegen.opcodes.push_back(arguments.size()); //argument count
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]); //arguments
- }
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ // Construct a built-in type.
+ Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+ gen->write_construct(result, vtype, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
- //built in function
-
- Vector<int> arguments;
- int slevel = p_stack_level;
- for (int i = 0; i < call->arguments.size(); i++) {
- int ret = _parse_expression(codegen, call->arguments[i], slevel);
- if (ret < 0) {
- return ret;
- }
-
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
-
- arguments.push_back(ret);
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name));
- codegen.opcodes.push_back(arguments.size());
- codegen.alloc_call(arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
- }
-
+ // Built-in function.
+ GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
+ gen->write_call_builtin(result, func, arguments);
} else {
- //regular function
-
+ // Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
- Vector<int> arguments;
- int slevel = p_stack_level;
-
- // TODO: Use callables when possible if needed.
- int ret = -1;
- int super_address = -1;
if (call->is_super) {
// Super call.
- if (call->callee == nullptr) {
- // Implicit super function call.
- super_address = codegen.get_name_map_pos(codegen.function_node->identifier->name);
- } else {
- super_address = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- }
+ gen->write_super_call(result, call->function_name, arguments);
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
- ret = (GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS);
+ GDScriptCodeGenerator::Address self;
+ self.mode = GDScriptCodeGenerator::Address::CLASS;
+ gen->write_call(result, self, call->function_name, arguments);
} else {
- ret = (GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
+ gen->write_call_self(result, call->function_name, arguments);
}
- arguments.push_back(ret);
- ret = codegen.get_name_map_pos(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- arguments.push_back(ret);
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
if (subscript->is_attribute) {
- ret = _parse_expression(codegen, subscript->base, slevel);
- if (ret < 0) {
- return ret;
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ if (within_await) {
+ gen->write_call_async(result, base, call->function_name, arguments);
+ } else {
+ gen->write_call(result, base, call->function_name, arguments);
+ }
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- arguments.push_back(ret);
- arguments.push_back(codegen.get_name_map_pos(subscript->attribute->name));
} else {
_set_error("Cannot call something that isn't a function.", call->callee);
- return -1;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
} else {
- _set_error("Cannot call something that isn't a function.", call->callee);
- return -1;
- }
- }
-
- for (int i = 0; i < call->arguments.size(); i++) {
- ret = _parse_expression(codegen, call->arguments[i], slevel);
- if (ret < 0) {
- return ret;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
- if ((ret >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
- arguments.push_back(ret);
- }
-
- int opcode = GDScriptFunction::OPCODE_CALL_RETURN;
- if (call->is_super) {
- opcode = GDScriptFunction::OPCODE_CALL_SELF_BASE;
- } else if (within_await) {
- opcode = GDScriptFunction::OPCODE_CALL_ASYNC;
- } else if (p_root) {
- opcode = GDScriptFunction::OPCODE_CALL;
}
+ }
- codegen.opcodes.push_back(opcode); // perform operator
- if (call->is_super) {
- codegen.opcodes.push_back(super_address);
- }
- codegen.opcodes.push_back(call->arguments.size());
- codegen.alloc_call(call->arguments.size());
- for (int i = 0; i < arguments.size(); i++) {
- codegen.opcodes.push_back(arguments[i]);
+ for (int i = 0; i < arguments.size(); i++) {
+ if (arguments[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
}
- OPERATOR_RETURN;
+ return result;
} break;
case GDScriptParser::Node::GET_NODE: {
const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);
@@ -816,59 +487,55 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
}
}
- int arg_address = codegen.get_constant_pos(NodePath(node_name));
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(codegen.add_constant(NodePath(node_name)));
+
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
+
+ MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
+ gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
- codegen.opcodes.push_back(p_root ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- codegen.opcodes.push_back(1); // number of arguments.
- codegen.alloc_call(1);
- codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // self.
- codegen.opcodes.push_back(codegen.get_name_map_pos("get_node")); // function.
- codegen.opcodes.push_back(arg_address); // argument (NodePath).
- OPERATOR_RETURN;
+ return result;
} break;
case GDScriptParser::Node::PRELOAD: {
const GDScriptParser::PreloadNode *preload = static_cast<const GDScriptParser::PreloadNode *>(p_expression);
// Add resource as constant.
- return codegen.get_constant_pos(preload->resource);
+ return codegen.add_constant(preload->resource);
} break;
case GDScriptParser::Node::AWAIT: {
const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);
- int slevel = p_stack_level;
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype()));
within_await = true;
- int argument = _parse_expression(codegen, await->to_await, slevel);
+ GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await);
within_await = false;
- if (argument < 0) {
- return argument;
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- if ((argument >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+
+ gen->write_await(result, argument);
+
+ if (argument.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- //push call bytecode
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_AWAIT);
- codegen.opcodes.push_back(argument);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_AWAIT_RESUME);
- //next will be where to place the result :)
- OPERATOR_RETURN;
+ return result;
} break;
-
- //indexing operator
+ // Indexing operator.
case GDScriptParser::Node::SUBSCRIPT: {
- int slevel = p_stack_level;
-
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
- int from = _parse_expression(codegen, subscript->base, slevel);
- if (from < 0) {
- return from;
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
bool named = subscript->is_attribute;
- int index;
- if (p_index_addr != 0) {
+ StringName name;
+ GDScriptCodeGenerator::Address index;
+ if (p_index_addr.mode != GDScriptCodeGenerator::Address::NIL) {
index = p_index_addr;
} else if (subscript->is_attribute) {
if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
@@ -879,306 +546,179 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (MI && MI->get().getter == codegen.function_name) {
String n = identifier->name;
_set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);
- return -1;
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
#endif
if (MI && MI->get().getter == "") {
- // Faster than indexing self (as if no self. had been used)
- return (MI->get().index) | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
+ // Remove result temp as we don't need it.
+ gen->pop_temporary();
+ // Faster than indexing self (as if no self. had been used).
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->get().index, _gdtype_from_datatype(subscript->get_datatype()));
}
}
- index = codegen.get_name_map_pos(subscript->attribute->name);
-
+ name = subscript->attribute->name;
+ named = true;
} else {
if (subscript->index->type == GDScriptParser::Node::LITERAL && static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value.get_type() == Variant::STRING) {
- //also, somehow, named (speed up anyway)
- StringName name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value;
- index = codegen.get_name_map_pos(name);
+ // Also, somehow, named (speed up anyway).
+ name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value;
named = true;
-
} else {
- //regular indexing
- if (from & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
-
- index = _parse_expression(codegen, subscript->index, slevel);
- if (index < 0) {
- return index;
+ // Regular indexing.
+ index = _parse_expression(codegen, r_error, subscript->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
}
}
- codegen.opcodes.push_back(named ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET); // perform operator
- codegen.opcodes.push_back(from); // argument 1
- codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter)
- OPERATOR_RETURN;
+ if (named) {
+ gen->write_get_named(result, name, base);
+ } else {
+ gen->write_get(result, index, base);
+ }
+
+ if (index.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+
+ return result;
} break;
case GDScriptParser::Node::UNARY_OPERATOR: {
- //unary operators
const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
- switch (unary->operation) {
- case GDScriptParser::UnaryOpNode::OP_NEGATIVE: {
- if (!_create_unary_operator(codegen, unary, Variant::OP_NEGATE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::UnaryOpNode::OP_POSITIVE: {
- if (!_create_unary_operator(codegen, unary, Variant::OP_POSITIVE, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::UnaryOpNode::OP_LOGIC_NOT: {
- if (!_create_unary_operator(codegen, unary, Variant::OP_NOT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::UnaryOpNode::OP_COMPLEMENT: {
- if (!_create_unary_operator(codegen, unary, Variant::OP_BIT_NEGATE, p_stack_level)) {
- return -1;
- }
- } break;
+
+ GDScriptCodeGenerator::Address result = codegen.add_temporary();
+
+ GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+
+ gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address());
+
+ if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- OPERATOR_RETURN;
+
+ return result;
}
case GDScriptParser::Node::BINARY_OPERATOR: {
- //binary operators (in precedence order)
const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary();
+
switch (binary->operation) {
case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
- // AND operator with early out on failure
+ // AND operator with early out on failure.
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ gen->write_and_left_operand(left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
+ gen->write_and_right_operand(right_operand);
- int res = _parse_expression(codegen, binary->left_operand, p_stack_level);
- if (res < 0) {
- return res;
+ gen->write_end_and(result);
+
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- res = _parse_expression(codegen, binary->right_operand, p_stack_level);
- if (res < 0) {
- return res;
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos2 = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
- codegen.opcodes.write[jump_fail_pos2] = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
-
} break;
case GDScriptParser::BinaryOpNode::OP_LOGIC_OR: {
- // OR operator with early out on success
+ // OR operator with early out on success.
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ gen->write_or_left_operand(left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
+ gen->write_or_right_operand(right_operand);
- int res = _parse_expression(codegen, binary->left_operand, p_stack_level);
- if (res < 0) {
- return res;
+ gen->write_end_or(result);
+
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(res);
- int jump_success_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- res = _parse_expression(codegen, binary->right_operand, p_stack_level);
- if (res < 0) {
- return res;
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF);
- codegen.opcodes.push_back(res);
- int jump_success_pos2 = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
-
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_FALSE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- codegen.opcodes.write[jump_success_pos] = codegen.opcodes.size();
- codegen.opcodes.write[jump_success_pos2] = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TRUE);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
-
} break;
case GDScriptParser::BinaryOpNode::OP_TYPE_TEST: {
- int slevel = p_stack_level;
-
- int src_address_a = _parse_expression(codegen, binary->left_operand, slevel);
- if (src_address_a < 0) {
- return -1;
- }
+ GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, binary->left_operand);
- if (src_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++; //uses stack for return, increase stack
- }
-
- int src_address_b = -1;
- bool builtin = false;
if (binary->right_operand->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name) != Variant::VARIANT_MAX) {
- // `is` with builtin type
- builtin = true;
- src_address_b = (int)GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name);
+ // `is` with builtin type)
+ Variant::Type type = GDScriptParser::get_builtin_type(static_cast<const GDScriptParser::IdentifierNode *>(binary->right_operand)->name);
+ gen->write_type_test_builtin(result, operand, type);
} else {
- src_address_b = _parse_expression(codegen, binary->right_operand, slevel);
- if (src_address_b < 0) {
- return -1;
+ GDScriptCodeGenerator::Address type = _parse_expression(codegen, r_error, binary->right_operand);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_type_test(result, operand, type);
+ if (type.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
}
+ } break;
+ default: {
+ GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
+ GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
- codegen.opcodes.push_back(builtin ? GDScriptFunction::OPCODE_IS_BUILTIN : GDScriptFunction::OPCODE_EXTENDS_TEST); // perform operator
- codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ gen->write_operator(result, binary->variant_op, left_operand, right_operand);
- } break;
- case GDScriptParser::BinaryOpNode::OP_CONTENT_TEST: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_IN, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_EQUAL: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_NOT_EQUAL: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_NOT_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_LESS: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_LESS, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_LESS_EQUAL: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_LESS_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_GREATER: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_GREATER, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_COMP_GREATER_EQUAL: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_GREATER_EQUAL, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_ADDITION: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_ADD, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_SUBTRACTION: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_SUBTRACT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_MULTIPLICATION: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_MULTIPLY, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_DIVISION: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_DIVIDE, p_stack_level)) {
- return -1;
+ if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- } break;
- case GDScriptParser::BinaryOpNode::OP_MODULO: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_MODULE, p_stack_level)) {
- return -1;
+ if (left_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- } break;
- case GDScriptParser::BinaryOpNode::OP_BIT_AND: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_AND, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_BIT_OR: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_OR, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_BIT_XOR: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_BIT_XOR, p_stack_level)) {
- return -1;
- }
- } break;
- //shift
- case GDScriptParser::BinaryOpNode::OP_BIT_LEFT_SHIFT: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_SHIFT_LEFT, p_stack_level)) {
- return -1;
- }
- } break;
- case GDScriptParser::BinaryOpNode::OP_BIT_RIGHT_SHIFT: {
- if (!_create_binary_operator(codegen, binary, Variant::OP_SHIFT_RIGHT, p_stack_level)) {
- return -1;
- }
- } break;
+ }
}
- OPERATOR_RETURN;
+ return result;
} break;
- // ternary operators
case GDScriptParser::Node::TERNARY_OPERATOR: {
- // x IF a ELSE y operator with early out on failure
-
+ // x IF a ELSE y operator with early out on failure.
const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
- int res = _parse_expression(codegen, ternary->condition, p_stack_level);
- if (res < 0) {
- return res;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(res);
- int jump_fail_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype()));
- res = _parse_expression(codegen, ternary->true_expr, p_stack_level);
- if (res < 0) {
- return res;
- }
+ gen->write_start_ternary(result);
- codegen.alloc_stack(p_stack_level); //it will be used..
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int jump_past_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, r_error, ternary->condition);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_condition(condition);
- codegen.opcodes.write[jump_fail_pos] = codegen.opcodes.size();
- res = _parse_expression(codegen, ternary->false_expr, p_stack_level);
- if (res < 0) {
- return res;
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.opcodes.push_back(res);
+ GDScriptCodeGenerator::Address true_expr = _parse_expression(codegen, r_error, ternary->true_expr);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_true_expr(true_expr);
+ if (true_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- codegen.opcodes.write[jump_past_pos] = codegen.opcodes.size();
+ GDScriptCodeGenerator::Address false_expr = _parse_expression(codegen, r_error, ternary->false_expr);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ gen->write_ternary_false_expr(false_expr);
+ if (false_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
- return p_stack_level | GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ gen->write_end_ternary();
+ return result;
} break;
- //assignment operators
case GDScriptParser::Node::ASSIGNMENT: {
const GDScriptParser::AssignmentNode *assignment = static_cast<const GDScriptParser::AssignmentNode *>(p_expression);
@@ -1186,20 +726,16 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
// SET (chained) MODE!
const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);
#ifdef DEBUG_ENABLED
- if (subscript->is_attribute) {
- if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name);
- if (MI && MI->get().setter == codegen.function_name) {
- String n = subscript->attribute->name;
- _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);
- return -1;
- }
+ if (subscript->is_attribute && subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
+ const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name);
+ if (MI && MI->get().setter == codegen.function_name) {
+ String n = subscript->attribute->name;
+ _set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
}
}
#endif
-
- int slevel = p_stack_level;
-
/* Find chain of sets */
StringName assign_property;
@@ -1207,13 +743,12 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
List<const GDScriptParser::SubscriptNode *> chain;
{
- //create get/set chain
+ // Create get/set chain.
const GDScriptParser::SubscriptNode *n = subscript;
while (true) {
chain.push_back(n);
-
if (n->base->type != GDScriptParser::Node::SUBSCRIPT) {
- //check for a built-in property
+ // Check for a built-in property.
if (n->base->type == GDScriptParser::Node::IDENTIFIER) {
GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(n->base);
if (_is_class_member_property(codegen, identifier->name)) {
@@ -1228,366 +763,396 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
/* Chain of gets */
- //get at (potential) root stack pos, so it can be returned
- int prev_pos = _parse_expression(codegen, chain.back()->get()->base, slevel);
- if (prev_pos < 0) {
- return prev_pos;
+ // Get at (potential) root stack pos, so it can be returned.
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- int retval = prev_pos;
- if (retval & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ GDScriptCodeGenerator::Address prev_base = base;
- Vector<int> setchain;
+ struct ChainInfo {
+ bool is_named = false;
+ GDScriptCodeGenerator::Address base;
+ GDScriptCodeGenerator::Address key;
+ StringName name;
+ };
- if (assign_property != StringName()) {
- // recover and assign at the end, this allows stuff like
- // position.x+=2.0
- // in Node2D
- setchain.push_back(prev_pos);
- setchain.push_back(codegen.get_name_map_pos(assign_property));
- setchain.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
- }
+ List<ChainInfo> set_chain;
for (List<const GDScriptParser::SubscriptNode *>::Element *E = chain.back(); E; E = E->prev()) {
- if (E == chain.front()) { //ignore first
+ if (E == chain.front()) {
+ // Skip the main subscript, since we'll assign to that.
break;
}
-
const GDScriptParser::SubscriptNode *subscript_elem = E->get();
- int key_idx;
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype()));
+ GDScriptCodeGenerator::Address key;
+ StringName name;
if (subscript_elem->is_attribute) {
- key_idx = codegen.get_name_map_pos(subscript_elem->attribute->name);
- //printf("named key %x\n",key_idx);
-
+ name = subscript_elem->attribute->name;
+ gen->write_get_named(value, name, prev_base);
} else {
- if (prev_pos & (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS)) {
- slevel++;
- codegen.alloc_stack(slevel);
+ key = _parse_expression(codegen, r_error, subscript_elem->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
-
- GDScriptParser::ExpressionNode *key = subscript_elem->index;
- key_idx = _parse_expression(codegen, key, slevel);
- //printf("expr key %x\n",key_idx);
-
- //stack was raised here if retval was stack but..
+ gen->write_get(value, key, prev_base);
}
- if (key_idx < 0) { //error
- return key_idx;
- }
-
- codegen.opcodes.push_back(subscript_elem->is_attribute ? GDScriptFunction::OPCODE_GET_NAMED : GDScriptFunction::OPCODE_GET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(key_idx);
- slevel++;
- codegen.alloc_stack(slevel);
- int dst_pos = (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) | slevel;
-
- codegen.opcodes.push_back(dst_pos);
-
- //add in reverse order, since it will be reverted
-
- setchain.push_back(dst_pos);
- setchain.push_back(key_idx);
- setchain.push_back(prev_pos);
- setchain.push_back(subscript_elem->is_attribute ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
-
- prev_pos = dst_pos;
+ // Store base and key for setting it back later.
+ set_chain.push_front({ subscript_elem->is_attribute, prev_base, key, name }); // Push to front to invert the list.
+ prev_base = value;
}
- setchain.invert();
-
- int set_index;
-
+ // Get value to assign.
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ // Get the key if needed.
+ GDScriptCodeGenerator::Address key;
+ StringName name;
if (subscript->is_attribute) {
- set_index = codegen.get_name_map_pos(subscript->attribute->name);
+ name = subscript->attribute->name;
} else {
- set_index = _parse_expression(codegen, subscript->index, slevel + 1);
+ key = _parse_expression(codegen, r_error, subscript->index);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
}
- if (set_index < 0) { //error
- return set_index;
+ // Perform operator if any.
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address value = codegen.add_temporary();
+ if (subscript->is_attribute) {
+ gen->write_get_named(value, name, prev_base);
+ } else {
+ gen->write_get(value, key, prev_base);
+ }
+ gen->write_operator(value, assignment->variant_op, value, assigned);
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ assigned = value;
}
- if (set_index & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
+ // Perform assignment.
+ if (subscript->is_attribute) {
+ gen->write_set_named(prev_base, name, assigned);
+ } else {
+ gen->write_set(prev_base, key, assigned);
}
-
- int set_value = _parse_assign_right_expression(codegen, assignment, slevel + 1, subscript->is_attribute ? 0 : set_index);
- if (set_value < 0) { //error
- return set_value;
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
- codegen.opcodes.push_back(subscript->is_attribute ? GDScriptFunction::OPCODE_SET_NAMED : GDScriptFunction::OPCODE_SET);
- codegen.opcodes.push_back(prev_pos);
- codegen.opcodes.push_back(set_index);
- codegen.opcodes.push_back(set_value);
+ assigned = prev_base;
- for (int i = 0; i < setchain.size(); i++) {
- codegen.opcodes.push_back(setchain[i]);
+ // Set back the values into their bases.
+ for (List<ChainInfo>::Element *E = set_chain.front(); E; E = E->next()) {
+ const ChainInfo &info = E->get();
+ if (!info.is_named) {
+ gen->write_set(info.base, info.key, assigned);
+ if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ assigned = info.base;
}
- return retval;
+ // If this is a local member, also assign to it.
+ // This allow things like: position.x += 2.0
+ if (assign_property != StringName()) {
+ gen->write_set_member(assigned, assign_property);
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
} else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
- //assignment to member property
-
- int slevel = p_stack_level;
-
- int src_address = _parse_assign_right_expression(codegen, assignment, slevel);
- if (src_address < 0) {
- return -1;
+ // Assignment to member property.
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
+ GDScriptCodeGenerator::Address assign_temp = assigned;
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_SET_MEMBER);
- codegen.opcodes.push_back(codegen.get_name_map_pos(name));
- codegen.opcodes.push_back(src_address);
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address member = codegen.add_temporary();
+ gen->write_get_member(member, name);
+ gen->write_operator(assigned, assignment->variant_op, member, assigned);
+ gen->pop_temporary();
+ }
- return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
- } else {
- //REGULAR ASSIGNMENT MODE!!
+ gen->write_set_member(assigned, name);
- int slevel = p_stack_level;
- int dst_address_a = -1;
+ if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ // Regular assignment.
+ GDScriptCodeGenerator::Address target;
bool has_setter = false;
bool is_in_setter = false;
StringName setter_function;
if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (!codegen.stack_identifiers.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
setter_function = codegen.script->member_indices[var_name].setter;
if (setter_function != StringName()) {
has_setter = true;
is_in_setter = setter_function == codegen.function_name;
- dst_address_a = codegen.script->member_indices[var_name].index;
+ target.mode = GDScriptCodeGenerator::Address::MEMBER;
+ target.address = codegen.script->member_indices[var_name].index;
}
}
}
if (has_setter) {
- if (is_in_setter) {
- // Use direct member access.
- dst_address_a |= GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS;
- } else {
+ if (!is_in_setter) {
// Store stack slot for the temp value.
- dst_address_a = slevel++;
- codegen.alloc_stack(slevel);
- dst_address_a |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
+ target = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype()));
}
} else {
- dst_address_a = _parse_expression(codegen, assignment->assignee, slevel);
- if (dst_address_a < 0) {
- return -1;
+ target = _parse_expression(codegen, r_error, assignment->assignee);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
+ }
- if (dst_address_a & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
- slevel++;
- codegen.alloc_stack(slevel);
- }
+ GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ GDScriptCodeGenerator::Address op_result;
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- int src_address_b = _parse_assign_right_expression(codegen, assignment, slevel);
- if (src_address_b < 0) {
- return -1;
+ if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ // Perform operation.
+ op_result = codegen.add_temporary();
+ gen->write_operator(op_result, assignment->variant_op, target, assigned);
+ } else {
+ op_result = assigned;
+ assigned = GDScriptCodeGenerator::Address();
}
GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
if (has_setter && !is_in_setter) {
// Call setter.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL);
- codegen.opcodes.push_back(1); // Argument count.
- codegen.opcodes.push_back(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS); // Base (self).
- codegen.opcodes.push_back(codegen.get_name_map_pos(setter_function)); // Method name.
- codegen.opcodes.push_back(dst_address_a); // Argument.
- codegen.opcodes.push_back(dst_address_a); // Result address (won't be used here).
- codegen.alloc_call(1);
- } else if (!_generate_typed_assign(codegen, src_address_b, dst_address_a, assign_type, assignment->assigned_value->get_datatype())) {
- return -1;
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(op_result);
+ gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
+ } else {
+ // Just assign.
+ gen->write_assign(target, op_result);
}
- return dst_address_a; //if anything, returns wathever was assigned or correct stack position
+ if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
+ return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
} break;
-#undef OPERATOR_RETURN
- //TYPE_TYPE,
default: {
- ERR_FAIL_V_MSG(-1, "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); //unreachable code
+ ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
} break;
}
}
-Error GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, const GDScriptParser::PatternNode *p_pattern, int p_stack_level, int p_value_addr, int p_type_addr, int &r_bound_variables, Vector<int> &r_patch_addresses, Vector<int> &r_block_patch_address) {
- // TODO: Many "repeated" code here that could be abstracted. This compiler is going away when new VM arrives though, so...
+GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested) {
switch (p_pattern->pattern_type) {
case GDScriptParser::PatternNode::PT_LITERAL: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+
// Get literal type into constant map.
- int literal_type_addr = -1;
- if (!codegen.constant_map.has((int)p_pattern->literal->value.get_type())) {
- literal_type_addr = codegen.constant_map.size();
- codegen.constant_map[(int)p_pattern->literal->value.get_type()] = literal_type_addr;
+ GDScriptCodeGenerator::Address literal_type_addr = codegen.add_constant((int)p_pattern->literal->value.get_type());
- } else {
- literal_type_addr = codegen.constant_map[(int)p_pattern->literal->value.get_type()];
- }
- literal_type_addr |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS;
+ // Equality is always a boolean.
+ GDScriptDataType equality_type;
+ equality_type.has_type = true;
+ equality_type.kind = GDScriptDataType::BUILTIN;
+ equality_type.builtin_type = Variant::BOOL;
// Check type equality.
- int equality_addr = p_stack_level++;
- equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(p_stack_level);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_type_addr);
- codegen.opcodes.push_back(literal_type_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if not the same type.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
+ codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+ codegen.generator->write_and_left_operand(type_equality_addr);
// Get literal.
- int literal_addr = _parse_expression(codegen, p_pattern->literal, p_stack_level);
+ GDScriptCodeGenerator::Address literal_addr = _parse_expression(codegen, r_error, p_pattern->literal);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
// Check value equality.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_value_addr);
- codegen.opcodes.push_back(literal_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if doesn't match.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
-
- // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- r_block_patch_address.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);
+ codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
+ codegen.generator->write_and_right_operand(equality_addr);
+
+ // AND both together (reuse temporary location).
+ codegen.generator->write_end_and(type_equality_addr);
+
+ codegen.generator->pop_temporary(); // Remove equality_addr from stack.
+
+ if (literal_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(type_equality_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(type_equality_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, type_equality_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove type_equality_addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_EXPRESSION: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Create the result temps first since it's the last to go away.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address equality_test_addr = codegen.add_temporary();
+
// Evaluate expression.
- int expr_addr = _parse_expression(codegen, p_pattern->expression, p_stack_level);
- if ((expr_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- p_stack_level++;
- codegen.alloc_stack(p_stack_level);
+ GDScriptCodeGenerator::Address expr_addr;
+ expr_addr = _parse_expression(codegen, r_error, p_pattern->expression);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
// Evaluate expression type.
- int expr_type_addr = p_stack_level++;
- expr_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(p_stack_level);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(expr_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(expr_type_addr); // Address to result.
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(expr_addr);
+ codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
// Check type equality.
- int equality_addr = p_stack_level++;
- equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(p_stack_level);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_type_addr);
- codegen.opcodes.push_back(expr_type_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if not the same type.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Check value equality.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_value_addr);
- codegen.opcodes.push_back(expr_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if doesn't match.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
-
- // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- r_block_patch_address.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
- } break;
- case GDScriptParser::PatternNode::PT_BIND: {
- // Create new stack variable.
- int bind_addr = p_stack_level | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
- codegen.add_stack_identifier(p_pattern->bind->name, p_stack_level++);
- codegen.alloc_stack(p_stack_level);
- r_bound_variables++;
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_and_right_operand(equality_test_addr);
- // Assign value to bound variable.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(bind_addr); // Destination.
- codegen.opcodes.push_back(p_value_addr); // Source.
- // Not need to block jump because bind happens only once.
+ // AND both type and value equality.
+ codegen.generator->write_end_and(result_addr);
+
+ // We don't need the expression temporary anymore.
+ if (expr_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ codegen.generator->pop_temporary(); // Remove type equality temporary.
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_ARRAY: {
- int slevel = p_stack_level;
-
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
// Get array type into constant map.
- int array_type_addr = codegen.get_constant_pos(Variant::ARRAY);
+ GDScriptCodeGenerator::Address array_type_addr = codegen.add_constant((int)Variant::ARRAY);
+
+ // Equality is always a boolean.
+ GDScriptDataType temp_type;
+ temp_type.has_type = true;
+ temp_type.kind = GDScriptDataType::BUILTIN;
+ temp_type.builtin_type = Variant::BOOL;
// Check type equality.
- int equality_addr = slevel++;
- equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(slevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_type_addr);
- codegen.opcodes.push_back(array_type_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if not the same type.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
- int array_length_addr = codegen.get_constant_pos(p_pattern->rest_used ? p_pattern->array.size() - 1 : p_pattern->array.size());
+ GDScriptCodeGenerator::Address array_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->array.size() - 1 : p_pattern->array.size());
// Get value length.
- int value_length_addr = slevel++;
- codegen.alloc_stack(slevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::LEN);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(p_value_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(value_length_addr); // Address to result.
+ temp_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
+ Vector<GDScriptCodeGenerator::Address> len_args;
+ len_args.push_back(p_value_addr);
+ codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
// Test length compatibility.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL);
- codegen.opcodes.push_back(value_length_addr);
- codegen.opcodes.push_back(array_length_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if length is not compatible.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
+ codegen.generator->write_and_right_operand(length_compat_addr);
+
+ // AND type and length check.
+ codegen.generator->write_end_and(result_addr);
+
+ // Remove length temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ // Create temporaries outside the loop so they can be reused.
+ GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->array.size(); i++) {
@@ -1596,494 +1161,461 @@ Error GDScriptCompiler::_parse_match_pattern(CodeGen &codegen, const GDScriptPar
break;
}
- int stlevel = p_stack_level;
- Vector<int> element_block_patches; // I want to internal patterns try the next element instead of going to the block.
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_addr);
+
// Add index to constant map.
- int index_addr = codegen.get_constant_pos(i);
+ GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);
// Get the actual element from the user-sent array.
- int element_addr = stlevel++;
- codegen.alloc_stack(stlevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET);
- codegen.opcodes.push_back(p_value_addr); // Source.
- codegen.opcodes.push_back(index_addr); // Index.
- codegen.opcodes.push_back(element_addr); // Destination.
+ codegen.generator->write_get(element_addr, index_addr, p_value_addr);
// Also get type of element.
- int element_type_addr = stlevel++;
- element_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(stlevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(element_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(element_type_addr); // Address to result.
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(element_addr);
+ codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
// Try the pattern inside the element.
- Error err = _parse_match_pattern(codegen, p_pattern->array[i], stlevel, element_addr, element_type_addr, r_bound_variables, r_patch_addresses, element_block_patches);
- if (err != OK) {
- return err;
+ test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
+ if (r_error != OK) {
+ return GDScriptCodeGenerator::Address();
}
- // Patch jumps to block to try the next element.
- for (int j = 0; j < element_block_patches.size(); j++) {
- codegen.opcodes.write[element_block_patches[j]] = codegen.opcodes.size();
- }
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
}
+ // Remove element temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
- // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
- // Also here for the case of empty arrays.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- r_block_patch_address.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ return test_addr;
} break;
case GDScriptParser::PatternNode::PT_DICTIONARY: {
- int slevel = p_stack_level;
-
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
// Get dictionary type into constant map.
- int dict_type_addr = codegen.get_constant_pos(Variant::DICTIONARY);
+ GDScriptCodeGenerator::Address dict_type_addr = codegen.add_constant((int)Variant::DICTIONARY);
+
+ // Equality is always a boolean.
+ GDScriptDataType temp_type;
+ temp_type.has_type = true;
+ temp_type.kind = GDScriptDataType::BUILTIN;
+ temp_type.builtin_type = Variant::BOOL;
// Check type equality.
- int equality_addr = slevel++;
- equality_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(slevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(Variant::OP_EQUAL);
- codegen.opcodes.push_back(p_type_addr);
- codegen.opcodes.push_back(dict_type_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if not the same type.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
- int dict_length_addr = codegen.get_constant_pos(p_pattern->rest_used ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
+ GDScriptCodeGenerator::Address dict_length_addr = codegen.add_constant(p_pattern->rest_used ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
// Get user's dictionary length.
- int value_length_addr = slevel++;
- codegen.alloc_stack(slevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::LEN);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(p_value_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(value_length_addr); // Address to result.
+ temp_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
+ Vector<GDScriptCodeGenerator::Address> func_args;
+ func_args.push_back(p_value_addr);
+ codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
// Test length compatibility.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_OPERATOR);
- codegen.opcodes.push_back(p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL);
- codegen.opcodes.push_back(value_length_addr);
- codegen.opcodes.push_back(dict_length_addr);
- codegen.opcodes.push_back(equality_addr); // Address to result.
-
- // Jump away if length is not compatible.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(equality_addr);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
+ codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
+ codegen.generator->write_and_right_operand(length_compat_addr);
+
+ // AND type and length check.
+ codegen.generator->write_end_and(result_addr);
+
+ // Remove length temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ // Create temporaries outside the loop so they can be reused.
+ temp_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address test_result = codegen.add_temporary(temp_type);
+ GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
+ GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->dictionary.size(); i++) {
const GDScriptParser::PatternNode::Pair &element = p_pattern->dictionary[i];
if (element.value_pattern && element.value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) {
// Ignore rest pattern.
- continue;
+ break;
}
- int stlevel = p_stack_level;
- Vector<int> element_block_patches; // I want to internal patterns try the next element instead of going to the block.
+
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_addr);
// Get the pattern key.
- int pattern_key_addr = _parse_expression(codegen, element.key, stlevel);
- if (pattern_key_addr < 0) {
- return ERR_PARSE_ERROR;
- }
- if ((pattern_key_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- stlevel++;
- codegen.alloc_stack(stlevel);
+ GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
}
- // Create stack slot for test result.
- int test_result = stlevel++;
- test_result |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(stlevel);
-
- // Check if pattern key exists in user's dictionary.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_RETURN);
- codegen.opcodes.push_back(1); // Argument count.
- codegen.opcodes.push_back(p_value_addr); // Base (user dictionary).
- codegen.opcodes.push_back(codegen.get_name_map_pos("has")); // Function name.
- codegen.opcodes.push_back(pattern_key_addr); // Argument (pattern key).
- codegen.opcodes.push_back(test_result); // Return address.
-
- // Jump away if key doesn't exist.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(test_result);
- r_patch_addresses.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ // Check if pattern key exists in user's dictionary. This will be AND-ed with next result.
+ func_args.clear();
+ func_args.push_back(pattern_key_addr);
+ codegen.generator->write_call(test_result, p_value_addr, "has", func_args);
if (element.value_pattern != nullptr) {
+ // Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
+ codegen.generator->write_and_left_operand(test_result);
+
// Get actual value from user dictionary.
- int value_addr = stlevel++;
- codegen.alloc_stack(stlevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_GET);
- codegen.opcodes.push_back(p_value_addr); // Source.
- codegen.opcodes.push_back(pattern_key_addr); // Index.
- codegen.opcodes.push_back(value_addr); // Destination.
+ codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);
// Also get type of value.
- int value_type_addr = stlevel++;
- value_type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(stlevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(value_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(value_type_addr); // Address to result.
+ func_args.clear();
+ func_args.push_back(element_addr);
+ codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
// Try the pattern inside the value.
- Error err = _parse_match_pattern(codegen, element.value_pattern, stlevel, value_addr, value_type_addr, r_bound_variables, r_patch_addresses, element_block_patches);
- if (err != OK) {
- return err;
+ test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
+ if (r_error != OK) {
+ return GDScriptCodeGenerator::Address();
}
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
}
- // Patch jumps to block to try the next element.
- for (int j = 0; j < element_block_patches.size(); j++) {
- codegen.opcodes.write[element_block_patches[j]] = codegen.opcodes.size();
+ codegen.generator->write_and_right_operand(test_addr);
+ codegen.generator->write_end_and(test_addr);
+
+ // Remove pattern key temporary.
+ if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
}
- // Jump to the actual block since it matches. This is needed to take multi-pattern into account.
- // Also here for the case of empty dictionaries.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- r_block_patch_address.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ // Remove element temporaries.
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+ codegen.generator->pop_temporary();
+ return test_addr;
} break;
case GDScriptParser::PatternNode::PT_REST:
// Do nothing.
+ return p_previous_test;
break;
+ case GDScriptParser::PatternNode::PT_BIND: {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ // Get the bind address.
+ GDScriptCodeGenerator::Address bind = codegen.locals[p_pattern->bind->name];
+
+ // Assign value to bound variable.
+ codegen.generator->write_assign(bind, p_value_addr);
+ }
+ [[fallthrough]]; // Act like matching anything too.
case GDScriptParser::PatternNode::PT_WILDCARD:
- // This matches anything so just do the jump.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- r_block_patch_address.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be replaced.
+ // If this is a fall through we don't want to do this again.
+ if (p_pattern->pattern_type != GDScriptParser::PatternNode::PT_BIND) {
+ if (p_is_nested) {
+ codegen.generator->write_and_left_operand(p_previous_test);
+ } else if (!p_is_first) {
+ codegen.generator->write_or_left_operand(p_previous_test);
+ }
+ }
+ // This matches anything so just do the same as `if(true)`.
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the operator with the `true` constant so it works as always matching.
+ GDScriptCodeGenerator::Address constant = codegen.add_constant(true);
+ codegen.generator->write_and_right_operand(constant);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the operator with the `true` constant so it works as always matching.
+ GDScriptCodeGenerator::Address constant = codegen.add_constant(true);
+ codegen.generator->write_or_right_operand(constant);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign_true(p_previous_test);
+ }
+ return p_previous_test;
}
- return OK;
+ ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern.");
}
-Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, int p_stack_level, int p_break_addr, int p_continue_addr) {
- codegen.push_stack_identifiers();
- int new_identifiers = 0;
- codegen.current_line = p_block->start_line;
+void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {
+ for (int i = 0; i < p_block->locals.size(); i++) {
+ if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) {
+ // Parameters are added directly from function and loop variables are declared explicitly.
+ continue;
+ }
+ codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype()));
+ }
+}
+
+Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals) {
+ Error error = OK;
+ GDScriptCodeGenerator *gen = codegen.generator;
+
+ codegen.start_block();
+
+ if (p_add_locals) {
+ _add_locals_in_block(codegen, p_block);
+ }
for (int i = 0; i < p_block->statements.size(); i++) {
const GDScriptParser::Node *s = p_block->statements[i];
#ifdef DEBUG_ENABLED
// Add a newline before each statement, since the debugger needs those.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(s->start_line);
- codegen.current_line = s->start_line;
+ gen->write_newline(s->start_line);
#endif
switch (s->type) {
case GDScriptParser::Node::MATCH: {
const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(s);
- int slevel = p_stack_level;
+ gen->start_match();
+ codegen.start_block();
- // First, let's save the addres of the value match.
- int temp_addr = _parse_expression(codegen, match->test, slevel);
- if (temp_addr < 0) {
- return ERR_PARSE_ERROR;
- }
- if ((temp_addr >> GDScriptFunction::ADDR_BITS & GDScriptFunction::ADDR_TYPE_STACK) == GDScriptFunction::ADDR_TYPE_STACK) {
- slevel++;
- codegen.alloc_stack(slevel);
+ // Evaluate the match expression.
+ GDScriptCodeGenerator::Address value = _parse_expression(codegen, error, match->test);
+ if (error) {
+ return error;
}
// Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
- int type_addr = slevel++;
- type_addr |= GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS;
- codegen.alloc_stack(slevel);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- codegen.opcodes.push_back(GDScriptFunctions::TYPE_OF);
- codegen.opcodes.push_back(1); // One argument.
- codegen.opcodes.push_back(temp_addr); // Argument is the value we want to test.
- codegen.opcodes.push_back(type_addr); // Address to result.
-
- Vector<int> patch_match_end; // Will patch the jump to the end of match.
+ GDScriptCodeGenerator::Address type = codegen.add_temporary();
+ Vector<GDScriptCodeGenerator::Address> typeof_args;
+ typeof_args.push_back(value);
+ gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
// Now we can actually start testing.
// For each branch.
for (int j = 0; j < match->branches.size(); j++) {
+ if (j > 0) {
+ // Use `else` to not check the next branch after matching.
+ gen->write_else();
+ }
+
const GDScriptParser::MatchBranchNode *branch = match->branches[j];
- int bound_variables = 0;
- codegen.push_stack_identifiers(); // Create an extra block around for binds.
+ gen->start_match_branch(); // Need so lower level code can patch 'continue' jumps.
+ codegen.start_block(); // Create an extra block around for binds.
+
+ // Add locals in block before patterns, so temporaries don't use the stack address for binds.
+ _add_locals_in_block(codegen, branch->block);
#ifdef DEBUG_ENABLED
// Add a newline before each branch, since the debugger needs those.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(s->start_line);
- codegen.current_line = s->start_line;
+ gen->write_newline(branch->start_line);
#endif
- Vector<int> patch_addrs; // Will patch with end of pattern to jump.
- Vector<int> block_patch_addrs; // Will patch with start of block to jump.
-
// For each pattern in branch.
+ GDScriptCodeGenerator::Address pattern_result = codegen.add_temporary();
for (int k = 0; k < branch->patterns.size(); k++) {
- if (k > 0) {
- // Patch jumps per pattern to allow for multipattern. If a pattern fails it just tries the next.
- for (int l = 0; l < patch_addrs.size(); l++) {
- codegen.opcodes.write[patch_addrs[l]] = codegen.opcodes.size();
- }
- patch_addrs.clear();
+ pattern_result = _parse_match_pattern(codegen, error, branch->patterns[k], value, type, pattern_result, k == 0, false);
+ if (error != OK) {
+ return error;
}
- Error err = _parse_match_pattern(codegen, branch->patterns[k], slevel, temp_addr, type_addr, bound_variables, patch_addrs, block_patch_addrs);
- if (err != OK) {
- return err;
- }
- }
- // Patch jumps to the block.
- for (int k = 0; k < block_patch_addrs.size(); k++) {
- codegen.opcodes.write[block_patch_addrs[k]] = codegen.opcodes.size();
}
- // Leave space for bound variables.
- slevel += bound_variables;
- codegen.alloc_stack(slevel);
-
- // Parse the branch block.
- _parse_block(codegen, branch->block, slevel, p_break_addr, p_continue_addr);
+ // Check if pattern did match.
+ gen->write_if(pattern_result);
- // Jump to end of match.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- patch_match_end.push_back(codegen.opcodes.size());
- codegen.opcodes.push_back(0); // Will be patched.
+ // Remove the result from stack.
+ gen->pop_temporary();
- // Patch the addresses of last pattern to jump to the end of the branch, into the next one.
- for (int k = 0; k < patch_addrs.size(); k++) {
- codegen.opcodes.write[patch_addrs[k]] = codegen.opcodes.size();
+ // Parse the branch block.
+ error = _parse_block(codegen, branch->block, false); // Don't add locals again.
+ if (error) {
+ return error;
}
- codegen.pop_stack_identifiers(); // Get out of extra block.
+ codegen.end_block(); // Get out of extra block.
}
- // Patch the addresses to jump to the end of the match statement.
- for (int j = 0; j < patch_match_end.size(); j++) {
- codegen.opcodes.write[patch_match_end[j]] = codegen.opcodes.size();
+
+ // End all nested `if`s.
+ for (int j = 0; j < match->branches.size(); j++) {
+ gen->write_endif();
}
- } break;
+ gen->pop_temporary();
+
+ if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ gen->end_match();
+ } break;
case GDScriptParser::Node::IF: {
const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);
- int ret2 = _parse_expression(codegen, if_n->condition, p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, if_n->condition);
+ if (error) {
+ return error;
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- int else_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0); //temporary
+ gen->write_if(condition);
- Error err = _parse_block(codegen, if_n->true_block, p_stack_level, p_break_addr, p_continue_addr);
- if (err) {
- return err;
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ error = _parse_block(codegen, if_n->true_block);
+ if (error) {
+ return error;
}
if (if_n->false_block) {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- int end_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(0);
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
-
- Error err2 = _parse_block(codegen, if_n->false_block, p_stack_level, p_break_addr, p_continue_addr);
- if (err2) {
- return err2;
- }
+ gen->write_else();
- codegen.opcodes.write[end_addr] = codegen.opcodes.size();
- } else {
- //end without else
- codegen.opcodes.write[else_addr] = codegen.opcodes.size();
+ error = _parse_block(codegen, if_n->false_block);
+ if (error) {
+ return error;
+ }
}
+ gen->write_endif();
} break;
case GDScriptParser::Node::FOR: {
const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);
- int slevel = p_stack_level;
- int iter_stack_pos = slevel;
- int iterator_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int counter_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = (slevel++) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- codegen.alloc_stack(slevel);
-
- codegen.push_stack_identifiers();
- codegen.add_stack_identifier(for_n->variable->name, iter_stack_pos);
-
- int ret2 = _parse_expression(codegen, for_n->list, slevel, false);
- if (ret2 < 0) {
- return ERR_COMPILATION_FAILED;
- }
-
- //assign container
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(ret2);
-
- //begin loop
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE_BEGIN);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(codegen.opcodes.size() + 4);
- codegen.opcodes.push_back(iterator_pos);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(codegen.opcodes.size() + 8);
- //break loop
- int break_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP); //skip code for next
- codegen.opcodes.push_back(0); //skip code for next
- //next loop
- int continue_pos = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ITERATE);
- codegen.opcodes.push_back(counter_pos);
- codegen.opcodes.push_back(container_pos);
- codegen.opcodes.push_back(break_pos);
- codegen.opcodes.push_back(iterator_pos);
-
- Error err = _parse_block(codegen, for_n->loop, slevel, break_pos, continue_pos);
- if (err) {
- return err;
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_pos);
- codegen.opcodes.write[break_pos + 1] = codegen.opcodes.size();
-
- codegen.pop_stack_identifiers();
+ codegen.start_block();
+ GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
+
+ GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list);
+ if (error) {
+ return error;
+ }
+
+ gen->write_for(iterator, list);
+
+ error = _parse_block(codegen, for_n->loop);
+ if (error) {
+ return error;
+ }
+
+ gen->write_endfor();
+
+ if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ codegen.end_block();
} break;
case GDScriptParser::Node::WHILE: {
const GDScriptParser::WhileNode *while_n = static_cast<const GDScriptParser::WhileNode *>(s);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(codegen.opcodes.size() + 3);
- int break_addr = codegen.opcodes.size();
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(0);
- int continue_addr = codegen.opcodes.size();
-
- int ret2 = _parse_expression(codegen, while_n->condition, p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+
+ gen->start_while_condition();
+
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, while_n->condition);
+ if (error) {
+ return error;
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_IF_NOT);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(break_addr);
- Error err = _parse_block(codegen, while_n->loop, p_stack_level, break_addr, continue_addr);
- if (err) {
- return err;
+
+ gen->write_while(condition);
+
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(continue_addr);
- codegen.opcodes.write[break_addr + 1] = codegen.opcodes.size();
+ error = _parse_block(codegen, while_n->loop);
+ if (error) {
+ return error;
+ }
+ gen->write_endwhile();
} break;
case GDScriptParser::Node::BREAK: {
- if (p_break_addr < 0) {
- _set_error("'break'' not within loop", s);
- return ERR_COMPILATION_FAILED;
- }
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_break_addr);
-
+ gen->write_break();
} break;
case GDScriptParser::Node::CONTINUE: {
- if (p_continue_addr < 0) {
- _set_error("'continue' not within loop", s);
- return ERR_COMPILATION_FAILED;
+ const GDScriptParser::ContinueNode *cont = static_cast<const GDScriptParser::ContinueNode *>(s);
+ if (cont->is_for_match) {
+ gen->write_continue_match();
+ } else {
+ gen->write_continue();
}
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP);
- codegen.opcodes.push_back(p_continue_addr);
-
} break;
case GDScriptParser::Node::RETURN: {
const GDScriptParser::ReturnNode *return_n = static_cast<const GDScriptParser::ReturnNode *>(s);
- int ret2;
+
+ GDScriptCodeGenerator::Address return_value;
if (return_n->return_value != nullptr) {
- ret2 = _parse_expression(codegen, return_n->return_value, p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ return_value = _parse_expression(codegen, error, return_n->return_value);
+ if (error) {
+ return error;
}
-
- } else {
- ret2 = GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_RETURN);
- codegen.opcodes.push_back(ret2);
-
+ gen->write_return(return_value);
+ if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
} break;
case GDScriptParser::Node::ASSERT: {
#ifdef DEBUG_ENABLED
- // try subblocks
-
const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s);
- int ret2 = _parse_expression(codegen, as->condition, p_stack_level, false);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, as->condition);
+ if (error) {
+ return error;
}
- int message_ret = 0;
+ GDScriptCodeGenerator::Address message;
+
if (as->message) {
- message_ret = _parse_expression(codegen, as->message, p_stack_level + 1, false);
- if (message_ret < 0) {
- return ERR_PARSE_ERROR;
+ message = _parse_expression(codegen, error, as->message);
+ if (error) {
+ return error;
}
}
+ gen->write_assert(condition, message);
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSERT);
- codegen.opcodes.push_back(ret2);
- codegen.opcodes.push_back(message_ret);
+ if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+ if (message.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
#endif
} break;
case GDScriptParser::Node::BREAKPOINT: {
#ifdef DEBUG_ENABLED
- // try subblocks
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_BREAKPOINT);
+ gen->write_breakpoint();
#endif
} break;
case GDScriptParser::Node::VARIABLE: {
const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
-
- // since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
- //
- //if (_is_class_member_property(codegen, lv->name)) {
- // _set_error("Name for local variable '" + String(lv->name) + "' can't shadow class property of the same name.", lv);
- // return ERR_ALREADY_EXISTS;
- //}
-
- codegen.add_stack_identifier(lv->identifier->name, p_stack_level++);
- codegen.alloc_stack(p_stack_level);
- new_identifiers++;
+ // Should be already in stack when the block began.
+ GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
if (lv->initializer != nullptr) {
- int dst_address = codegen.stack_identifiers[lv->identifier->name];
- dst_address |= GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS;
-
- int src_address = _parse_expression(codegen, lv->initializer, p_stack_level);
- if (src_address < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
+ if (error) {
+ return error;
}
- if (!_generate_typed_assign(codegen, src_address, dst_address, _gdtype_from_datatype(lv->get_datatype()), lv->initializer->get_datatype())) {
- return ERR_PARSE_ERROR;
+ gen->write_assign(local, src_address);
+ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
}
} break;
@@ -2094,281 +1626,159 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
_set_error("Local constant must have a constant value as initializer.", lc->initializer);
return ERR_PARSE_ERROR;
}
- codegen.local_named_constants[lc->identifier->name] = codegen.get_constant_pos(lc->initializer->reduced_value);
+
+ codegen.add_local_constant(lc->identifier->name, lc->initializer->reduced_value);
} break;
case GDScriptParser::Node::PASS:
// Nothing to do.
break;
default: {
- //expression
+ // Expression.
if (s->is_expression()) {
- int ret2 = _parse_expression(codegen, static_cast<const GDScriptParser::ExpressionNode *>(s), p_stack_level, true);
- if (ret2 < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address expr = _parse_expression(codegen, error, static_cast<const GDScriptParser::ExpressionNode *>(s), true);
+ if (error) {
+ return error;
+ }
+ if (expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
} else {
- ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bug in bytecode compiler, unexpected node in parse tree while parsing statement."); //unreachable code
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Bug in bytecode compiler, unexpected node in parse tree while parsing statement."); // Unreachable code.
}
} break;
}
}
- codegen.pop_stack_identifiers();
+ codegen.end_block();
return OK;
}
Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
- Vector<int> bytecode;
+ Error error = OK;
CodeGen codegen;
+ codegen.generator = memnew(GDScriptByteCodeGenerator);
codegen.class_node = p_class;
codegen.script = p_script;
codegen.function_node = p_func;
- codegen.stack_max = 0;
- codegen.current_line = 0;
- codegen.call_max = 0;
- codegen.debug_stack = EngineDebugger::is_active();
- Vector<StringName> argnames;
- int stack_level = 0;
+ StringName func_name;
+ bool is_static = false;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ GDScriptDataType return_type;
+ return_type.has_type = true;
+ return_type.kind = GDScriptDataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
+
+ if (p_func) {
+ func_name = p_func->identifier->name;
+ is_static = p_func->is_static;
+ rpc_mode = p_func->rpc_mode;
+ return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
+ } else {
+ if (p_for_ready) {
+ func_name = "_ready";
+ } else {
+ func_name = "@implicit_new";
+ }
+ }
+
+ codegen.function_name = func_name;
+ codegen.generator->write_start(p_script, func_name, is_static, rpc_mode, return_type);
+
int optional_parameters = 0;
if (p_func) {
for (int i = 0; i < p_func->parameters.size(); i++) {
- // since we are using properties now for most class access, allow shadowing of class members to make user's life easier.
- //
- //if (_is_class_member_property(p_script, p_func->arguments[i])) {
- // _set_error("Name for argument '" + String(p_func->arguments[i]) + "' can't shadow class property of the same name.", p_func);
- // return ERR_ALREADY_EXISTS;
- //}
-
- codegen.add_stack_identifier(p_func->parameters[i]->identifier->name, i);
-#ifdef TOOLS_ENABLED
- argnames.push_back(p_func->parameters[i]->identifier->name);
-#endif
+ const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
+ GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);
+ uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type);
+ codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
+
if (p_func->parameters[i]->default_value != nullptr) {
optional_parameters++;
}
}
- stack_level = p_func->parameters.size();
}
- codegen.alloc_stack(stack_level);
-
- /* Parse initializer -if applies- */
-
+ // Parse initializer if applies.
bool is_implicit_initializer = !p_for_ready && !p_func;
bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready");
- if (is_implicit_initializer) {
+ if (is_implicit_initializer || is_for_ready) {
// Initialize class fields.
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
continue;
}
const GDScriptParser::VariableNode *field = p_class->members[i].variable;
- if (field->onready) {
+ if (field->onready != is_for_ready) {
// Only initialize in _ready.
continue;
}
if (field->initializer) {
// Emit proper line change.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(field->initializer->start_line);
+ codegen.generator->write_newline(field->initializer->start_line);
- int src_address = _parse_expression(codegen, field->initializer, stack_level, false, true);
- if (src_address < 0) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
}
- int dst_address = codegen.script->member_indices[field->identifier->name].index;
- dst_address |= GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS;
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
- if (!_generate_typed_assign(codegen, src_address, dst_address, _gdtype_from_datatype(field->get_datatype()), field->initializer->get_datatype())) {
- return ERR_PARSE_ERROR;
+ codegen.generator->write_assign(dst_address, src_address);
+ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
}
}
}
- if (p_for_ready || (p_func && String(p_func->identifier->name) == "_ready")) {
- // Initialize class fields on ready.
- for (int i = 0; i < p_class->members.size(); i++) {
- if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
- continue;
- }
- const GDScriptParser::VariableNode *field = p_class->members[i].variable;
- if (!field->onready) {
- continue;
- }
-
- if (field->initializer) {
- // Emit proper line change.
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_LINE);
- codegen.opcodes.push_back(field->initializer->start_line);
-
- int src_address = _parse_expression(codegen, field->initializer, stack_level, false, true);
- if (src_address < 0) {
- return ERR_PARSE_ERROR;
- }
- int dst_address = codegen.script->member_indices[field->identifier->name].index;
- dst_address |= GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS;
-
- if (!_generate_typed_assign(codegen, src_address, dst_address, _gdtype_from_datatype(field->get_datatype()), field->initializer->get_datatype())) {
- return ERR_PARSE_ERROR;
- }
- }
- }
- }
-
- /* Parse default argument code -if applies- */
-
- Vector<int> defarg_addr;
- StringName func_name;
-
+ // Parse default argument code if applies.
if (p_func) {
if (optional_parameters > 0) {
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
- defarg_addr.push_back(codegen.opcodes.size());
+ codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
- int src_addr = _parse_expression(codegen, p_func->parameters[i]->default_value, stack_level, true);
- if (src_addr < 0) {
- return ERR_PARSE_ERROR;
+ const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
}
- int dst_addr = codegen.stack_identifiers[p_func->parameters[i]->identifier->name] | (GDScriptFunction::ADDR_TYPE_STACK_VARIABLE << GDScriptFunction::ADDR_BITS);
- if (!_generate_typed_assign(codegen, src_addr, dst_addr, _gdtype_from_datatype(p_func->parameters[i]->get_datatype()), p_func->parameters[i]->default_value->get_datatype())) {
- return ERR_PARSE_ERROR;
+ GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
+ codegen.generator->write_assign(dst_addr, src_addr);
+ if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
}
- defarg_addr.push_back(codegen.opcodes.size());
}
- defarg_addr.invert();
+ codegen.generator->end_parameters();
}
- func_name = p_func->identifier->name;
- codegen.function_name = func_name;
- Error err = _parse_block(codegen, p_func->body, stack_level);
+ Error err = _parse_block(codegen, p_func->body);
if (err) {
+ memdelete(codegen.generator);
return err;
}
-
- } else {
- if (p_for_ready) {
- func_name = "_ready";
- } else {
- func_name = "@implicit_new";
- }
- }
-
- codegen.function_name = func_name;
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_END);
-
- /*
- if (String(p_func->name)=="") { //initializer func
- gdfunc = &p_script->initializer;
- */
- //} else { //regular func
- p_script->member_functions[func_name] = memnew(GDScriptFunction);
- GDScriptFunction *gdfunc = p_script->member_functions[func_name];
- //}
-
- if (p_func) {
- gdfunc->_static = p_func->is_static;
- gdfunc->rpc_mode = p_func->rpc_mode;
- gdfunc->argument_types.resize(p_func->parameters.size());
- for (int i = 0; i < p_func->parameters.size(); i++) {
- gdfunc->argument_types.write[i] = _gdtype_from_datatype(p_func->parameters[i]->get_datatype());
- }
- gdfunc->return_type = _gdtype_from_datatype(p_func->get_datatype());
- } else {
- gdfunc->_static = false;
- gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
- gdfunc->return_type = GDScriptDataType();
- gdfunc->return_type.has_type = true;
- gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
- gdfunc->return_type.builtin_type = Variant::NIL;
- }
-
-#ifdef TOOLS_ENABLED
- gdfunc->arg_names = argnames;
-#endif
- //constants
- if (codegen.constant_map.size()) {
- gdfunc->_constant_count = codegen.constant_map.size();
- gdfunc->constants.resize(codegen.constant_map.size());
- gdfunc->_constants_ptr = gdfunc->constants.ptrw();
- const Variant *K = nullptr;
- while ((K = codegen.constant_map.next(K))) {
- int idx = codegen.constant_map[*K];
- gdfunc->constants.write[idx] = *K;
- }
- } else {
- gdfunc->_constants_ptr = nullptr;
- gdfunc->_constant_count = 0;
- }
- //global names
- if (codegen.name_map.size()) {
- gdfunc->global_names.resize(codegen.name_map.size());
- gdfunc->_global_names_ptr = &gdfunc->global_names[0];
- for (Map<StringName, int>::Element *E = codegen.name_map.front(); E; E = E->next()) {
- gdfunc->global_names.write[E->get()] = E->key();
- }
- gdfunc->_global_names_count = gdfunc->global_names.size();
-
- } else {
- gdfunc->_global_names_ptr = nullptr;
- gdfunc->_global_names_count = 0;
- }
-
-#ifdef TOOLS_ENABLED
- // Named globals
- if (codegen.named_globals.size()) {
- gdfunc->named_globals.resize(codegen.named_globals.size());
- gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
- for (int i = 0; i < codegen.named_globals.size(); i++) {
- gdfunc->named_globals.write[i] = codegen.named_globals[i];
- }
- gdfunc->_named_globals_count = gdfunc->named_globals.size();
}
-#endif
-
- if (codegen.opcodes.size()) {
- gdfunc->code = codegen.opcodes;
- gdfunc->_code_ptr = &gdfunc->code[0];
- gdfunc->_code_size = codegen.opcodes.size();
- } else {
- gdfunc->_code_ptr = nullptr;
- gdfunc->_code_size = 0;
- }
-
- if (defarg_addr.size()) {
- gdfunc->default_arguments = defarg_addr;
- gdfunc->_default_arg_count = defarg_addr.size() - 1;
- gdfunc->_default_arg_ptr = &gdfunc->default_arguments[0];
- } else {
- gdfunc->_default_arg_count = 0;
- gdfunc->_default_arg_ptr = nullptr;
- }
-
- gdfunc->_argument_count = p_func ? p_func->parameters.size() : 0;
- gdfunc->_stack_size = codegen.stack_max;
- gdfunc->_call_size = codegen.call_max;
- gdfunc->name = func_name;
#ifdef DEBUG_ENABLED
if (EngineDebugger::is_active()) {
String signature;
- //path
+ // Path.
if (p_script->get_path() != String()) {
signature += p_script->get_path();
}
- //loc
+ // Location.
if (p_func) {
signature += "::" + itos(p_func->body->start_line);
} else {
signature += "::0";
}
- //function and class
+ // Function and class.
if (p_class->identifier) {
signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
@@ -2376,65 +1786,41 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
signature += "::" + String(func_name);
}
- gdfunc->profile.signature = signature;
+ codegen.generator->set_signature(signature);
}
#endif
- gdfunc->_script = p_script;
- gdfunc->source = source;
-
-#ifdef DEBUG_ENABLED
- {
- gdfunc->func_cname = (String(source) + " - " + String(func_name)).utf8();
- gdfunc->_func_cname = gdfunc->func_cname.get_data();
- }
-
-#endif
if (p_func) {
- gdfunc->_initial_line = p_func->start_line;
+ codegen.generator->set_initial_line(p_func->start_line);
#ifdef TOOLS_ENABLED
-
p_script->member_lines[func_name] = p_func->start_line;
#endif
} else {
- gdfunc->_initial_line = 0;
+ codegen.generator->set_initial_line(0);
}
- if (codegen.debug_stack) {
- gdfunc->stack_debug = codegen.stack_debug;
- }
+ GDScriptFunction *gd_function = codegen.generator->write_end();
if (is_initializer) {
- p_script->initializer = gdfunc;
- }
- if (is_implicit_initializer) {
- p_script->implicit_initializer = gdfunc;
+ p_script->initializer = gd_function;
+ } else if (is_implicit_initializer) {
+ p_script->implicit_initializer = gd_function;
}
+ p_script->member_functions[func_name] = gd_function;
+
+ memdelete(codegen.generator);
+
return OK;
}
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
- Vector<int> bytecode;
+ Error error = OK;
CodeGen codegen;
+ codegen.generator = memnew(GDScriptByteCodeGenerator);
codegen.class_node = p_class;
codegen.script = p_script;
- codegen.function_node = nullptr;
- codegen.stack_max = 0;
- codegen.current_line = 0;
- codegen.call_max = 0;
- codegen.debug_stack = EngineDebugger::is_active();
- Vector<StringName> argnames;
-
- int stack_level = 0;
-
- if (p_is_setter) {
- codegen.add_stack_identifier(p_variable->setter_parameter->name, stack_level++);
- argnames.push_back(p_variable->setter_parameter->name);
- }
-
- codegen.alloc_stack(stack_level);
StringName func_name;
@@ -2443,76 +1829,33 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
} else {
func_name = "@" + p_variable->identifier->name + "_getter";
}
- codegen.function_name = func_name;
- Error err = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter, stack_level);
- if (err != OK) {
- return err;
+ GDScriptDataType return_type;
+ if (p_is_setter) {
+ return_type.has_type = true;
+ return_type.kind = GDScriptDataType::BUILTIN;
+ return_type.builtin_type = Variant::NIL;
+ } else {
+ return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
}
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_END);
-
- p_script->member_functions[func_name] = memnew(GDScriptFunction);
- GDScriptFunction *gdfunc = p_script->member_functions[func_name];
+ codegen.generator->write_start(p_script, func_name, false, p_variable->rpc_mode, return_type);
- gdfunc->_static = false;
- gdfunc->rpc_mode = p_variable->rpc_mode;
- gdfunc->argument_types.resize(p_is_setter ? 1 : 0);
- gdfunc->return_type = _gdtype_from_datatype(p_variable->get_datatype());
-#ifdef TOOLS_ENABLED
- gdfunc->arg_names = argnames;
-#endif
-
- // TODO: Unify this with function compiler.
- //constants
- if (codegen.constant_map.size()) {
- gdfunc->_constant_count = codegen.constant_map.size();
- gdfunc->constants.resize(codegen.constant_map.size());
- gdfunc->_constants_ptr = gdfunc->constants.ptrw();
- const Variant *K = nullptr;
- while ((K = codegen.constant_map.next(K))) {
- int idx = codegen.constant_map[*K];
- gdfunc->constants.write[idx] = *K;
- }
- } else {
- gdfunc->_constants_ptr = nullptr;
- gdfunc->_constant_count = 0;
+ if (p_is_setter) {
+ uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));
+ codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype()));
}
- //global names
- if (codegen.name_map.size()) {
- gdfunc->global_names.resize(codegen.name_map.size());
- gdfunc->_global_names_ptr = &gdfunc->global_names[0];
- for (Map<StringName, int>::Element *E = codegen.name_map.front(); E; E = E->next()) {
- gdfunc->global_names.write[E->get()] = E->key();
- }
- gdfunc->_global_names_count = gdfunc->global_names.size();
- } else {
- gdfunc->_global_names_ptr = nullptr;
- gdfunc->_global_names_count = 0;
+ error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter);
+ if (error) {
+ memdelete(codegen.generator);
+ return error;
}
-#ifdef TOOLS_ENABLED
- // Named globals
- if (codegen.named_globals.size()) {
- gdfunc->named_globals.resize(codegen.named_globals.size());
- gdfunc->_named_globals_ptr = gdfunc->named_globals.ptr();
- for (int i = 0; i < codegen.named_globals.size(); i++) {
- gdfunc->named_globals.write[i] = codegen.named_globals[i];
- }
- gdfunc->_named_globals_count = gdfunc->named_globals.size();
- }
-#endif
+ GDScriptFunction *gd_function = codegen.generator->write_end();
+
+ p_script->member_functions[func_name] = gd_function;
- gdfunc->code = codegen.opcodes;
- gdfunc->_code_ptr = &gdfunc->code[0];
- gdfunc->_code_size = codegen.opcodes.size();
- gdfunc->_default_arg_count = 0;
- gdfunc->_default_arg_ptr = nullptr;
- gdfunc->_argument_count = argnames.size();
- gdfunc->_stack_size = codegen.stack_max;
- gdfunc->_call_size = codegen.call_max;
- gdfunc->name = func_name;
#ifdef DEBUG_ENABLED
if (EngineDebugger::is_active()) {
String signature;
@@ -2531,29 +1874,15 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
signature += "::" + String(func_name);
}
- gdfunc->profile.signature = signature;
+ codegen.generator->set_signature(signature);
}
#endif
- gdfunc->_script = p_script;
- gdfunc->source = source;
+ codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
-#ifdef DEBUG_ENABLED
-
- {
- gdfunc->func_cname = (String(source) + " - " + String(func_name)).utf8();
- gdfunc->_func_cname = gdfunc->func_cname.get_data();
- }
-
-#endif
- gdfunc->_initial_line = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line;
#ifdef TOOLS_ENABLED
-
- p_script->member_lines[func_name] = gdfunc->_initial_line;
+ p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line;
#endif
-
- if (codegen.debug_stack) {
- gdfunc->stack_debug = codegen.stack_debug;
- }
+ memdelete(codegen.generator);
return OK;
}
@@ -2604,25 +1933,38 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->native = native;
} break;
case GDScriptDataType::GDSCRIPT: {
- Ref<GDScript> base = base_type.script_type;
+ Ref<GDScript> base = Ref<GDScript>(base_type.script_type);
p_script->base = base;
p_script->_base = base.ptr();
if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.class_type != nullptr) {
- if (!parsed_classes.has(p_script->_base)) {
- if (parsing_classes.has(p_script->_base)) {
- String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
- _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
- return ERR_PARSE_ERROR;
+ if (p_class->base_type.script_path == main_script->path) {
+ if (!parsed_classes.has(p_script->_base)) {
+ if (parsing_classes.has(p_script->_base)) {
+ String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
+ _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
+ return ERR_PARSE_ERROR;
+ }
+ Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
+ }
}
- Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
+ } else {
+ Error err = OK;
+ base = GDScriptCache::get_full_script(p_class->base_type.script_path, err, main_script->path);
if (err) {
return err;
}
+ if (base.is_null() && !base->is_valid()) {
+ return ERR_COMPILATION_FAILED;
+ }
}
}
p_script->member_indices = base->member_indices;
+ native = base->native;
+ p_script->native = native;
} break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
@@ -2660,7 +2002,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
break;
}
minfo.rpc_mode = variable->rpc_mode;
- minfo.data_type = _gdtype_from_datatype(variable->get_datatype());
+ minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
PropertyInfo prop_info = minfo.data_type;
prop_info.name = name;
@@ -2696,11 +2038,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
const GDScriptParser::ConstantNode *constant = member.constant;
StringName name = constant->identifier->name;
- ERR_CONTINUE(constant->initializer->type != GDScriptParser::Node::LITERAL);
-
- const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(constant->initializer);
-
- p_script->constants.insert(name, literal->value);
+ p_script->constants.insert(name, constant->initializer->reduced_value);
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = constant->start_line;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index e8601f69c7..157c801f56 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,111 +31,86 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
-#include "core/set.h"
+#include "core/templates/set.h"
#include "gdscript.h"
+#include "gdscript_codegen.h"
#include "gdscript_function.h"
#include "gdscript_parser.h"
class GDScriptCompiler {
- const GDScriptParser *parser;
+ const GDScriptParser *parser = nullptr;
Set<GDScript *> parsed_classes;
Set<GDScript *> parsing_classes;
- GDScript *main_script;
+ GDScript *main_script = nullptr;
+
struct CodeGen {
- GDScript *script;
- const GDScriptParser::ClassNode *class_node;
- const GDScriptParser::FunctionNode *function_node;
+ GDScript *script = nullptr;
+ const GDScriptParser::ClassNode *class_node = nullptr;
+ const GDScriptParser::FunctionNode *function_node = nullptr;
StringName function_name;
- bool debug_stack;
-
- List<Map<StringName, int>> stack_id_stack;
- Map<StringName, int> stack_identifiers;
-
- List<GDScriptFunction::StackDebug> stack_debug;
- List<Map<StringName, int>> block_identifier_stack;
- Map<StringName, int> block_identifiers;
- Map<StringName, int> local_named_constants;
-
- void add_stack_identifier(const StringName &p_id, int p_stackpos) {
- stack_identifiers[p_id] = p_stackpos;
- if (debug_stack) {
- block_identifiers[p_id] = p_stackpos;
- GDScriptFunction::StackDebug sd;
- sd.added = true;
- sd.line = current_line;
- sd.identifier = p_id;
- sd.pos = p_stackpos;
- stack_debug.push_back(sd);
- }
+ GDScriptCodeGenerator *generator = nullptr;
+ Map<StringName, GDScriptCodeGenerator::Address> parameters;
+ Map<StringName, GDScriptCodeGenerator::Address> locals;
+ List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack;
+
+ GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
+ uint32_t addr = generator->add_local(p_name, p_type);
+ locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type);
+ return locals[p_name];
}
- void push_stack_identifiers() {
- stack_id_stack.push_back(stack_identifiers);
- if (debug_stack) {
- block_identifier_stack.push_back(block_identifiers);
- block_identifiers.clear();
- }
+ GDScriptCodeGenerator::Address add_local_constant(const StringName &p_name, const Variant &p_value) {
+ uint32_t addr = generator->add_local_constant(p_name, p_value);
+ locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_CONSTANT, addr);
+ return locals[p_name];
}
- void pop_stack_identifiers() {
- stack_identifiers = stack_id_stack.back()->get();
- stack_id_stack.pop_back();
-
- if (debug_stack) {
- for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
- GDScriptFunction::StackDebug sd;
- sd.added = false;
- sd.identifier = E->key();
- sd.line = current_line;
- sd.pos = E->get();
- stack_debug.push_back(sd);
- }
- block_identifiers = block_identifier_stack.back()->get();
- block_identifier_stack.pop_back();
- }
+ GDScriptCodeGenerator::Address add_temporary(const GDScriptDataType &p_type = GDScriptDataType()) {
+ uint32_t addr = generator->add_temporary();
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::TEMPORARY, addr, p_type);
}
- HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
- Map<StringName, int> name_map;
-#ifdef TOOLS_ENABLED
- Vector<StringName> named_globals;
-#endif
-
- int get_name_map_pos(const StringName &p_identifier) {
- int ret;
- if (!name_map.has(p_identifier)) {
- ret = name_map.size();
- name_map[p_identifier] = ret;
- } else {
- ret = name_map[p_identifier];
+ GDScriptCodeGenerator::Address add_constant(const Variant &p_constant) {
+ GDScriptDataType type;
+ type.has_type = true;
+ type.kind = GDScriptDataType::BUILTIN;
+ type.builtin_type = p_constant.get_type();
+ if (type.builtin_type == Variant::OBJECT) {
+ Object *obj = p_constant;
+ if (obj) {
+ type.kind = GDScriptDataType::NATIVE;
+ type.native_type = obj->get_class_name();
+
+ Ref<Script> script = obj->get_script();
+ if (script.is_valid()) {
+ type.script_type = script.ptr();
+ Ref<GDScript> gdscript = script;
+ if (gdscript.is_valid()) {
+ type.kind = GDScriptDataType::GDSCRIPT;
+ } else {
+ type.kind = GDScriptDataType::SCRIPT;
+ }
+ }
+ } else {
+ type.builtin_type = Variant::NIL;
+ }
}
- return ret;
- }
- int get_constant_pos(const Variant &p_constant) {
- if (constant_map.has(p_constant)) {
- return constant_map[p_constant] | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
- }
- int pos = constant_map.size();
- constant_map[p_constant] = pos;
- return pos | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ uint32_t addr = generator->add_or_get_constant(p_constant);
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CONSTANT, addr, type);
}
- Vector<int> opcodes;
- void alloc_stack(int p_level) {
- if (p_level >= stack_max) {
- stack_max = p_level + 1;
- }
- }
- void alloc_call(int p_params) {
- if (p_params >= call_max) {
- call_max = p_params;
- }
+ void start_block() {
+ Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
+ locals_stack.push_back(old_locals);
+ generator->start_block();
}
- int current_line;
- int stack_max;
- int call_max;
+ void end_block() {
+ locals = locals_stack.back()->get();
+ locals_stack.pop_back();
+ generator->end_block();
+ }
};
bool _is_class_member_property(CodeGen &codegen, const StringName &p_name);
@@ -143,17 +118,16 @@ class GDScriptCompiler {
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
- bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::UnaryOpNode *on, Variant::Operator op, int p_stack_level);
- bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false, int p_index_addr = 0);
- bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, int p_stack_level, bool p_initializer = false, int p_index_addr = 0);
- bool _generate_typed_assign(CodeGen &codegen, int p_src_address, int p_dst_address, const GDScriptDataType &p_datatype, const GDScriptParser::DataType &p_value_type);
+ Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner = nullptr) const;
- int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::AssignmentNode *p_assignment, int p_stack_level, int p_index_addr = 0);
- int _parse_expression(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false, int p_index_addr = 0);
- Error _parse_match_pattern(CodeGen &codegen, const GDScriptParser::PatternNode *p_pattern, int p_stack_level, int p_value_addr, int p_type_addr, int &r_bound_variables, Vector<int> &r_patch_addresses, Vector<int> &r_block_patch_address);
- Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
+ GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
+ GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
+ void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
+ Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 88bf8cde0f..a426046797 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -30,8 +30,8 @@
#include "gdscript.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
#include "core/os/file_access.h"
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
@@ -39,7 +39,7 @@
#include "gdscript_tokenizer.h"
#ifdef TOOLS_ENABLED
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#endif
@@ -383,8 +383,8 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
}
bool skip = false;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (E->key() == GlobalConstants::get_global_constant_name(i)) {
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (E->key() == CoreConstants::get_global_constant_name(i)) {
skip = true;
break;
}
@@ -483,6 +483,8 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
#ifdef TOOLS_ENABLED
+#define COMPLETION_RECURSION_LIMIT 200
+
struct GDScriptCompletionIdentifier {
GDScriptParser::DataType type;
String enumeration;
@@ -766,9 +768,11 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite,
}
}
-static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result);
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth);
+
+static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
+ ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
-static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result) {
if (!p_parent_only) {
bool outer = false;
const GDScriptParser::ClassNode *clss = p_class;
@@ -788,6 +792,9 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
continue;
}
option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ if (member.constant->initializer) {
+ option.default_value = member.constant->initializer->reduced_value;
+ }
break;
case GDScriptParser::ClassNode::Member::CLASS:
if (p_only_functions) {
@@ -820,7 +827,6 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
break;
case GDScriptParser::ClassNode::Member::SIGNAL:
if (p_only_functions || outer) {
- clss = clss->outer;
continue;
}
option = ScriptCodeCompletionOption(member.signal->identifier->name, ScriptCodeCompletionOption::KIND_SIGNAL);
@@ -840,10 +846,12 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
base_type.type = p_class->base_type;
base_type.type.is_meta_type = p_static;
- _find_identifiers_in_base(base_type, p_only_functions, r_result);
+ _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1);
}
-static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) {
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
+ ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
+
GDScriptParser::DataType base_type = p_base.type;
bool _static = base_type.is_meta_type;
@@ -856,7 +864,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
while (!base_type.has_no_type()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
- _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result);
+ _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1);
// This already finds all parent identifiers, so we are done.
base_type = GDScriptParser::DataType();
} break;
@@ -965,7 +973,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -1007,14 +1016,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
-static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) {
+static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result, int p_recursion_depth) {
if (!p_only_functions && p_context.current_suite) {
// This includes function parameters, since they are also locals.
_find_identifiers_in_suite(p_context.current_suite, r_result);
}
if (p_context.current_class) {
- _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result);
+ _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
}
for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
@@ -1515,7 +1524,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = _guess_identifier_type_from_base(c, base, id, r_type);
} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
Callable::CallError err;
- Variant base_val = Variant::construct(base.type.builtin_type, nullptr, 0, err);
+ Variant base_val;
+ Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);
bool valid = false;
Variant res = base_val.get(index.value, &valid);
if (valid) {
@@ -1552,9 +1562,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
Callable::CallError ce;
bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, nullptr, 0, ce);
+ Variant d1;
+ Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);
+ Variant d2;
+ Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);
+
+ Variant v1 = (v1_use_value) ? p1.value : d1;
bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, nullptr, 0, ce);
+ Variant v2 = (v2_use_value) ? p2.value : d2;
// avoid potential invalid ops
if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
v2 = 1;
@@ -1944,7 +1959,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
@@ -2094,7 +2110,8 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
}
@@ -2131,9 +2148,9 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
r_result.insert(option.display, option);
}
} else {
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (CoreConstants::get_global_constant_enum(i) == current_enum) {
+ ScriptCodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
}
@@ -2249,7 +2266,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
case GDScriptParser::DataType::BUILTIN: {
if (base.get_type() == Variant::NIL) {
Callable::CallError err;
- base = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant::construct(base_type.builtin_type, base, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -2298,11 +2315,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
-
- if ((info.name == "load" || info.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
- }
-
r_arghint = _make_arguments_hint(info, p_argidx);
return;
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2399,6 +2411,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
Variant::get_constants_for_type(completion_context.builtin_type, &constants);
for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) {
ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ bool valid = false;
+ Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E->get(), &valid);
+ if (valid) {
+ option.default_value = default_value;
+ }
options.insert(option.display, option);
}
} break;
@@ -2453,7 +2470,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {
- _find_identifiers(completion_context, false, options);
+ _find_identifiers(completion_context, false, options, 0);
r_forced = true;
break;
}
@@ -2462,7 +2479,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
_find_enumeration_candidates(completion_context, type.enumeration, options);
r_forced = options.size() > 0;
} else {
- _find_identifiers(completion_context, false, options);
+ _find_identifiers(completion_context, false, options, 0);
r_forced = true;
}
} break;
@@ -2470,7 +2487,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
is_function = true;
[[fallthrough]];
case GDScriptParser::COMPLETION_IDENTIFIER: {
- _find_identifiers(completion_context, is_function, options);
+ _find_identifiers(completion_context, is_function, options, 0);
} break;
case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:
is_function = true;
@@ -2484,7 +2501,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
- _find_identifiers_in_base(base, is_function, options);
+ _find_identifiers_in_base(base, is_function, options, 0);
}
} break;
case GDScriptParser::COMPLETION_SUBSCRIPT: {
@@ -2504,7 +2521,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
c.current_class = nullptr;
}
- _find_identifiers_in_base(base, false, options);
+ _find_identifiers_in_base(base, false, options, 0);
} break;
case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
if (!completion_context.current_class) {
@@ -2530,7 +2547,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
// TODO: Improve this to only list types.
if (found) {
- _find_identifiers_in_base(base, false, options);
+ _find_identifiers_in_base(base, false, options, 0);
}
r_forced = true;
} break;
@@ -2651,7 +2668,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
if (!completion_context.current_class) {
break;
}
- _find_identifiers_in_class(completion_context.current_class, true, false, true, options);
+ _find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0);
} break;
}
@@ -2870,7 +2887,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
v = v_ref;
} else {
Callable::CallError err;
- v = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ Variant::construct(base_type.builtin_type, v, NULL, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
break;
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index a4e37a79f8..8372672cf7 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -34,6 +34,10 @@
#include "gdscript.h"
#include "gdscript_functions.h"
+#ifdef DEBUG_ENABLED
+#include "core/string/string_builder.h"
+#endif
+
Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
int address = p_address & ADDR_MASK;
@@ -105,9 +109,9 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
#ifdef TOOLS_ENABLED
case ADDR_TYPE_NAMED_GLOBAL: {
#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _named_globals_count, nullptr);
+ ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
#endif
- StringName id = _named_globals_ptr[address];
+ StringName id = _global_names_ptr[address];
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
@@ -212,7 +216,6 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
&&OPCODE_CALL_RETURN, \
&&OPCODE_CALL_ASYNC, \
&&OPCODE_CALL_BUILT_IN, \
- &&OPCODE_CALL_SELF, \
&&OPCODE_CALL_SELF_BASE, \
&&OPCODE_AWAIT, \
&&OPCODE_AWAIT_RESUME, \
@@ -328,7 +331,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
return Variant();
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
- Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
+ Variant arg;
+ Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
memnew_placement(&stack[i], Variant(arg));
} else {
memnew_placement(&stack[i], Variant(*p_args[i]));
@@ -617,7 +621,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
const StringName *index = &_global_names_ptr[indexname];
bool valid;
- dst->set_named(*index, *value, &valid);
+ dst->set_named(*index, *value, valid);
#ifdef DEBUG_ENABLED
if (!valid) {
@@ -644,10 +648,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool valid;
#ifdef DEBUG_ENABLED
//allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get_named(*index, &valid);
+ Variant ret = src->get_named(*index, valid);
#else
- *dst = src->get_named(*index, &valid);
+ *dst = src->get_named(*index, valid);
#endif
#ifdef DEBUG_ENABLED
if (!valid) {
@@ -752,7 +756,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (Variant::can_convert_strict(src->get_type(), var_type)) {
#endif // DEBUG_ENABLED
Callable::CallError ce;
- *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce);
+ Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
} else {
#ifdef DEBUG_ENABLED
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
@@ -854,7 +858,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
Callable::CallError err;
- *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
+ Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
@@ -952,7 +956,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GET_VARIANT_PTR(dst, 3 + argc);
Callable::CallError err;
- *dst = Variant::construct(t, (const Variant **)argptrs, argc, err);
+ Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
#ifdef DEBUG_ENABLED
if (err.error != Callable::CallError::CALL_OK) {
@@ -1044,7 +1048,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err;
if (call_ret) {
GET_VARIANT_PTR(ret, argc);
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err);
+ base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED
if (!call_async && ret->get_type() == Variant::OBJECT) {
// Check if getting a function state without await.
@@ -1055,14 +1059,15 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
err_text = "Got a freed object as a result of the call.";
OPCODE_BREAK;
}
- if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
err_text = R"(Trying to call an async function without "await".)";
OPCODE_BREAK;
}
}
#endif
} else {
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, nullptr, err);
+ Variant ret;
+ base->call(*methodname, (const Variant **)argptrs, argc, ret, err);
}
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -1090,6 +1095,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK;
}
}
+ } else if (methodstr == "call_recursive" && basestr == "TreeItem") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
}
err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
OPCODE_BREAK;
@@ -1139,10 +1151,6 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
- OPCODE(OPCODE_CALL_SELF) {
- OPCODE_BREAK;
- }
-
OPCODE(OPCODE_CALL_SELF_BASE) {
CHECK_SPACE(2);
int self_fun = _code_ptr[ip + 1];
@@ -1214,8 +1222,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_AWAIT) {
- int ipofs = 2;
- CHECK_SPACE(3);
+ CHECK_SPACE(2);
//do the oneshot connect
GET_VARIANT_PTR(argobj, 1);
@@ -1265,7 +1272,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.stack_size = _stack_size;
gdfs->state.self = self;
gdfs->state.alloca_size = alloca_size;
- gdfs->state.ip = ip + ipofs;
+ gdfs->state.ip = ip + 2;
gdfs->state.line = line;
gdfs->state.script = _script;
{
@@ -1884,3 +1891,506 @@ GDScriptFunctionState::~GDScriptFunctionState() {
instances_list.remove_from_list();
}
}
+
+#ifdef DEBUG_ENABLED
+static String _get_variant_string(const Variant &p_variant) {
+ String txt;
+ if (p_variant.get_type() == Variant::STRING) {
+ txt = "\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::STRING_NAME) {
+ txt = "&\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::NODE_PATH) {
+ txt = "^\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::OBJECT) {
+ Object *obj = p_variant;
+ if (!obj) {
+ txt = "null";
+ } else {
+ GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+ if (cls) {
+ txt += cls->get_name();
+ txt += " (class)";
+ } else {
+ txt = obj->get_class();
+ if (obj->get_script_instance()) {
+ txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+ }
+ }
+ }
+ } else {
+ txt = p_variant;
+ }
+ return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+ int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+ switch (p_address >> GDScriptFunction::ADDR_BITS) {
+ case GDScriptFunction::ADDR_TYPE_SELF: {
+ return "self";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS: {
+ return "class";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_MEMBER: {
+ return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
+ return "class_const(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
+ return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK: {
+ return "stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
+ return "var_stack(" + itos(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_GLOBAL: {
+ return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
+ return "named_global(" + p_function.get_global_name(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_NIL: {
+ return "nil";
+ } break;
+ }
+
+ return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+ for (int ip = 0; ip < _code_size;) {
+ StringBuilder text;
+ int incr = 0;
+
+ text += " ";
+ text += itos(ip);
+ text += ": ";
+
+ // This makes the compiler complain if some opcode is unchecked in the switch.
+ Opcode code = Opcode(_code_ptr[ip]);
+
+ switch (code) {
+ case OPCODE_OPERATOR: {
+ int operation = _code_ptr[ip + 1];
+
+ text += "operator ";
+
+ text += DADDR(4);
+ text += " = ";
+ text += DADDR(2);
+ text += " ";
+ text += Variant::get_operator_name(Variant::Operator(operation));
+ text += " ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_EXTENDS_TEST: {
+ text += "is object ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_IS_BUILTIN: {
+ text += "is builtin ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2]));
+
+ incr += 4;
+ } break;
+ case OPCODE_SET: {
+ text += "set ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET: {
+ text += "get ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_NAMED: {
+ text += "set_named ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED: {
+ text += "get_named ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_MEMBER: {
+ text += "set_member ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_GET_MEMBER: {
+ text += "get_member ";
+ text += DADDR(2);
+ text += " = ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "\"]";
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TRUE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = true";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_FALSE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = false";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_TYPED_BUILTIN: {
+ text += "assign typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]);
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "assign typed native (";
+ text += nc->get_name().operator String();
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 1]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "assign typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_BUILTIN: {
+ text += "cast builtin ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_NATIVE: {
+ Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
+
+ text += "cast native ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += nc->get_name();
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_SCRIPT: {
+ text += "cast ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(2);
+ text += " as ";
+ text += DADDR(1);
+
+ incr += 4;
+ } break;
+ case OPCODE_CONSTRUCT: {
+ Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
+ int argc = _code_ptr[ip + 2];
+
+ text += "construct ";
+ text += DADDR(3 + argc);
+ text += " = ";
+
+ text += Variant::get_type_name(t) + "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(i + 3);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_ARRAY: {
+ int argc = _code_ptr[ip + 1];
+ text += " make_array ";
+ text += DADDR(2 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(2 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+ int argc = _code_ptr[ip + 1];
+ text += "make_dict ";
+ text += DADDR(2 + argc * 2);
+ text += " = {";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(2 + i * 2 + 0);
+ text += ": ";
+ text += DADDR(2 + i * 2 + 1);
+ }
+
+ text += "}";
+
+ incr += 3 + argc * 2;
+ } break;
+ case OPCODE_CALL:
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL_ASYNC: {
+ bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
+ bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
+
+ if (ret) {
+ text += "call-ret ";
+ } else if (async) {
+ text += "call-async ";
+ } else {
+ text += "call ";
+ }
+
+ int argc = _code_ptr[ip + 1];
+ if (ret || async) {
+ text += DADDR(4 + argc) + " = ";
+ }
+
+ text += DADDR(2) + ".";
+ text += String(_global_names_ptr[_code_ptr[ip + 3]]);
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(4 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_BUILT_IN: {
+ text += "call-built-in ";
+
+ int argc = _code_ptr[ip + 2];
+ text += DADDR(3 + argc) + " = ";
+
+ text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1]));
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(3 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+ text += "call-self-base ";
+
+ int argc = _code_ptr[ip + 2];
+ text += DADDR(3 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 1]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0)
+ text += ", ";
+ text += DADDR(3 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_AWAIT: {
+ text += "await ";
+ text += DADDR(1);
+
+ incr += 2;
+ } break;
+ case OPCODE_AWAIT_RESUME: {
+ text += "await resume ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP: {
+ text += "jump ";
+ text += itos(_code_ptr[ip + 1]);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP_IF: {
+ text += "jump-if ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_IF_NOT: {
+ text += "jump-if-not ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+ text += "jump-to-default-argument ";
+
+ incr = 1;
+ } break;
+ case OPCODE_RETURN: {
+ text += "return ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_ITERATE_BEGIN: {
+ text += "for-init ";
+ text += DADDR(4);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 3]);
+
+ incr += 5;
+ } break;
+ case OPCODE_ITERATE: {
+ text += "for-loop ";
+ text += DADDR(4);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 3]);
+
+ incr += 5;
+ } break;
+ case OPCODE_LINE: {
+ int line = _code_ptr[ip + 1] - 1;
+ if (line >= 0 && line < p_code_lines.size()) {
+ text += "line ";
+ text += itos(line + 1);
+ text += ": ";
+ text += p_code_lines[line];
+ } else {
+ text += "";
+ }
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSERT: {
+ text += "assert (";
+ text += DADDR(1);
+ text += ", ";
+ text += DADDR(2);
+ text += ")";
+
+ incr += 3;
+ } break;
+ case OPCODE_BREAKPOINT: {
+ text += "breakpoint";
+
+ incr += 1;
+ } break;
+ case OPCODE_END: {
+ text += "== END ==";
+
+ incr += 1;
+ } break;
+ }
+
+ ip += incr;
+ if (text.get_string_length() > 0) {
+ print_line(text.as_string());
+ }
+ }
+}
+#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 771baf6a08..50eadcaf86 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -31,13 +31,13 @@
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
+#include "core/object/reference.h"
+#include "core/object/script_language.h"
#include "core/os/thread.h"
-#include "core/pair.h"
-#include "core/reference.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
-#include "core/string_name.h"
-#include "core/variant.h"
+#include "core/string/string_name.h"
+#include "core/templates/pair.h"
+#include "core/templates/self_list.h"
+#include "core/variant/variant.h"
class GDScriptInstance;
class GDScript;
@@ -56,7 +56,8 @@ struct GDScriptDataType {
bool has_type = false;
Variant::Type builtin_type = Variant::NIL;
StringName native_type;
- Ref<Script> script_type;
+ Script *script_type = nullptr;
+ Ref<Script> script_type_ref;
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
if (!has_type) {
@@ -182,7 +183,6 @@ public:
OPCODE_CALL_RETURN,
OPCODE_CALL_ASYNC,
OPCODE_CALL_BUILT_IN,
- OPCODE_CALL_SELF,
OPCODE_CALL_SELF_BASE,
OPCODE_AWAIT,
OPCODE_AWAIT_RESUME,
@@ -224,6 +224,7 @@ public:
private:
friend class GDScriptCompiler;
+ friend class GDScriptByteCodeGenerator;
StringName source;
@@ -232,10 +233,6 @@ private:
int _constant_count;
const StringName *_global_names_ptr;
int _global_names_count;
-#ifdef TOOLS_ENABLED
- const StringName *_named_globals_ptr;
- int _named_globals_count;
-#endif
const int *_default_arg_ptr;
int _default_arg_count;
const int *_code_ptr;
@@ -252,9 +249,6 @@ private:
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
-#ifdef TOOLS_ENABLED
- Vector<StringName> named_globals;
-#endif
Vector<int> default_arguments;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
@@ -344,6 +338,10 @@ public:
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
+#ifdef DEBUG_ENABLED
+ void disassemble(const Vector<String> &p_code_lines) const;
+#endif
+
_FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; }
GDScriptFunction();
~GDScriptFunction();
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
index fefbf906f0..3a7c1a8676 100644
--- a/modules/gdscript/gdscript_functions.cpp
+++ b/modules/gdscript/gdscript_functions.cpp
@@ -30,14 +30,13 @@
#include "gdscript_functions.h"
-#include "core/class_db.h"
-#include "core/func_ref.h"
#include "core/io/json.h"
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
+#include "core/object/class_db.h"
+#include "core/object/reference.h"
#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
#include "gdscript.h"
const char *GDScriptFunctions::get_func_name(Function p_func) {
@@ -83,7 +82,8 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"randomize",
"randi",
"randf",
- "rand_range",
+ "randf_range",
+ "randi_range",
"seed",
"rand_seed",
"deg2rad",
@@ -99,7 +99,6 @@ const char *GDScriptFunctions::get_func_name(Function p_func) {
"clamp",
"nearest_po2",
"weakref",
- "funcref",
"convert",
"typeof",
"type_exists",
@@ -286,7 +285,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
int64_t i = *p_args[0];
r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
} else if (p_args[0]->get_type() == Variant::FLOAT) {
- real_t r = *p_args[0];
+ double r = *p_args[0];
r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
@@ -419,7 +418,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
Math::randomize();
r_ret = Variant();
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
VALIDATE_ARG_COUNT(0);
r_ret = Math::rand();
} break;
@@ -427,12 +426,18 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_COUNT(0);
r_ret = Math::randf();
} break;
- case MATH_RANDOM: {
+ case MATH_RANDF_RANGE: {
VALIDATE_ARG_COUNT(2);
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
} break;
+ case MATH_RANDI_RANGE: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret = Math::random((int)*p_args[0], (int)*p_args[1]);
+ } break;
case MATH_SEED: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
@@ -505,8 +510,8 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
+ double a = *p_args[0];
+ double b = *p_args[1];
r_ret = MAX(a, b);
}
@@ -522,8 +527,8 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
+ double a = *p_args[0];
+ double b = *p_args[1];
r_ret = MIN(a, b);
}
@@ -540,9 +545,9 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
VALIDATE_ARG_NUM(1);
VALIDATE_ARG_NUM(2);
- real_t a = *p_args[0];
- real_t b = *p_args[1];
- real_t c = *p_args[2];
+ double a = *p_args[0];
+ double b = *p_args[1];
+ double c = *p_args[2];
r_ret = CLAMP(a, b, c);
}
@@ -582,31 +587,6 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
return;
}
} break;
- case FUNC_FUNCREF: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- return;
- }
- if (p_args[1]->get_type() != Variant::STRING && p_args[1]->get_type() != Variant::NODE_PATH) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- Ref<FuncRef> fr = memnew(FuncRef);
-
- fr->set_instance(*p_args[0]);
- fr->set_function(*p_args[1]);
-
- r_ret = fr;
-
- } break;
case TYPE_CONVERT: {
VALIDATE_ARG_COUNT(2);
VALIDATE_ARG_NUM(1);
@@ -619,7 +599,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
return;
} else {
- r_ret = Variant::construct(Variant::Type(type), p_args, 1, r_error);
+ Variant::construct(Variant::Type(type), r_ret, p_args, 1, r_error);
}
} break;
case TYPE_OF: {
@@ -635,7 +615,7 @@ void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_
case TEXT_CHAR: {
VALIDATE_ARG_COUNT(1);
VALIDATE_ARG_NUM(0);
- CharType result[2] = { *p_args[0], 0 };
+ char32_t result[2] = { *p_args[0], 0 };
r_ret = String(result);
} break;
case TEXT_ORD: {
@@ -1655,7 +1635,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::NIL;
return mi;
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
MethodInfo mi("randi");
mi.return_val.type = Variant::INT;
return mi;
@@ -1665,11 +1645,16 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
mi.return_val.type = Variant::FLOAT;
return mi;
} break;
- case MATH_RANDOM: {
- MethodInfo mi("rand_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
+ case MATH_RANDF_RANGE: {
+ MethodInfo mi("randf_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
mi.return_val.type = Variant::FLOAT;
return mi;
} break;
+ case MATH_RANDI_RANGE: {
+ MethodInfo mi("randi_range", PropertyInfo(Variant::INT, "from"), PropertyInfo(Variant::INT, "to"));
+ mi.return_val.type = Variant::INT;
+ return mi;
+ } break;
case MATH_SEED: {
MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
mi.return_val.type = Variant::NIL;
@@ -1749,13 +1734,6 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
- case FUNC_FUNCREF: {
- MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "FuncRef";
- return mi;
-
- } break;
case TYPE_CONVERT: {
MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
mi.return_val.type = Variant::NIL;
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
index 2c6dc02913..005b49c5da 100644
--- a/modules/gdscript/gdscript_functions.h
+++ b/modules/gdscript/gdscript_functions.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_FUNCTIONS_H
#define GDSCRIPT_FUNCTIONS_H
-#include "core/variant.h"
+#include "core/variant/variant.h"
class GDScriptFunctions {
public:
@@ -73,9 +73,10 @@ public:
MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
- MATH_RAND,
+ MATH_RANDI,
MATH_RANDF,
- MATH_RANDOM,
+ MATH_RANDF_RANGE,
+ MATH_RANDI_RANGE,
MATH_SEED,
MATH_RANDSEED,
MATH_DEG2RAD,
@@ -91,7 +92,6 @@ public:
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
- FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 0967f74285..fde3662d66 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -30,15 +30,15 @@
#include "gdscript_parser.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
@@ -65,7 +65,7 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
builtin_types["Basis"] = Variant::BASIS;
builtin_types["Transform"] = Variant::TRANSFORM;
builtin_types["Color"] = Variant::COLOR;
- builtin_types["RID"] = Variant::_RID;
+ builtin_types["RID"] = Variant::RID;
builtin_types["Object"] = Variant::OBJECT;
builtin_types["StringName"] = Variant::STRING_NAME;
builtin_types["NodePath"] = Variant::NODE_PATH;
@@ -327,7 +327,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
bool found = false;
const String &line = lines[i];
for (int j = 0; j < line.size(); j++) {
- if (line[j] == CharType(0xFFFF)) {
+ if (line[j] == char32_t(0xFFFF)) {
found = true;
break;
} else if (line[j] == '\t') {
@@ -466,44 +466,40 @@ void GDScriptParser::pop_multiline() {
}
bool GDScriptParser::is_statement_end() {
- return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON);
+ return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF);
}
void GDScriptParser::end_statement(const String &p_context) {
bool found = false;
- while (is_statement_end()) {
+ while (is_statement_end() && !is_at_end()) {
// Remove sequential newlines/semicolons.
found = true;
advance();
}
- if (!found) {
+ if (!found && !is_at_end()) {
push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name()));
}
}
void GDScriptParser::parse_program() {
- if (current.type == GDScriptTokenizer::Token::TK_EOF) {
- // Empty file.
- push_error("Source file is empty.");
- return;
- }
-
head = alloc_node<ClassNode>();
current_class = head;
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
// Check for @tool annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
- if (annotation->name == "@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) {
- push_error(R"(Expected newline after "@tool" annotation.)");
- }
- // @tool annotation has no specific target.
- annotation->apply(this, nullptr);
- } else {
- annotation_stack.push_back(annotation);
+ if (annotation != nullptr) {
+ if (annotation->name == "@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) {
+ push_error(R"(Expected newline after "@tool" annotation.)");
+ }
+ // @tool annotation has no specific target.
+ annotation->apply(this, nullptr);
+ } else {
+ annotation_stack.push_back(annotation);
+ }
}
}
@@ -590,6 +586,14 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
return n_class;
}
+ if (match(GDScriptTokenizer::Token::EXTENDS)) {
+ if (n_class->extends_used) {
+ push_error(R"(Cannot use "extends" more than once in the same class.)");
+ }
+ parse_extends();
+ end_statement("superclass");
+ }
+
parse_class_body();
consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
@@ -635,7 +639,6 @@ void GDScriptParser::parse_extends() {
current_class->extends_path = previous.literal;
if (!match(GDScriptTokenizer::Token::PERIOD)) {
- end_statement("superclass path");
return;
}
}
@@ -990,9 +993,15 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
signal->identifier = parse_identifier();
if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
- while (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
+ do {
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
+ }
+
ParameterNode *parameter = parse_parameter();
if (parameter == nullptr) {
+ push_error("Expected signal parameter name.");
break;
}
if (parameter->default_value != nullptr) {
@@ -1004,7 +1013,8 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
signal->parameters_indices[parameter->identifier->name] = signal->parameters.size();
signal->parameters.push_back(parameter);
}
- }
+ } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
+
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
}
@@ -1026,17 +1036,22 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
push_multiline(true);
consume(GDScriptTokenizer::Token::BRACE_OPEN, vformat(R"(Expected "{" after %s.)", named ? "enum name" : R"("enum")"));
- int current_value = 0;
+ HashMap<StringName, int> elements;
do {
if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
break; // Allow trailing comma.
}
- if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifer for enum key.)")) {
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
EnumNode::Value item;
item.identifier = parse_identifier();
+ item.parent_enum = enum_node;
+ item.line = previous.start_line;
+ item.leftmost_column = previous.leftmost_column;
- if (!named) {
+ if (elements.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
+ } else if (!named) {
// TODO: Abstract this recursive member check.
ClassNode *parent = current_class;
while (parent != nullptr) {
@@ -1048,19 +1063,18 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
}
}
- if (match(GDScriptTokenizer::Token::EQUAL)) {
- if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected integer value after "=".)")) {
- item.custom_value = parse_literal();
+ elements[item.identifier->name] = item.line;
- if (item.custom_value->value.get_type() != Variant::INT) {
- push_error(R"(Expected integer value after "=".)");
- item.custom_value = nullptr;
- } else {
- current_value = item.custom_value->value;
- }
+ if (match(GDScriptTokenizer::Token::EQUAL)) {
+ ExpressionNode *value = parse_expression(false);
+ if (value == nullptr) {
+ push_error(R"(Expected expression value after "=".)");
}
+ item.custom_value = value;
}
- item.value = current_value++;
+ item.rightmost_column = previous.rightmost_column;
+
+ item.index = enum_node->values.size();
enum_node->values.push_back(item);
if (!named) {
// Add as member of current class.
@@ -1142,6 +1156,9 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
if (match(GDScriptTokenizer::Token::FORWARD_ARROW)) {
make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, function);
function->return_type = parse_type(true);
+ if (function->return_type == nullptr) {
+ push_error(R"(Expected return type or "void" after "->".)");
+ }
}
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
@@ -1346,7 +1363,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
advance();
ReturnNode *n_return = alloc_node<ReturnNode>();
if (!is_statement_end()) {
- if (current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
+ if (current_function && current_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
push_error(R"(Constructor cannot return a value.)");
}
n_return->return_value = parse_expression(false);
@@ -1459,7 +1476,9 @@ GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
}
current_suite->has_continue = true;
end_statement(R"("continue")");
- return alloc_node<ContinueNode>();
+ ContinueNode *cont = alloc_node<ContinueNode>();
+ cont->is_for_match = is_continue_match;
+ return cont;
}
GDScriptParser::ForNode *GDScriptParser::parse_for() {
@@ -1478,10 +1497,12 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
// Save break/continue state.
bool could_break = can_break;
bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
// Allow break/continue.
can_break = true;
can_continue = true;
+ is_continue_match = false;
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
@@ -1494,6 +1515,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
// Reset break/continue state.
can_break = could_break;
can_continue = could_continue;
+ is_continue_match = was_continue_match;
return n_for;
}
@@ -1628,8 +1650,10 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
// Save continue state.
bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
// Allow continue for match.
can_continue = true;
+ is_continue_match = true;
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
@@ -1646,6 +1670,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
// Restore continue state.
can_continue = could_continue;
+ is_continue_match = was_continue_match;
return branch;
}
@@ -1803,16 +1828,19 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
// Save break/continue state.
bool could_break = can_break;
bool could_continue = can_continue;
+ bool was_continue_match = is_continue_match;
// Allow break/continue.
can_break = true;
can_continue = true;
+ is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
// Reset break/continue state.
can_break = could_break;
can_continue = could_continue;
+ is_continue_match = was_continue_match;
return n_while;
}
@@ -1932,8 +1960,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_previous_operand, bool p_can_assign) {
- if (!current_function || current_function->is_static) {
- push_error(R"(Cannot use "self" outside a non-static function.)");
+ if (current_function && current_function->is_static) {
+ push_error(R"(Cannot use "self" inside a static function.)");
}
SelfNode *self = alloc_node<SelfNode>();
self->current_class = current_class;
@@ -2458,26 +2486,28 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
}
}
- if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Arguments.
- push_completion_call(call);
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, 0, true);
- int argument_index = 0;
- do {
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, argument_index++, true);
- if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Allow for trailing comma.
- break;
- }
- ExpressionNode *argument = parse_expression(false);
- if (argument == nullptr) {
- push_error(R"(Expected expression as the function argument.)");
- } else {
- call->arguments.push_back(argument);
- }
- } while (match(GDScriptTokenizer::Token::COMMA));
- pop_completion_call();
+ // Arguments.
+ CompletionType ct = COMPLETION_CALL_ARGUMENTS;
+ if (get_builtin_function(call->function_name) == GDScriptFunctions::RESOURCE_LOAD) {
+ ct = COMPLETION_RESOURCE_PATH;
}
+ push_completion_call(call);
+ int argument_index = 0;
+ do {
+ make_completion_context(ct, call, argument_index++, true);
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
+ }
+ ExpressionNode *argument = parse_expression(false);
+ if (argument == nullptr) {
+ push_error(R"(Expected expression as the function argument.)");
+ } else {
+ call->arguments.push_back(argument);
+ }
+ ct = COMPLETION_CALL_ARGUMENTS;
+ } while (match(GDScriptTokenizer::Token::COMMA));
+ pop_completion_call();
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
@@ -2488,27 +2518,30 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
if (match(GDScriptTokenizer::Token::LITERAL)) {
if (previous.literal.get_type() != Variant::STRING) {
- push_error(R"(Expect node path as string or identifer after "$".)");
+ push_error(R"(Expect node path as string or identifier after "$".)");
return nullptr;
}
GetNodeNode *get_node = alloc_node<GetNodeNode>();
make_completion_context(COMPLETION_GET_NODE, get_node);
get_node->string = parse_literal();
return get_node;
- } else if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
+ } else if (current.is_node_name()) {
GetNodeNode *get_node = alloc_node<GetNodeNode>();
int chain_position = 0;
do {
make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
- if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expect node identifer after "/".)")) {
+ if (!current.is_node_name()) {
+ push_error(R"(Expect node path after "/".)");
return nullptr;
}
- IdentifierNode *identifier = parse_identifier();
+ advance();
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
get_node->chain.push_back(identifier);
} while (match(GDScriptTokenizer::Token::SLASH));
return get_node;
} else {
- push_error(R"(Expect node path as string or identifer after "$".)");
+ push_error(R"(Expect node path as string or identifier after "$".)");
return nullptr;
}
}
@@ -2527,29 +2560,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
if (preload->path == nullptr) {
push_error(R"(Expected resource path after "(".)");
- } else if (preload->path->type != Node::LITERAL) {
- push_error("Preloaded path must be a constant string.");
- } else {
- LiteralNode *path = static_cast<LiteralNode *>(preload->path);
- if (path->value.get_type() != Variant::STRING) {
- push_error("Preloaded path must be a constant string.");
- } else {
- preload->resolved_path = path->value;
- // TODO: Save this as script dependency.
- if (preload->resolved_path.is_rel_path()) {
- preload->resolved_path = script_path.get_base_dir().plus_file(preload->resolved_path);
- }
- preload->resolved_path = preload->resolved_path.simplify_path();
- if (!FileAccess::exists(preload->resolved_path)) {
- push_error(vformat(R"(Preload file "%s" does not exist.)", preload->resolved_path));
- } else {
- // TODO: Don't load if validating: use completion cache.
- preload->resource = ResourceLoader::load(preload->resolved_path);
- if (preload->resource.is_null()) {
- push_error(vformat(R"(Could not preload resource file "%s".)", preload->resolved_path));
- }
- }
- }
}
pop_completion_call();
@@ -2794,7 +2804,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
Callable::CallError error;
Vector<Variant> args = varray(string->name);
const Variant *name = args.ptr();
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(name), 1, error));
+ Variant r;
+ Variant::construct(parameter.type, r, &(name), 1, error);
+ p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
@@ -2816,7 +2828,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
}
Callable::CallError error;
const Variant *args = &value;
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(args), 1, error));
+ Variant r;
+ Variant::construct(parameter.type, r, &(args), 1, error);
+ p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index edfe330c0c..b24acc4778 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,23 +31,23 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
-#include "core/hash_map.h"
#include "core/io/multiplayer_api.h"
-#include "core/list.h"
-#include "core/map.h"
-#include "core/reference.h"
-#include "core/resource.h"
-#include "core/script_language.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/io/resource.h"
+#include "core/object/reference.h"
+#include "core/object/script_language.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "gdscript_cache.h"
#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
#ifdef DEBUG_ENABLED
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#include "gdscript_warning.h"
#endif // DEBUG_ENABLED
@@ -405,7 +405,10 @@ public:
struct EnumNode : public Node {
struct Value {
IdentifierNode *identifier = nullptr;
- LiteralNode *custom_value = nullptr;
+ ExpressionNode *custom_value = nullptr;
+ EnumNode *parent_enum = nullptr;
+ int index = -1;
+ bool resolved = false;
int value = 0;
int line = 0;
int leftmost_column = 0;
@@ -606,6 +609,7 @@ public:
};
struct ContinueNode : public Node {
+ bool is_for_match = false;
ContinueNode() {
type = CONTINUE;
}
@@ -1076,6 +1080,7 @@ private:
bool panic_mode = false;
bool can_break = false;
bool can_continue = false;
+ bool is_continue_match = false; // Whether a `continue` will act on a `match`.
bool is_ignoring_warnings = false;
List<bool> multiline_stack;
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 7a4bdd88ba..b91777ede1 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -30,7 +30,7 @@
#include "gdscript_tokenizer.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -156,6 +156,64 @@ const char *GDScriptTokenizer::Token::get_name() const {
return token_names[type];
}
+bool GDScriptTokenizer::Token::is_identifier() const {
+ // Note: Most keywords should not be recognized as identifiers.
+ // These are only exceptions for stuff that already is on the engine's API.
+ switch (type) {
+ case IDENTIFIER:
+ case MATCH: // Used in String.match().
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool GDScriptTokenizer::Token::is_node_name() const {
+ // This is meant to allow keywords with the $ notation, but not as general identifiers.
+ switch (type) {
+ case IDENTIFIER:
+ case AND:
+ case AS:
+ case ASSERT:
+ case AWAIT:
+ case BREAK:
+ case BREAKPOINT:
+ case CLASS_NAME:
+ case CLASS:
+ case CONST:
+ case CONTINUE:
+ case ELIF:
+ case ELSE:
+ case ENUM:
+ case EXTENDS:
+ case FOR:
+ case FUNC:
+ case IF:
+ case IN:
+ case IS:
+ case MATCH:
+ case NAMESPACE:
+ case NOT:
+ case OR:
+ case PASS:
+ case PRELOAD:
+ case RETURN:
+ case SELF:
+ case SIGNAL:
+ case STATIC:
+ case SUPER:
+ case TRAIT:
+ case UNDERSCORE:
+ case VAR:
+ case VOID:
+ case WHILE:
+ case YIELD:
+ return true;
+ default:
+ return false;
+ }
+}
+
String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {
ERR_FAIL_INDEX_V_MSG(p_token_type, Token::TK_MAX, "<error>", "Using token type out of the enum.");
return token_names[p_token_type];
@@ -164,7 +222,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {
void GDScriptTokenizer::set_source_code(const String &p_source_code) {
source = p_source_code;
if (source.empty()) {
- _source = L"";
+ _source = U"";
} else {
_source = source.ptr();
}
@@ -205,7 +263,7 @@ bool GDScriptTokenizer::is_past_cursor() const {
return true;
}
-CharType GDScriptTokenizer::_advance() {
+char32_t GDScriptTokenizer::_advance() {
if (unlikely(_is_at_end())) {
return '\0';
}
@@ -224,15 +282,15 @@ CharType GDScriptTokenizer::_advance() {
return _peek(-1);
}
-void GDScriptTokenizer::push_paren(CharType p_char) {
+void GDScriptTokenizer::push_paren(char32_t p_char) {
paren_stack.push_back(p_char);
}
-bool GDScriptTokenizer::pop_paren(CharType p_expected) {
+bool GDScriptTokenizer::pop_paren(char32_t p_expected) {
if (paren_stack.empty()) {
return false;
}
- CharType actual = paren_stack.back()->get();
+ char32_t actual = paren_stack.back()->get();
paren_stack.pop_back();
return actual == p_expected;
@@ -244,19 +302,19 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() {
return error;
}
-static bool _is_alphanumeric(CharType c) {
+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(CharType c) {
+static bool _is_digit(char32_t c) {
return (c >= '0' && c <= '9');
}
-static bool _is_hex_digit(CharType c) {
+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(CharType c) {
+static bool _is_binary_digit(char32_t c) {
return (c == '0' || c == '1');
}
@@ -346,7 +404,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) {
error_stack.push_back(p_error);
}
-GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) {
+GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) {
if (paren_stack.empty()) {
return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren));
}
@@ -355,8 +413,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(CharType p_paren) {
return error;
}
-GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(CharType p_test, Token::Type p_double_type) {
- const CharType *next = _current + 1;
+GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, Token::Type p_double_type) {
+ const char32_t *next = _current + 1;
int chars = 2; // Two already matched.
// Test before consuming characters, since we don't want to consume more than needed.
@@ -520,7 +578,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
}
void GDScriptTokenizer::newline(bool p_make_token) {
- // Don't overwrite previous newline, nor create if we want a line contination.
+ // Don't overwrite previous newline, nor create if we want a line continuation.
if (p_make_token && !pending_newline && !line_continuation) {
Token newline(Token::NEWLINE);
newline.start_line = line;
@@ -544,7 +602,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
bool has_decimal = false;
bool has_exponent = false;
bool has_error = false;
- bool (*digit_check_func)(CharType) = _is_digit;
+ bool (*digit_check_func)(char32_t) = _is_digit;
if (_peek(-1) == '.') {
has_decimal = true;
@@ -563,7 +621,19 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
}
// Allow '_' to be used in a number, for readability.
+ bool previous_was_underscore = false;
while (digit_check_func(_peek()) || _peek() == '_') {
+ if (_peek() == '_') {
+ if (previous_was_underscore) {
+ Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = true;
+ }
_advance();
}
@@ -614,7 +684,27 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
}
// Consume exponent digits.
+ if (!_is_digit(_peek())) {
+ Token error = make_error(R"(Expected exponent value after "e".)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = false;
while (_is_digit(_peek()) || _peek() == '_') {
+ if (_peek() == '_') {
+ if (previous_was_underscore) {
+ Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
+ error.start_column = column;
+ error.leftmost_column = column;
+ error.end_column = column + 1;
+ error.rightmost_column = column + 1;
+ push_error(error);
+ }
+ previous_was_underscore = true;
+ }
_advance();
}
}
@@ -672,7 +762,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
}
- CharType quote_char = _peek(-1);
+ char32_t quote_char = _peek(-1);
if (_peek() == quote_char && _peek(1) == quote_char) {
is_multiline = true;
@@ -689,7 +779,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
return make_error("Unterminated string.");
}
- CharType ch = _peek();
+ char32_t ch = _peek();
if (ch == '\\') {
// Escape pattern.
@@ -699,13 +789,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
}
// Grab escape character.
- CharType code = _peek();
+ char32_t code = _peek();
_advance();
if (_is_at_end()) {
return make_error("Unterminated string.");
}
- CharType escaped = 0;
+ char32_t escaped = 0;
bool valid_escape = true;
switch (code) {
@@ -746,8 +836,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
return make_error("Unterminated string.");
}
- CharType digit = _peek();
- CharType value = 0;
+ char32_t digit = _peek();
+ char32_t value = 0;
if (digit >= '0' && digit <= '9') {
value = digit - '0';
} else if (digit >= 'a' && digit <= 'f') {
@@ -850,7 +940,7 @@ void GDScriptTokenizer::check_indent() {
}
for (;;) {
- CharType current_indent_char = _peek();
+ char32_t current_indent_char = _peek();
int indent_count = 0;
if (current_indent_char != ' ' && current_indent_char != '\t' && current_indent_char != '\r' && current_indent_char != '\n' && current_indent_char != '#') {
@@ -880,7 +970,7 @@ void GDScriptTokenizer::check_indent() {
// Check indent level.
bool mixed = false;
while (!_is_at_end()) {
- CharType space = _peek();
+ char32_t space = _peek();
if (space == '\t') {
// Consider individual tab columns.
column += tab_size - 1;
@@ -949,7 +1039,7 @@ void GDScriptTokenizer::check_indent() {
// First time indenting, choose character now.
indent_char = current_indent_char;
} else if (current_indent_char != indent_char) {
- Token error = make_error(vformat("Used \"%c\" for indentation instead \"%c\" as used before in the file.", String(&current_indent_char, 1).c_escape(), String(&indent_char, 1).c_escape()));
+ Token error = make_error(vformat("Used \"%s\" for indentation instead \"%s\" as used before in the file.", String(&current_indent_char, 1).c_escape(), String(&indent_char, 1).c_escape()));
error.start_line = line;
error.start_column = 1;
error.leftmost_column = 1;
@@ -1013,7 +1103,7 @@ void GDScriptTokenizer::_skip_whitespace() {
}
for (;;) {
- CharType c = _peek();
+ char32_t c = _peek();
switch (c) {
case ' ':
_advance();
@@ -1102,7 +1192,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
return make_token(Token::TK_EOF);
}
- const CharType c = _advance();
+ const char32_t c = _advance();
if (c == '\\') {
// Line continuation with backslash.
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 059a226924..d51f1f250f 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,10 +31,10 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
-#include "core/list.h"
-#include "core/set.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/templates/list.h"
+#include "core/templates/set.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
class GDScriptTokenizer {
public:
@@ -168,9 +168,9 @@ public:
String source;
const char *get_name() const;
- // TODO: Allow some keywords as identifiers?
- bool is_identifier() const { return type == IDENTIFIER; }
- StringName get_identifier() const { return literal; }
+ bool is_identifier() const;
+ bool is_node_name() const;
+ StringName get_identifier() const { return source; }
Token(Type p_type) {
type = p_type;
@@ -183,14 +183,14 @@ public:
private:
String source;
- const CharType *_source = nullptr;
- const CharType *_current = nullptr;
+ const char32_t *_source = nullptr;
+ const char32_t *_current = nullptr;
int line = -1, column = -1;
int cursor_line = -1, cursor_column = -1;
int tab_size = 4;
// Keep track of multichar tokens.
- const CharType *_start = nullptr;
+ const char32_t *_start = nullptr;
int start_line = 0, start_column = 0;
int leftmost_column = 0, rightmost_column = 0;
@@ -202,30 +202,30 @@ private:
Token last_newline;
int pending_indents = 0;
List<int> indent_stack;
- List<CharType> paren_stack;
- CharType indent_char = '\0';
+ List<char32_t> paren_stack;
+ char32_t indent_char = '\0';
int position = 0;
int length = 0;
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
- _FORCE_INLINE_ CharType _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
+ _FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
int indent_level() const { return indent_stack.size(); }
bool has_error() const { return !error_stack.empty(); }
Token pop_error();
- CharType _advance();
+ char32_t _advance();
void _skip_whitespace();
void check_indent();
Token make_error(const String &p_message);
void push_error(const String &p_message);
void push_error(const Token &p_error);
- Token make_paren_error(CharType p_paren);
+ Token make_paren_error(char32_t p_paren);
Token make_token(Token::Type p_type);
Token make_literal(const Variant &p_literal);
Token make_identifier(const StringName &p_identifier);
- Token check_vcs_marker(CharType p_test, Token::Type p_double_type);
- void push_paren(CharType p_char);
- bool pop_paren(CharType p_expected);
+ Token check_vcs_marker(char32_t p_test, Token::Type p_double_type);
+ void push_paren(char32_t p_char);
+ bool pop_paren(char32_t p_expected);
void newline(bool p_make_token);
Token number();
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 105facd9d0..56704d3e0a 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -30,7 +30,7 @@
#include "gdscript_warning.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index e183d6f302..e0857703d8 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -33,8 +33,8 @@
#ifdef DEBUG_ENABLED
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
class GDScriptWarning {
public:
diff --git a/modules/gdscript/icons/GDScript.svg b/modules/gdscript/icons/GDScript.svg
index 953bb9ae9e..aa59125ea9 100644
--- a/modules/gdscript/icons/GDScript.svg
+++ b/modules/gdscript/icons/GDScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539h2l0.56445-2.2578a5 5 0 0 0 0.6875 -0.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 0.28516 -0.68555l2.2539-0.5625v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539h2l.56445-2.2578a5 5 0 0 0 .6875-.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 .28516-.68555l2.2539-.5625v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index ae7898fdf2..668dfd4835 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -237,7 +237,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
case ClassNode::Member::ENUM_VALUE: {
lsp::DocumentSymbol symbol;
- symbol.name = m.constant->identifier->name;
+ symbol.name = m.enum_value.identifier->name;
symbol.kind = lsp::SymbolKind::EnumMember;
symbol.deprecated = false;
symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line);
@@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int start_pos = p_position.character;
for (int c = p_position.character; c >= 0; c--) {
start_pos = c;
- CharType ch = line[c];
+ char32_t ch = line[c];
bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
if (!valid_char) {
break;
@@ -500,7 +500,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++) {
- CharType ch = line[c];
+ char32_t ch = line[c];
bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
if (!valid_char) {
break;
@@ -552,7 +552,7 @@ Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_positi
}
while (c >= 0) {
- const CharType &character = line[c];
+ const char32_t &character = line[c];
if (character == ')') {
++bracket_stack;
} else if (character == '(') {
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 0c031d7883..bb02d3dc99 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -32,7 +32,7 @@
#define GDSCRIPT_EXTEND_PARSER_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "lsp.hpp"
#ifndef LINE_NUMBER_TO_INDEX
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 2a67d2ff4f..6ddb0d149e 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -30,9 +30,9 @@
#include "gdscript_language_protocol.h"
+#include "core/config/project_settings.h"
#include "core/io/json.h"
#include "core/os/copymem.h"
-#include "core/project_settings.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index b2fd0c31f9..2a5755bec6 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -31,8 +31,8 @@
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
+#include "core/object/reference.h"
#include "core/os/file_access.h"
-#include "core/reference.h"
#include "lsp.hpp"
class GDScriptTextDocument : public Reference {
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 776193e37c..f6643d07f9 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -32,8 +32,8 @@
#include "../gdscript.h"
#include "../gdscript_parser.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/config/project_settings.h"
+#include "core/object/script_language.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index e45b06747d..fc244c6357 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -32,7 +32,7 @@
#define GDSCRIPT_WORKSPACE_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index cf27a1578c..bf32c1c978 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -31,8 +31,8 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
-#include "core/class_db.h"
-#include "core/list.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.h"
#include "editor/doc_data.h"
namespace lsp {
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index c554cbac05..065f01e654 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -39,6 +39,11 @@
#include "gdscript_cache.h"
#include "gdscript_tokenizer.h"
+#ifdef TESTS_ENABLED
+#include "tests/test_gdscript.h"
+#include "tests/test_macros.h"
+#endif
+
GDScriptLanguage *script_language_gd = nullptr;
Ref<ResourceFormatLoaderGDScript> resource_loader_gd;
Ref<ResourceFormatSaverGDScript> resource_saver_gd;
@@ -54,7 +59,7 @@ GDScriptCache *gdscript_cache = nullptr;
#include "editor/gdscript_translation_parser_plugin.h"
#ifndef GDSCRIPT_NO_LSP
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "language_server/gdscript_language_server.h"
#endif // !GDSCRIPT_NO_LSP
@@ -79,7 +84,7 @@ public:
return;
}
- // TODO: Readd compiled/encrypted GDScript on export.
+ // TODO: Readd compiled GDScript on export.
return;
}
};
@@ -153,3 +158,26 @@ void unregister_gdscript_types() {
GDScriptParser::cleanup();
GDScriptAnalyzer::cleanup();
}
+
+#ifdef TESTS_ENABLED
+void test_tokenizer() {
+ TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
+}
+
+void test_parser() {
+ TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
+}
+
+void test_compiler() {
+ TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
+}
+
+void test_bytecode() {
+ TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE);
+}
+
+REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
+REGISTER_TEST_COMMAND("gdscript-parser", &test_parser);
+REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler);
+REGISTER_TEST_COMMAND("gdscript-bytecode", &test_bytecode);
+#endif
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
new file mode 100644
index 0000000000..50b3783388
--- /dev/null
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -0,0 +1,307 @@
+/*************************************************************************/
+/* test_gdscript.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "test_gdscript.h"
+
+#include "core/config/project_settings.h"
+#include "core/io/file_access_pack.h"
+#include "core/os/file_access.h"
+#include "core/os/main_loop.h"
+#include "core/os/os.h"
+#include "core/string/string_builder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "modules/gdscript/gdscript_analyzer.h"
+#include "modules/gdscript/gdscript_compiler.h"
+#include "modules/gdscript/gdscript_parser.h"
+#include "modules/gdscript/gdscript_tokenizer.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#endif
+
+namespace TestGDScript {
+
+static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) {
+ GDScriptTokenizer tokenizer;
+ tokenizer.set_source_code(p_code);
+
+ int tab_size = 4;
+#ifdef TOOLS_ENABLED
+ if (EditorSettings::get_singleton()) {
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ }
+#endif // TOOLS_ENABLED
+ String tab = String(" ").repeat(tab_size);
+
+ GDScriptTokenizer::Token current = tokenizer.scan();
+ while (current.type != GDScriptTokenizer::Token::TK_EOF) {
+ StringBuilder token;
+ token += " --> "; // Padding for line number.
+
+ for (int l = current.start_line; l <= current.end_line; l++) {
+ print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab));
+ }
+
+ {
+ // Print carets to point at the token.
+ StringBuilder pointer;
+ pointer += " "; // Padding for line number.
+ int rightmost_column = current.rightmost_column;
+ if (current.end_line > current.start_line) {
+ rightmost_column--; // Don't point to the newline as a column.
+ }
+ for (int col = 1; col < rightmost_column; col++) {
+ if (col < current.leftmost_column) {
+ pointer += " ";
+ } else {
+ pointer += "^";
+ }
+ }
+ print_line(pointer.as_string());
+ }
+
+ token += current.get_name();
+
+ if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) {
+ token += "(";
+ token += Variant::get_type_name(current.literal.get_type());
+ token += ") ";
+ token += current.literal;
+ }
+
+ print_line(token.as_string());
+
+ print_line("-------------------------------------------------------");
+
+ current = tokenizer.scan();
+ }
+
+ print_line(current.get_name()); // Should be EOF
+}
+
+static void test_parser(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
+ GDScriptParser parser;
+ Error err = parser.parse(p_code, p_script_path, false);
+
+ if (err != OK) {
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ }
+
+ GDScriptParser::TreePrinter printer;
+
+ printer.print_tree(parser);
+}
+
+static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
+ GDScriptParser parser;
+ Error err = parser.parse(p_code, p_script_path, false);
+
+ if (err != OK) {
+ print_line("Error in parser:");
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ return;
+ }
+
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+
+ if (err != OK) {
+ print_line("Error in analyzer:");
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
+ const GDScriptParser::ParserError &error = E->get();
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ return;
+ }
+
+ GDScriptCompiler compiler;
+ Ref<GDScript> script;
+ script.instance();
+ script->set_path(p_script_path);
+
+ err = compiler.compile(&parser, script.ptr(), false);
+
+ if (err) {
+ print_line("Error in compiler:");
+ print_line(vformat("%02d:%02d: %s", compiler.get_error_line(), compiler.get_error_column(), compiler.get_error()));
+ return;
+ }
+
+ for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
+ const GDScriptFunction *func = E->value();
+
+ String signature = "Disassembling " + func->get_name().operator String() + "(";
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += func->get_argument_name(i);
+ }
+ print_line(signature + ")");
+
+ func->disassemble(p_lines);
+ print_line("");
+ print_line("");
+ }
+}
+
+void init_autoloads() {
+ Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ // First pass, add the constants so they exist before any script is loaded.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
+ }
+ }
+ }
+
+ // Second pass, load into global constants.
+ for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+ const ProjectSettings::AutoloadInfo &info = E->get();
+
+ if (!info.is_singleton) {
+ // Skip non-singletons since we don't have a scene tree here anyway.
+ continue;
+ }
+
+ 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->instance();
+ } else if (res->is_class("Script")) {
+ Ref<Script> script_res = res;
+ StringName ibt = script_res->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::instance(ibt);
+
+ ERR_CONTINUE_MSG(obj == nullptr,
+ "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
+ String(ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(script_res);
+ }
+
+ ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ n->set_name(info.name);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, n);
+ }
+ }
+}
+
+void test(TestType p_type) {
+ List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
+
+ if (cmdlargs.empty()) {
+ return;
+ }
+
+ String test = cmdlargs.back()->get();
+ if (!test.ends_with(".gd")) {
+ print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test);
+ return;
+ }
+
+ FileAccessRef fa = FileAccess::open(test, FileAccess::READ);
+ ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test);
+
+ // Init PackedData since it's used by ProjectSettings.
+ PackedData *packed_data = memnew(PackedData);
+
+ // Setup project settings since it's needed by the languages to get the global scripts.
+ // This also sets up the base resource path.
+ Error err = ProjectSettings::get_singleton()->setup(fa->get_path_absolute().get_base_dir(), String(), true);
+ if (err) {
+ print_line("Could not load project settings.");
+ // Keep going since some scripts still work without this.
+ }
+
+ // Initialize the language for the test routine.
+ ScriptServer::init_languages();
+ init_autoloads();
+
+ Vector<uint8_t> buf;
+ int flen = fa->get_len();
+ buf.resize(fa->get_len() + 1);
+ fa->get_buffer(buf.ptrw(), flen);
+ buf.write[flen] = 0;
+
+ String code;
+ code.parse_utf8((const char *)&buf[0]);
+
+ Vector<String> lines;
+ int last = 0;
+ for (int i = 0; i <= code.length(); i++) {
+ if (code[i] == '\n' || code[i] == 0) {
+ lines.push_back(code.substr(last, i - last));
+ last = i + 1;
+ }
+ }
+
+ switch (p_type) {
+ case TEST_TOKENIZER:
+ test_tokenizer(code, lines);
+ break;
+ case TEST_PARSER:
+ test_parser(code, test, lines);
+ break;
+ case TEST_COMPILER:
+ test_compiler(code, test, lines);
+ break;
+ case TEST_BYTECODE:
+ print_line("Not implemented.");
+ }
+
+ // Destroy stuff we set up earlier.
+ ScriptServer::finish_languages();
+ memdelete(packed_data);
+}
+
+} // namespace TestGDScript
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
new file mode 100644
index 0000000000..5aa962dcf8
--- /dev/null
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* test_gdscript.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 TEST_GDSCRIPT_H
+#define TEST_GDSCRIPT_H
+
+namespace TestGDScript {
+
+enum TestType {
+ TEST_TOKENIZER,
+ TEST_PARSER,
+ TEST_COMPILER,
+ TEST_BYTECODE,
+};
+
+void test(TestType p_type);
+
+} // namespace TestGDScript
+
+#endif // TEST_GDSCRIPT_H
diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub
index c1d23a138b..58c033c75d 100644
--- a/modules/glslang/SCsub
+++ b/modules/glslang/SCsub
@@ -9,6 +9,7 @@ env_glslang = env_modules.Clone()
if env["builtin_glslang"]:
thirdparty_dir = "#thirdparty/glslang/"
thirdparty_sources = [
+ "glslang/CInterface/glslang_c_interface.cpp",
"glslang/MachineIndependent/RemoveTree.cpp",
"glslang/MachineIndependent/ParseHelper.cpp",
"glslang/MachineIndependent/iomapper.cpp",
@@ -30,7 +31,6 @@ if env["builtin_glslang"]:
"glslang/MachineIndependent/intermOut.cpp",
"glslang/MachineIndependent/SymbolTable.cpp",
"glslang/MachineIndependent/glslang_tab.cpp",
- "glslang/MachineIndependent/pch.cpp",
"glslang/MachineIndependent/Versions.cpp",
"glslang/MachineIndependent/ShaderLang.cpp",
"glslang/MachineIndependent/parseConst.cpp",
@@ -40,6 +40,7 @@ if env["builtin_glslang"]:
"glslang/GenericCodeGen/Link.cpp",
"glslang/GenericCodeGen/CodeGen.cpp",
"OGLCompilersDLL/InitializeDll.cpp",
+ "SPIRV/CInterface/spirv_c_interface.cpp",
"SPIRV/InReadableOrder.cpp",
"SPIRV/GlslangToSpv.cpp",
"SPIRV/SpvBuilder.cpp",
@@ -49,6 +50,7 @@ if env["builtin_glslang"]:
"SPIRV/SPVRemapper.cpp",
"SPIRV/SpvPostProcess.cpp",
"SPIRV/Logger.cpp",
+ "StandAlone/ResourceLimits.cpp",
]
if env["platform"] == "windows":
@@ -64,6 +66,8 @@ if env["builtin_glslang"]:
else:
env_glslang.Prepend(CPPPATH=[thirdparty_dir])
+ env_glslang.Append(CPPDEFINES=["ENABLE_OPT=0"])
+
env_thirdparty = env_glslang.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index dd28c4ad4a..3238e0108e 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -33,115 +33,10 @@
#include "servers/rendering/rendering_device.h"
#include <SPIRV/GlslangToSpv.h>
+#include <StandAlone/ResourceLimits.h>
#include <glslang/Include/Types.h>
#include <glslang/Public/ShaderLang.h>
-static const TBuiltInResource default_builtin_resource = {
- /*maxLights*/ 32,
- /*maxClipPlanes*/ 6,
- /*maxTextureUnits*/ 32,
- /*maxTextureCoords*/ 32,
- /*maxVertexAttribs*/ 64,
- /*maxVertexUniformComponents*/ 4096,
- /*maxVaryingFloats*/ 64,
- /*maxVertexTextureImageUnits*/ 32,
- /*maxCombinedTextureImageUnits*/ 80,
- /*maxTextureImageUnits*/ 32,
- /*maxFragmentUniformComponents*/ 4096,
- /*maxDrawBuffers*/ 32,
- /*maxVertexUniformVectors*/ 128,
- /*maxVaryingVectors*/ 8,
- /*maxFragmentUniformVectors*/ 16,
- /*maxVertexOutputVectors*/ 16,
- /*maxFragmentInputVectors*/ 15,
- /*minProgramTexelOffset*/ -8,
- /*maxProgramTexelOffset*/ 7,
- /*maxClipDistances*/ 8,
- /*maxComputeWorkGroupCountX*/ 65535,
- /*maxComputeWorkGroupCountY*/ 65535,
- /*maxComputeWorkGroupCountZ*/ 65535,
- /*maxComputeWorkGroupSizeX*/ 1024,
- /*maxComputeWorkGroupSizeY*/ 1024,
- /*maxComputeWorkGroupSizeZ*/ 64,
- /*maxComputeUniformComponents*/ 1024,
- /*maxComputeTextureImageUnits*/ 16,
- /*maxComputeImageUniforms*/ 8,
- /*maxComputeAtomicCounters*/ 8,
- /*maxComputeAtomicCounterBuffers*/ 1,
- /*maxVaryingComponents*/ 60,
- /*maxVertexOutputComponents*/ 64,
- /*maxGeometryInputComponents*/ 64,
- /*maxGeometryOutputComponents*/ 128,
- /*maxFragmentInputComponents*/ 128,
- /*maxImageUnits*/ 8,
- /*maxCombinedImageUnitsAndFragmentOutputs*/ 8,
- /*maxCombinedShaderOutputResources*/ 8,
- /*maxImageSamples*/ 0,
- /*maxVertexImageUniforms*/ 0,
- /*maxTessControlImageUniforms*/ 0,
- /*maxTessEvaluationImageUniforms*/ 0,
- /*maxGeometryImageUniforms*/ 0,
- /*maxFragmentImageUniforms*/ 8,
- /*maxCombinedImageUniforms*/ 8,
- /*maxGeometryTextureImageUnits*/ 16,
- /*maxGeometryOutputVertices*/ 256,
- /*maxGeometryTotalOutputComponents*/ 1024,
- /*maxGeometryUniformComponents*/ 1024,
- /*maxGeometryVaryingComponents*/ 64,
- /*maxTessControlInputComponents*/ 128,
- /*maxTessControlOutputComponents*/ 128,
- /*maxTessControlTextureImageUnits*/ 16,
- /*maxTessControlUniformComponents*/ 1024,
- /*maxTessControlTotalOutputComponents*/ 4096,
- /*maxTessEvaluationInputComponents*/ 128,
- /*maxTessEvaluationOutputComponents*/ 128,
- /*maxTessEvaluationTextureImageUnits*/ 16,
- /*maxTessEvaluationUniformComponents*/ 1024,
- /*maxTessPatchComponents*/ 120,
- /*maxPatchVertices*/ 32,
- /*maxTessGenLevel*/ 64,
- /*maxViewports*/ 16,
- /*maxVertexAtomicCounters*/ 0,
- /*maxTessControlAtomicCounters*/ 0,
- /*maxTessEvaluationAtomicCounters*/ 0,
- /*maxGeometryAtomicCounters*/ 0,
- /*maxFragmentAtomicCounters*/ 8,
- /*maxCombinedAtomicCounters*/ 8,
- /*maxAtomicCounterBindings*/ 1,
- /*maxVertexAtomicCounterBuffers*/ 0,
- /*maxTessControlAtomicCounterBuffers*/ 0,
- /*maxTessEvaluationAtomicCounterBuffers*/ 0,
- /*maxGeometryAtomicCounterBuffers*/ 0,
- /*maxFragmentAtomicCounterBuffers*/ 1,
- /*maxCombinedAtomicCounterBuffers*/ 1,
- /*maxAtomicCounterBufferSize*/ 16384,
- /*maxTransformFeedbackBuffers*/ 4,
- /*maxTransformFeedbackInterleavedComponents*/ 64,
- /*maxCullDistances*/ 8,
- /*maxCombinedClipAndCullDistances*/ 8,
- /*maxSamples*/ 4,
- /*maxMeshOutputVerticesNV*/ 0,
- /*maxMeshOutputPrimitivesNV*/ 0,
- /*maxMeshWorkGroupSizeX_NV*/ 0,
- /*maxMeshWorkGroupSizeY_NV*/ 0,
- /*maxMeshWorkGroupSizeZ_NV*/ 0,
- /*maxTaskWorkGroupSizeX_NV*/ 0,
- /*maxTaskWorkGroupSizeY_NV*/ 0,
- /*maxTaskWorkGroupSizeZ_NV*/ 0,
- /*maxMeshViewCountNV*/ 0,
- /*limits*/ {
- /*nonInductiveForLoops*/ true,
- /*whileLoops*/ true,
- /*doWhileLoops*/ true,
- /*generalUniformIndexing*/ true,
- /*generalAttributeMatrixVectorIndexing*/ true,
- /*generalVaryingIndexing*/ true,
- /*generalSamplerIndexing*/ true,
- /*generalVariableIndexing*/ true,
- /*generalConstantMatrixVectorIndexing*/ true,
- }
-};
-
static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error) {
Vector<uint8_t> ret;
@@ -175,7 +70,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
std::string pre_processed_code;
//preprocess
- if (!shader.preprocess(&default_builtin_resource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
+ if (!shader.preprocess(&glslang::DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
if (r_error) {
(*r_error) = "Failed pre-process:\n";
(*r_error) += shader.getInfoLog();
@@ -190,7 +85,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
shader.setStrings(&cs_strings, 1);
//parse
- if (!shader.parse(&default_builtin_resource, DefaultVersion, false, messages)) {
+ if (!shader.parse(&glslang::DefaultTBuiltInResource, DefaultVersion, false, messages)) {
if (r_error) {
(*r_error) = "Failed parse:\n";
(*r_error) += shader.getInfoLog();
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 79220da7c2..4dccb03369 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -10,7 +10,9 @@
Internally, a GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
+ <link title="Using gridmaps">https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
+ <link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
+ <link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link>
</tutorials>
<methods>
<method name="clear">
@@ -205,7 +207,7 @@
GridMaps act as static bodies, meaning they aren't affected by gravity or other forces. They only affect other physics bodies that collide with them.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this GridMap detects collisions in.
+ The physics layers this GridMap detects collisions in. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary].
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index ccf1e49693..633b209f0f 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -31,7 +31,7 @@
#include "grid_map.h"
#include "core/io/marshalls.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "scene/3d/light_3d.h"
#include "scene/resources/mesh_library.h"
#include "scene/resources/surface_tool.h"
@@ -706,7 +706,7 @@ void GridMap::_update_visibility() {
Octant *octant = e->value();
for (int i = 0; i < octant->multimesh_instances.size(); i++) {
const Octant::MultimeshInstance &mi = octant->multimesh_instances[i];
- RS::get_singleton()->instance_set_visible(mi.instance, is_visible());
+ RS::get_singleton()->instance_set_visible(mi.instance, is_visible_in_tree());
}
}
}
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 7e136ff9bb..ee17a52d31 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -93,7 +93,7 @@ class GridMapEditor : public VBoxContainer {
List<SetItem> set_items;
- GridMap *node;
+ GridMap *node = nullptr;
MeshLibrary *last_mesh_library;
ClipMode clip_mode;
diff --git a/modules/gridmap/icons/GridMap.svg b/modules/gridmap/icons/GridMap.svg
index eafe1211f2..7a36fd888c 100644
--- a/modules/gridmap/icons/GridMap.svg
+++ b/modules/gridmap/icons/GridMap.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/></svg>
diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp
index 906e506b62..ab384fa942 100644
--- a/modules/gridmap/register_types.cpp
+++ b/modules/gridmap/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
#ifndef _3D_DISABLED
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "grid_map.h"
#include "grid_map_editor_plugin.h"
#endif
diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index 333b1cf377..af3741bae9 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -31,7 +31,7 @@
#include "image_loader_hdr.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
String header = f->get_token();
diff --git a/modules/icloud/SCsub b/modules/icloud/SCsub
new file mode 100644
index 0000000000..805a484600
--- /dev/null
+++ b/modules/icloud/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_icloud = env_modules.Clone()
+
+# (iOS) Enable module support
+env_icloud.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_icloud.add_source_files(modules_sources, "*.cpp")
+env_icloud.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_icloud_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/icloud/config.py b/modules/icloud/config.py
new file mode 100644
index 0000000000..e68603fc93
--- /dev/null
+++ b/modules/icloud/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return platform == "iphone"
+
+
+def configure(env):
+ pass
diff --git a/modules/icloud/icloud.gdip b/modules/icloud/icloud.gdip
new file mode 100644
index 0000000000..9f81be8a34
--- /dev/null
+++ b/modules/icloud/icloud.gdip
@@ -0,0 +1,17 @@
+[config]
+name="iCloud"
+binary="icloud_lib.a"
+
+initialization="register_icloud_types"
+deinitialization="unregister_icloud_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=[]
+
+capabilities=[]
+
+files=[]
+
+[plist]
diff --git a/modules/icloud/icloud.h b/modules/icloud/icloud.h
new file mode 100644
index 0000000000..35eede0bf9
--- /dev/null
+++ b/modules/icloud/icloud.h
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* icloud.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 ICLOUD_H
+#define ICLOUD_H
+
+#include "core/object/class_db.h"
+
+class ICloud : public Object {
+ GDCLASS(ICloud, Object);
+
+ static ICloud *instance;
+ static void _bind_methods();
+
+ List<Variant> pending_events;
+
+public:
+ Error remove_key(String p_param);
+ Array set_key_values(Dictionary p_params);
+ Variant get_key_value(String p_param);
+ Error synchronize_key_values();
+ Variant get_all_key_values();
+
+ int get_pending_event_count();
+ Variant pop_pending_event();
+
+ static ICloud *get_singleton();
+
+ ICloud();
+ ~ICloud();
+};
+
+#endif
diff --git a/modules/icloud/icloud.mm b/modules/icloud/icloud.mm
new file mode 100644
index 0000000000..8a8ddbefe9
--- /dev/null
+++ b/modules/icloud/icloud.mm
@@ -0,0 +1,345 @@
+/*************************************************************************/
+/* icloud.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "icloud.h"
+
+#import "platform/iphone/app_delegate.h"
+
+#import <Foundation/Foundation.h>
+
+ICloud *ICloud::instance = NULL;
+
+void ICloud::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("remove_key"), &ICloud::remove_key);
+
+ ClassDB::bind_method(D_METHOD("set_key_values"), &ICloud::set_key_values);
+ ClassDB::bind_method(D_METHOD("get_key_value"), &ICloud::get_key_value);
+
+ ClassDB::bind_method(D_METHOD("synchronize_key_values"), &ICloud::synchronize_key_values);
+ ClassDB::bind_method(D_METHOD("get_all_key_values"), &ICloud::get_all_key_values);
+
+ ClassDB::bind_method(D_METHOD("get_pending_event_count"), &ICloud::get_pending_event_count);
+ ClassDB::bind_method(D_METHOD("pop_pending_event"), &ICloud::pop_pending_event);
+};
+
+int ICloud::get_pending_event_count() {
+ return pending_events.size();
+};
+
+Variant ICloud::pop_pending_event() {
+ Variant front = pending_events.front()->get();
+ pending_events.pop_front();
+
+ return front;
+};
+
+ICloud *ICloud::get_singleton() {
+ return instance;
+};
+
+//convert from apple's abstract type to godot's abstract type....
+Variant nsobject_to_variant(NSObject *object) {
+ if ([object isKindOfClass:[NSString class]]) {
+ const char *str = [(NSString *)object UTF8String];
+ return String::utf8(str != NULL ? str : "");
+ } else if ([object isKindOfClass:[NSData class]]) {
+ PackedByteArray ret;
+ NSData *data = (NSData *)object;
+ if ([data length] > 0) {
+ ret.resize([data length]);
+ {
+ // PackedByteArray::Write w = ret.write();
+ copymem((void *)ret.ptr(), [data bytes], [data length]);
+ }
+ }
+ return ret;
+ } else if ([object isKindOfClass:[NSArray class]]) {
+ Array result;
+ NSArray *array = (NSArray *)object;
+ for (NSUInteger i = 0; i < [array count]; ++i) {
+ NSObject *value = [array objectAtIndex:i];
+ result.push_back(nsobject_to_variant(value));
+ }
+ return result;
+ } else if ([object isKindOfClass:[NSDictionary class]]) {
+ Dictionary result;
+ NSDictionary *dic = (NSDictionary *)object;
+
+ NSArray *keys = [dic allKeys];
+ int count = [keys count];
+ for (int i = 0; i < count; ++i) {
+ NSObject *k = [keys objectAtIndex:i];
+ NSObject *v = [dic objectForKey:k];
+
+ result[nsobject_to_variant(k)] = nsobject_to_variant(v);
+ }
+ return result;
+ } else if ([object isKindOfClass:[NSNumber class]]) {
+ //Every type except numbers can reliably identify its type. The following is comparing to the *internal* representation, which isn't guaranteed to match the type that was used to create it, and is not advised, particularly when dealing with potential platform differences (ie, 32/64 bit)
+ //To avoid errors, we'll cast as broadly as possible, and only return int or float.
+ //bool, char, int, uint, longlong -> int
+ //float, double -> float
+ NSNumber *num = (NSNumber *)object;
+ if (strcmp([num objCType], @encode(BOOL)) == 0) {
+ return Variant((int)[num boolValue]);
+ } else if (strcmp([num objCType], @encode(char)) == 0) {
+ return Variant((int)[num charValue]);
+ } else if (strcmp([num objCType], @encode(int)) == 0) {
+ return Variant([num intValue]);
+ } else if (strcmp([num objCType], @encode(unsigned int)) == 0) {
+ return Variant((int)[num unsignedIntValue]);
+ } else if (strcmp([num objCType], @encode(long long)) == 0) {
+ return Variant((int)[num longValue]);
+ } else if (strcmp([num objCType], @encode(float)) == 0) {
+ return Variant([num floatValue]);
+ } else if (strcmp([num objCType], @encode(double)) == 0) {
+ return Variant((float)[num doubleValue]);
+ } else {
+ return Variant();
+ }
+ } else if ([object isKindOfClass:[NSDate class]]) {
+ //this is a type that icloud supports...but how did you submit it in the first place?
+ //I guess this is a type that *might* show up, if you were, say, trying to make your game
+ //compatible with existing cloud data written by another engine's version of your game
+ WARN_PRINT("NSDate unsupported, returning null Variant");
+ return Variant();
+ } else if ([object isKindOfClass:[NSNull class]] or object == nil) {
+ return Variant();
+ } else {
+ WARN_PRINT("Trying to convert unknown NSObject type to Variant");
+ return Variant();
+ }
+}
+
+NSObject *variant_to_nsobject(Variant v) {
+ if (v.get_type() == Variant::STRING) {
+ return [[NSString alloc] initWithUTF8String:((String)v).utf8().get_data()];
+ } else if (v.get_type() == Variant::FLOAT) {
+ return [NSNumber numberWithDouble:(double)v];
+ } else if (v.get_type() == Variant::INT) {
+ return [NSNumber numberWithLongLong:(long)(int)v];
+ } else if (v.get_type() == Variant::BOOL) {
+ return [NSNumber numberWithBool:BOOL((bool)v)];
+ } else if (v.get_type() == Variant::DICTIONARY) {
+ NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
+ Dictionary dic = v;
+ Array keys = dic.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ NSString *key = [[NSString alloc] initWithUTF8String:((String)(keys[i])).utf8().get_data()];
+ NSObject *value = variant_to_nsobject(dic[keys[i]]);
+
+ if (key == NULL || value == NULL) {
+ return NULL;
+ }
+
+ [result setObject:value forKey:key];
+ }
+ return result;
+ } else if (v.get_type() == Variant::ARRAY) {
+ NSMutableArray *result = [[NSMutableArray alloc] init];
+ Array arr = v;
+ for (int i = 0; i < arr.size(); ++i) {
+ NSObject *value = variant_to_nsobject(arr[i]);
+ if (value == NULL) {
+ //trying to add something unsupported to the array. cancel the whole array
+ return NULL;
+ }
+ [result addObject:value];
+ }
+ return result;
+ } else if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ PackedByteArray arr = v;
+ // PackedByteArray::Read r = arr.read();
+ NSData *result = [NSData dataWithBytes:arr.ptr() length:arr.size()];
+ return result;
+ }
+ WARN_PRINT(String("Could not add unsupported type to iCloud: '" + Variant::get_type_name(v.get_type()) + "'").utf8().get_data());
+ return NULL;
+}
+
+Error ICloud::remove_key(String p_param) {
+ NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ if (![[store dictionaryRepresentation] objectForKey:key]) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ [store removeObjectForKey:key];
+ return OK;
+}
+
+//return an array of the keys that could not be set
+Array ICloud::set_key_values(Dictionary p_params) {
+ Array keys = p_params.keys();
+
+ Array error_keys;
+
+ for (int i = 0; i < keys.size(); ++i) {
+ String variant_key = keys[i];
+ Variant variant_value = p_params[variant_key];
+
+ NSString *key = [[NSString alloc] initWithUTF8String:variant_key.utf8().get_data()];
+ if (key == NULL) {
+ error_keys.push_back(variant_key);
+ continue;
+ }
+
+ NSObject *value = variant_to_nsobject(variant_value);
+
+ if (value == NULL) {
+ error_keys.push_back(variant_key);
+ continue;
+ }
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ [store setObject:value forKey:key];
+ }
+
+ return error_keys;
+}
+
+Variant ICloud::get_key_value(String p_param) {
+ NSString *key = [[NSString alloc] initWithUTF8String:p_param.utf8().get_data()];
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ if (![[store dictionaryRepresentation] objectForKey:key]) {
+ return Variant();
+ }
+
+ Variant result = nsobject_to_variant([[store dictionaryRepresentation] objectForKey:key]);
+
+ return result;
+}
+
+Variant ICloud::get_all_key_values() {
+ Dictionary result;
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ NSDictionary *store_dictionary = [store dictionaryRepresentation];
+
+ NSArray *keys = [store_dictionary allKeys];
+ int count = [keys count];
+ for (int i = 0; i < count; ++i) {
+ NSString *k = [keys objectAtIndex:i];
+ NSObject *v = [store_dictionary objectForKey:k];
+
+ const char *str = [k UTF8String];
+ if (str != NULL) {
+ result[String::utf8(str)] = nsobject_to_variant(v);
+ }
+ }
+
+ return result;
+}
+
+Error ICloud::synchronize_key_values() {
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ BOOL result = [store synchronize];
+ if (result == YES) {
+ return OK;
+ } else {
+ return FAILED;
+ }
+}
+
+/*
+Error ICloud::initial_sync() {
+ //you sometimes have to write something to the store to get it to download new data. go apple!
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+ if ([store boolForKey:@"isb"])
+ {
+ [store setBool:NO forKey:@"isb"];
+ }
+ else
+ {
+ [store setBool:YES forKey:@"isb"];
+ }
+ return synchronize();
+}
+
+*/
+ICloud::ICloud() {
+ ERR_FAIL_COND(instance != NULL);
+ instance = this;
+ //connected = false;
+
+ [[NSNotificationCenter defaultCenter]
+ addObserverForName:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
+ object:[NSUbiquitousKeyValueStore defaultStore]
+ queue:nil
+ usingBlock:^(NSNotification *notification) {
+ NSDictionary *userInfo = [notification userInfo];
+ NSInteger change = [[userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] integerValue];
+
+ Dictionary ret;
+ ret["type"] = "key_value_changed";
+
+ //PackedStringArray result_keys;
+ //Array result_values;
+ Dictionary keyValues;
+ String reason = "";
+
+ if (change == NSUbiquitousKeyValueStoreServerChange) {
+ reason = "server";
+ } else if (change == NSUbiquitousKeyValueStoreInitialSyncChange) {
+ reason = "initial_sync";
+ } else if (change == NSUbiquitousKeyValueStoreQuotaViolationChange) {
+ reason = "quota_violation";
+ } else if (change == NSUbiquitousKeyValueStoreAccountChange) {
+ reason = "account";
+ }
+
+ ret["reason"] = reason;
+
+ NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
+
+ NSArray *keys = [userInfo objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
+ for (NSString *key in keys) {
+ const char *str = [key UTF8String];
+ if (str == NULL) {
+ continue;
+ }
+
+ NSObject *object = [store objectForKey:key];
+
+ //figure out what kind of object it is
+ Variant value = nsobject_to_variant(object);
+
+ keyValues[String::utf8(str)] = value;
+ }
+
+ ret["changed_values"] = keyValues;
+ pending_events.push_back(ret);
+ }];
+}
+
+ICloud::~ICloud() {}
diff --git a/modules/icloud/icloud_module.cpp b/modules/icloud/icloud_module.cpp
new file mode 100644
index 0000000000..43fdc7d45e
--- /dev/null
+++ b/modules/icloud/icloud_module.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* icloud_module.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "icloud_module.h"
+
+#include "core/config/engine.h"
+
+#include "icloud.h"
+
+ICloud *icloud;
+
+void register_icloud_types() {
+ icloud = memnew(ICloud);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud));
+}
+
+void unregister_icloud_types() {
+ if (icloud) {
+ memdelete(icloud);
+ }
+}
diff --git a/modules/icloud/icloud_module.h b/modules/icloud/icloud_module.h
new file mode 100644
index 0000000000..7fd057525e
--- /dev/null
+++ b/modules/icloud/icloud_module.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* icloud_module.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+void register_icloud_types();
+void unregister_icloud_types();
diff --git a/modules/inappstore/SCsub b/modules/inappstore/SCsub
new file mode 100644
index 0000000000..cee6a256d5
--- /dev/null
+++ b/modules/inappstore/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_inappstore = env_modules.Clone()
+
+# (iOS) Enable module support
+env_inappstore.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_inappstore.add_source_files(modules_sources, "*.cpp")
+env_inappstore.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_inappstore_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/inappstore/config.py b/modules/inappstore/config.py
new file mode 100644
index 0000000000..e68603fc93
--- /dev/null
+++ b/modules/inappstore/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return platform == "iphone"
+
+
+def configure(env):
+ pass
diff --git a/modules/inappstore/in_app_store.h b/modules/inappstore/in_app_store.h
new file mode 100644
index 0000000000..c8e5d17cec
--- /dev/null
+++ b/modules/inappstore/in_app_store.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* in_app_store.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 IN_APP_STORE_H
+#define IN_APP_STORE_H
+
+#include "core/object/class_db.h"
+
+#ifdef __OBJC__
+@class GodotProductsDelegate;
+@class GodotTransactionsObserver;
+
+typedef GodotProductsDelegate InAppStoreProductDelegate;
+typedef GodotTransactionsObserver InAppStoreTransactionObserver;
+#else
+typedef void InAppStoreProductDelegate;
+typedef void InAppStoreTransactionObserver;
+#endif
+
+class InAppStore : public Object {
+ GDCLASS(InAppStore, Object);
+
+ static InAppStore *instance;
+ static void _bind_methods();
+
+ List<Variant> pending_events;
+
+ InAppStoreProductDelegate *products_request_delegate;
+ InAppStoreTransactionObserver *transactions_observer;
+
+public:
+ Error request_product_info(Dictionary p_params);
+ Error restore_purchases();
+ Error purchase(Dictionary p_params);
+
+ int get_pending_event_count();
+ Variant pop_pending_event();
+ void finish_transaction(String product_id);
+ void set_auto_finish_transaction(bool b);
+
+ void _post_event(Variant p_event);
+ void _record_purchase(String product_id);
+
+ static InAppStore *get_singleton();
+
+ InAppStore();
+ ~InAppStore();
+};
+
+#endif
diff --git a/modules/inappstore/in_app_store.mm b/modules/inappstore/in_app_store.mm
new file mode 100644
index 0000000000..62977318c1
--- /dev/null
+++ b/modules/inappstore/in_app_store.mm
@@ -0,0 +1,411 @@
+/*************************************************************************/
+/* in_app_store.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "in_app_store.h"
+
+#import <Foundation/Foundation.h>
+#import <StoreKit/StoreKit.h>
+
+InAppStore *InAppStore::instance = NULL;
+
+@interface SKProduct (LocalizedPrice)
+
+@property(nonatomic, readonly) NSString *localizedPrice;
+
+@end
+
+//----------------------------------//
+// SKProduct extension
+//----------------------------------//
+@implementation SKProduct (LocalizedPrice)
+
+- (NSString *)localizedPrice {
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
+ [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
+ [numberFormatter setLocale:self.priceLocale];
+ NSString *formattedString = [numberFormatter stringFromNumber:self.price];
+ return formattedString;
+}
+
+@end
+
+@interface GodotProductsDelegate : NSObject <SKProductsRequestDelegate>
+
+@property(nonatomic, strong) NSMutableArray *loadedProducts;
+@property(nonatomic, strong) NSMutableArray *pendingRequests;
+
+- (void)performRequestWithProductIDs:(NSSet *)productIDs;
+- (Error)purchaseProductWithProductID:(NSString *)productID;
+- (void)reset;
+
+@end
+
+@implementation GodotProductsDelegate
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.loadedProducts = [NSMutableArray new];
+ self.pendingRequests = [NSMutableArray new];
+}
+
+- (void)performRequestWithProductIDs:(NSSet *)productIDs {
+ SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs];
+
+ request.delegate = self;
+ [self.pendingRequests addObject:request];
+ [request start];
+}
+
+- (Error)purchaseProductWithProductID:(NSString *)productID {
+ SKProduct *product = nil;
+
+ NSLog(@"searching for product!");
+
+ if (self.loadedProducts) {
+ for (SKProduct *storedProduct in self.loadedProducts) {
+ if ([storedProduct.productIdentifier isEqualToString:productID]) {
+ product = storedProduct;
+ break;
+ }
+ }
+ }
+
+ if (!product) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ NSLog(@"product found!");
+
+ SKPayment *payment = [SKPayment paymentWithProduct:product];
+ [[SKPaymentQueue defaultQueue] addPayment:payment];
+
+ NSLog(@"purchase sent!");
+
+ return OK;
+}
+
+- (void)reset {
+ [self.loadedProducts removeAllObjects];
+ [self.pendingRequests removeAllObjects];
+}
+
+- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
+ [self.pendingRequests removeObject:request];
+
+ Dictionary ret;
+ ret["type"] = "product_info";
+ ret["result"] = "error";
+ ret["error"] = String::utf8([error.localizedDescription UTF8String]);
+
+ InAppStore::get_singleton()->_post_event(ret);
+}
+
+- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
+ [self.pendingRequests removeObject:request];
+
+ NSArray *products = response.products;
+ [self.loadedProducts addObjectsFromArray:products];
+
+ Dictionary ret;
+ ret["type"] = "product_info";
+ ret["result"] = "ok";
+ PackedStringArray titles;
+ PackedStringArray descriptions;
+ PackedFloat32Array prices;
+ PackedStringArray ids;
+ PackedStringArray localized_prices;
+ PackedStringArray currency_codes;
+
+ for (NSUInteger i = 0; i < [products count]; i++) {
+ SKProduct *product = [products objectAtIndex:i];
+
+ const char *str = [product.localizedTitle UTF8String];
+ titles.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [product.localizedDescription UTF8String];
+ descriptions.push_back(String::utf8(str != NULL ? str : ""));
+ prices.push_back([product.price doubleValue]);
+ ids.push_back(String::utf8([product.productIdentifier UTF8String]));
+ localized_prices.push_back(String::utf8([product.localizedPrice UTF8String]));
+ currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String]));
+ }
+
+ ret["titles"] = titles;
+ ret["descriptions"] = descriptions;
+ ret["prices"] = prices;
+ ret["ids"] = ids;
+ ret["localized_prices"] = localized_prices;
+ ret["currency_codes"] = currency_codes;
+
+ PackedStringArray invalid_ids;
+
+ for (NSString *ipid in response.invalidProductIdentifiers) {
+ invalid_ids.push_back(String::utf8([ipid UTF8String]));
+ }
+
+ ret["invalid_ids"] = invalid_ids;
+
+ InAppStore::get_singleton()->_post_event(ret);
+}
+
+@end
+
+@interface GodotTransactionsObserver : NSObject <SKPaymentTransactionObserver>
+
+@property(nonatomic, assign) BOOL shouldAutoFinishTransactions;
+@property(nonatomic, strong) NSMutableDictionary *pendingTransactions;
+
+- (void)finishTransactionWithProductID:(NSString *)productID;
+- (void)reset;
+
+@end
+
+@implementation GodotTransactionsObserver
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.pendingTransactions = [NSMutableDictionary new];
+}
+
+- (void)finishTransactionWithProductID:(NSString *)productID {
+ SKPaymentTransaction *transaction = self.pendingTransactions[productID];
+
+ if (transaction) {
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ }
+
+ self.pendingTransactions[productID] = nil;
+}
+
+- (void)reset {
+ [self.pendingTransactions removeAllObjects];
+}
+
+- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
+ printf("transactions updated!\n");
+ for (SKPaymentTransaction *transaction in transactions) {
+ switch (transaction.transactionState) {
+ case SKPaymentTransactionStatePurchased: {
+ printf("status purchased!\n");
+ String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
+ String transactionId = String::utf8([transaction.transactionIdentifier UTF8String]);
+ InAppStore::get_singleton()->_record_purchase(pid);
+ Dictionary ret;
+ ret["type"] = "purchase";
+ ret["result"] = "ok";
+ ret["product_id"] = pid;
+ ret["transaction_id"] = transactionId;
+
+ NSData *receipt = nil;
+ int sdk_version = [[[UIDevice currentDevice] systemVersion] intValue];
+
+ NSBundle *bundle = [NSBundle mainBundle];
+ // Get the transaction receipt file path location in the app bundle.
+ NSURL *receiptFileURL = [bundle appStoreReceiptURL];
+
+ // Read in the contents of the transaction file.
+ receipt = [NSData dataWithContentsOfURL:receiptFileURL];
+
+ NSString *receipt_to_send = nil;
+
+ if (receipt != nil) {
+ receipt_to_send = [receipt base64EncodedStringWithOptions:0];
+ }
+ Dictionary receipt_ret;
+ receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : "");
+ receipt_ret["sdk"] = sdk_version;
+ ret["receipt"] = receipt_ret;
+
+ InAppStore::get_singleton()->_post_event(ret);
+
+ if (self.shouldAutoFinishTransactions) {
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } else {
+ self.pendingTransactions[transaction.payment.productIdentifier] = transaction;
+ }
+
+ } break;
+ case SKPaymentTransactionStateFailed: {
+ printf("status transaction failed!\n");
+ String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
+ Dictionary ret;
+ ret["type"] = "purchase";
+ ret["result"] = "error";
+ ret["product_id"] = pid;
+ ret["error"] = String::utf8([transaction.error.localizedDescription UTF8String]);
+ InAppStore::get_singleton()->_post_event(ret);
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } break;
+ case SKPaymentTransactionStateRestored: {
+ printf("status transaction restored!\n");
+ String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]);
+ InAppStore::get_singleton()->_record_purchase(pid);
+ Dictionary ret;
+ ret["type"] = "restore";
+ ret["result"] = "ok";
+ ret["product_id"] = pid;
+ InAppStore::get_singleton()->_post_event(ret);
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } break;
+ default: {
+ printf("status default %i!\n", (int)transaction.transactionState);
+ } break;
+ }
+ }
+}
+
+@end
+
+void InAppStore::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info);
+ ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases);
+ ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase);
+
+ ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count);
+ ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event);
+ ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction);
+ ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction);
+}
+
+Error InAppStore::request_product_info(Dictionary p_params) {
+ ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER);
+
+ PackedStringArray pids = p_params["product_ids"];
+ printf("************ request product info! %i\n", pids.size());
+
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()];
+ for (int i = 0; i < pids.size(); i++) {
+ printf("******** adding %s to product list\n", pids[i].utf8().get_data());
+ NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()];
+ [array addObject:pid];
+ };
+
+ NSSet *products = [[NSSet alloc] initWithArray:array];
+
+ [products_request_delegate performRequestWithProductIDs:products];
+
+ return OK;
+}
+
+Error InAppStore::restore_purchases() {
+ printf("restoring purchases!\n");
+ [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
+
+ return OK;
+}
+
+Error InAppStore::purchase(Dictionary p_params) {
+ ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
+ if (![SKPaymentQueue canMakePayments]) {
+ return ERR_UNAVAILABLE;
+ }
+
+ printf("purchasing!\n");
+ Dictionary params = p_params;
+ ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER);
+
+ NSString *pid = [[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()];
+
+ return [products_request_delegate purchaseProductWithProductID:pid];
+}
+
+int InAppStore::get_pending_event_count() {
+ return pending_events.size();
+}
+
+Variant InAppStore::pop_pending_event() {
+ Variant front = pending_events.front()->get();
+ pending_events.pop_front();
+
+ return front;
+}
+
+void InAppStore::_post_event(Variant p_event) {
+ pending_events.push_back(p_event);
+}
+
+void InAppStore::_record_purchase(String product_id) {
+ String skey = "purchased/" + product_id;
+ NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()];
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+InAppStore *InAppStore::get_singleton() {
+ return instance;
+}
+
+InAppStore::InAppStore() {
+ ERR_FAIL_COND(instance != NULL);
+ instance = this;
+
+ products_request_delegate = [[GodotProductsDelegate alloc] init];
+ transactions_observer = [[GodotTransactionsObserver alloc] init];
+
+ [[SKPaymentQueue defaultQueue] addTransactionObserver:transactions_observer];
+}
+
+void InAppStore::finish_transaction(String product_id) {
+ NSString *prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding];
+
+ [transactions_observer finishTransactionWithProductID:prod_id];
+}
+
+void InAppStore::set_auto_finish_transaction(bool b) {
+ transactions_observer.shouldAutoFinishTransactions = b;
+}
+
+InAppStore::~InAppStore() {
+ [products_request_delegate reset];
+ [transactions_observer reset];
+
+ products_request_delegate = nil;
+ [[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer];
+ transactions_observer = nil;
+}
diff --git a/modules/inappstore/in_app_store_module.cpp b/modules/inappstore/in_app_store_module.cpp
new file mode 100644
index 0000000000..039bdd4f83
--- /dev/null
+++ b/modules/inappstore/in_app_store_module.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* in_app_store_module.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "in_app_store_module.h"
+
+#include "core/config/engine.h"
+
+#include "in_app_store.h"
+
+InAppStore *store_kit;
+
+void register_inappstore_types() {
+ store_kit = memnew(InAppStore);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
+}
+
+void unregister_inappstore_types() {
+ if (store_kit) {
+ memdelete(store_kit);
+ }
+}
diff --git a/modules/inappstore/in_app_store_module.h b/modules/inappstore/in_app_store_module.h
new file mode 100644
index 0000000000..44673e58bc
--- /dev/null
+++ b/modules/inappstore/in_app_store_module.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* in_app_store_module.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+void register_inappstore_types();
+void unregister_inappstore_types();
diff --git a/modules/inappstore/inappstore.gdip b/modules/inappstore/inappstore.gdip
new file mode 100644
index 0000000000..7a5efb8ad3
--- /dev/null
+++ b/modules/inappstore/inappstore.gdip
@@ -0,0 +1,17 @@
+[config]
+name="InAppStore"
+binary="inappstore_lib.a"
+
+initialization="register_inappstore_types"
+deinitialization="unregister_inappstore_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["StoreKit.framework"]
+
+capabilities=[]
+
+files=[]
+
+[plist]
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 9c7ace5cf2..e5f041c618 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -31,7 +31,7 @@
#include "image_loader_jpegd.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <jpgd.h>
#include <string.h>
diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp
index 7bb70b098f..320da182f8 100644
--- a/modules/jsonrpc/jsonrpc.cpp
+++ b/modules/jsonrpc/jsonrpc.cpp
@@ -98,6 +98,10 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
if (p_action.get_type() == Variant::DICTIONARY) {
Dictionary dict = p_action;
String method = dict.get("method", "");
+ if (method.begins_with("$/")) {
+ return ret;
+ }
+
Array args;
if (dict.has("params")) {
Variant params = dict.get("params", Variant());
diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h
index e2b7ab0975..c2acf84515 100644
--- a/modules/jsonrpc/jsonrpc.h
+++ b/modules/jsonrpc/jsonrpc.h
@@ -31,8 +31,8 @@
#ifndef GODOT_JSON_RPC_H
#define GODOT_JSON_RPC_H
-#include "core/object.h"
-#include "core/variant.h"
+#include "core/object/class_db.h"
+#include "core/variant/variant.h"
class JSONRPC : public Object {
GDCLASS(JSONRPC, Object)
diff --git a/modules/jsonrpc/register_types.cpp b/modules/jsonrpc/register_types.cpp
index 0d3e446e3e..e6affaee41 100644
--- a/modules/jsonrpc/register_types.cpp
+++ b/modules/jsonrpc/register_types.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "jsonrpc.h"
void register_jsonrpc_types() {
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index 4de523baa0..1ef991841b 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "lightmapper_rd.h"
+#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/project_settings.h"
#include "lm_blendseams.glsl.gen.h"
#include "lm_compute.glsl.gen.h"
#include "lm_raster.glsl.gen.h"
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index cd000414cf..e17b5473a8 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -31,7 +31,7 @@
#ifndef LIGHTMAPPER_RD_H
#define LIGHTMAPPER_RD_H
-#include "core/local_vector.h"
+#include "core/templates/local_vector.h"
#include "scene/3d/lightmapper.h"
#include "scene/resources/mesh.h"
#include "servers/rendering/rendering_device.h"
diff --git a/modules/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp
index 0e6d7590cc..b16adaf5d6 100644
--- a/modules/lightmapper_rd/register_types.cpp
+++ b/modules/lightmapper_rd/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "lightmapper_rd.h"
#include "scene/3d/lightmapper.h"
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 501bfff075..bec792450a 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -32,10 +32,10 @@
#include "core/os/file_access.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/io/certs_compressed.gen.h"
#include "core/io/compression.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -43,8 +43,8 @@
#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n"
#define PEM_END_CRT "-----END CERTIFICATE-----\n"
-#include "mbedtls/pem.h"
#include <mbedtls/debug.h>
+#include <mbedtls/pem.h>
CryptoKey *CryptoKeyMbedTLS::create() {
return memnew(CryptoKeyMbedTLS);
@@ -294,20 +294,15 @@ Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoK
unsigned char buf[4096];
memset(buf, 0, 4096);
- Ref<X509CertificateMbedTLS> out;
- out.instance();
- mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg);
-
- int err = mbedtls_x509_crt_parse(&(out->cert), buf, 4096);
- if (err != 0) {
- mbedtls_mpi_free(&serial);
- mbedtls_x509write_crt_free(&crt);
- ERR_PRINT("Generated invalid certificate: " + itos(err));
- return nullptr;
- }
-
+ int ret = mbedtls_x509write_crt_pem(&crt, buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_mpi_free(&serial);
mbedtls_x509write_crt_free(&crt);
+ ERR_FAIL_COND_V_MSG(ret != 0, nullptr, "Failed to generate certificate: " + itos(ret));
+ buf[4095] = '\0'; // Make sure strlen can't fail.
+
+ Ref<X509CertificateMbedTLS> out;
+ out.instance();
+ out->load_from_memory(buf, strlen((char *)buf) + 1); // Use strlen to find correct output size.
return out;
}
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 2a446f9d48..e40ca08643 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -32,7 +32,7 @@
#define CRYPTO_MBEDTLS_H
#include "core/crypto/crypto.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h
index baaeb6eb85..96703a7eb7 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/ssl_context_mbedtls.h
@@ -35,7 +35,7 @@
#include "core/os/file_access.h"
-#include "core/reference.h"
+#include "core/object/reference.h"
#include <mbedtls/config.h>
#include <mbedtls/ctr_drbg.h>
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 75638d47c4..0bb555e780 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -35,9 +35,11 @@
void register_mobile_vr_types() {
ClassDB::register_class<MobileVRInterface>();
- Ref<MobileVRInterface> mobile_vr;
- mobile_vr.instance();
- XRServer::get_singleton()->add_interface(mobile_vr);
+ if (XRServer::get_singleton()) {
+ Ref<MobileVRInterface> mobile_vr;
+ mobile_vr.instance();
+ XRServer::get_singleton()->add_interface(mobile_vr);
+ }
}
void unregister_mobile_vr_types() {
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index e8f3174a0a..3b94949470 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -40,6 +40,11 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
godot_tools_build.build(env_mono, api_sln_cmd)
+ # Build Godot.NET.Sdk
+ import build_scripts.godot_net_sdk_build as godot_net_sdk_build
+
+ godot_net_sdk_build.build(env_mono)
+
# Add sources
env_mono.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py
new file mode 100644
index 0000000000..3bfba0f0f6
--- /dev/null
+++ b/modules/mono/build_scripts/godot_net_sdk_build.py
@@ -0,0 +1,45 @@
+# Build Godot.NET.Sdk solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_godot_net_sdk(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env["module_dir"]
+
+ solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
+ build_config = "Release"
+
+ from .solution_builder import build_solution
+
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
+ # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
+
+
+def build(env_mono):
+ assert env_mono["tools"]
+
+ output_dir = Dir("#bin").abspath
+ editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
+ nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
+
+ module_dir = os.getcwd()
+
+ package_version_file = os.path.join(
+ module_dir, "editor", "Godot.NET.Sdk", "Godot.NET.Sdk", "Godot.NET.Sdk_PackageVersion.txt"
+ )
+
+ with open(package_version_file, mode="r") as f:
+ version = f.read().strip()
+
+ target_filenames = ["Godot.NET.Sdk.%s.nupkg" % version]
+
+ targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
+ env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 3e771e06f0..6057004166 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,6 +1,5 @@
import os
import os.path
-import sys
import subprocess
from SCons.Script import Dir, Environment
@@ -397,9 +396,8 @@ def configure(env, env_mono):
mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
if tools_enabled:
+ # Only supported for editor builds.
copy_mono_root_files(env, mono_root)
- else:
- print("Ignoring option: 'copy_mono_root'; only available for builds with 'tools' enabled.")
def make_template_dir(env, mono_root):
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index 3090a4759a..0ec7e2f433 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -9,7 +9,7 @@ if os.name == "nt":
def _reg_open_key(key, subkey):
try:
return winreg.OpenKey(key, subkey)
- except (WindowsError, OSError):
+ except OSError:
if platform.architecture()[0] == "32bit":
bitness_sam = winreg.KEY_WOW64_64KEY
else:
@@ -37,7 +37,7 @@ def _find_mono_in_reg(subkey, bits):
with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
return value
- except (WindowsError, OSError):
+ except OSError:
return None
@@ -48,7 +48,7 @@ def _find_mono_in_reg_old(subkey, bits):
if default_clr:
return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
return None
- except (WindowsError, EnvironmentError):
+ except OSError:
return None
@@ -97,7 +97,7 @@ def find_msbuild_tools_path_reg():
raise ValueError("Cannot find `installationPath` entry")
except ValueError as e:
print("Error reading output from vswhere: " + e.message)
- except WindowsError:
+ except OSError:
pass # Fine, vswhere not found
except (subprocess.CalledProcessError, OSError):
pass
@@ -109,5 +109,5 @@ def find_msbuild_tools_path_reg():
with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
return value
- except (WindowsError, OSError):
+ except OSError:
return ""
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
index 03f4e57f02..6a621c3c8b 100644
--- a/modules/mono/build_scripts/solution_builder.py
+++ b/modules/mono/build_scripts/solution_builder.py
@@ -8,9 +8,6 @@ def find_dotnet_cli():
import os.path
if os.name == "nt":
- windows_exts = os.environ["PATHEXT"]
- windows_exts = windows_exts.split(os.pathsep) if windows_exts else []
-
for hint_dir in os.environ["PATH"].split(os.pathsep):
hint_dir = hint_dir.strip('"')
hint_path = os.path.join(hint_dir, "dotnet")
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index d7b2028204..e3119a8da7 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -32,9 +32,9 @@
#ifdef DEBUG_METHODS_ENABLED
+#include "core/config/project_settings.h"
#include "core/io/json.h"
#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "core/version.h"
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
diff --git a/modules/mono/class_db_api_json.h b/modules/mono/class_db_api_json.h
index 7f016ac3d6..6b7f5a4d88 100644
--- a/modules/mono/class_db_api_json.h
+++ b/modules/mono/class_db_api_json.h
@@ -31,13 +31,13 @@
#ifndef CLASS_DB_API_JSON_H
#define CLASS_DB_API_JSON_H
-// 'core/method_bind.h' defines DEBUG_METHODS_ENABLED, but it looks like we
-// cannot include it here. That's why we include it through 'core/class_db.h'.
-#include "core/class_db.h"
+// 'core/object/method_bind.h' defines DEBUG_METHODS_ENABLED, but it looks like we
+// cannot include it here. That's why we include it through 'core/object/class_db.h'.
+#include "core/object/class_db.h"
#ifdef DEBUG_METHODS_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api);
diff --git a/modules/mono/config.py b/modules/mono/config.py
index cd659057ef..d060ae9b28 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -23,18 +23,16 @@ def configure(env):
envvars.Add(
PathVariable(
"mono_prefix",
- "Path to the mono installation directory for the target platform and architecture",
+ "Path to the Mono installation directory for the target platform and architecture",
"",
PathVariable.PathAccept,
)
)
- envvars.Add(BoolVariable("mono_static", "Statically link mono", default_mono_static))
- envvars.Add(BoolVariable("mono_glue", "Build with the mono glue sources", True))
+ envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
+ envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True))
envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
envvars.Add(
- BoolVariable(
- "copy_mono_root", "Make a copy of the mono installation directory to bundle with the editor", False
- )
+ BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True)
)
# TODO: It would be great if this could be detected automatically instead
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index bbdec224f0..b4537f531d 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -33,6 +33,7 @@
#include <mono/metadata/threads.h>
#include <stdint.h>
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
#include "core/io/json.h"
@@ -40,7 +41,6 @@
#include "core/os/mutex.h"
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/bindings_generator.h"
@@ -477,7 +477,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::COLOR,
Variant::STRING_NAME,
Variant::NODE_PATH,
- Variant::_RID,
+ Variant::RID,
Variant::CALLABLE
};
@@ -940,8 +940,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Use a placeholder for now to avoid losing the state when saving a scene
- obj->set_script(scr);
-
PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj);
obj->set_script_instance(placeholder);
@@ -968,14 +966,13 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
Ref<CSharpScript> script = E->get();
- if (!script->get_path().empty()) {
#ifdef TOOLS_ENABLED
- script->exports_invalidated = true;
+ script->exports_invalidated = true;
#endif
- script->signals_invalidated = true;
+ script->signals_invalidated = true;
+ if (!script->get_path().empty()) {
script->reload(p_soft_reload);
- script->update_exports();
if (!script->valid) {
script->pending_reload_instances.clear();
@@ -1950,6 +1947,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
}
void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+ // Must make sure event signals are not left dangling
disconnect_event_signals();
#ifdef DEBUG_ENABLED
@@ -1965,6 +1963,9 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
CRASH_COND(gchandle.is_released());
#endif
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
r_remove_script_instance = false;
if (_unreference_owner_unsafe()) {
@@ -2224,6 +2225,9 @@ CSharpInstance::~CSharpInstance() {
destructing_script_instance = true;
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
if (!gchandle.is_released()) {
if (!predelete_notified && !ref_dying) {
// This destructor is not called from the owners destructor.
@@ -2702,7 +2706,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
if (!property->has_getter()) {
#ifdef TOOLS_ENABLED
if (exported) {
- ERR_PRINT("Read-only property cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
+ ERR_PRINT("Cannot export a property without a getter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
}
#endif
return false;
@@ -2710,7 +2714,7 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
if (!property->has_setter()) {
#ifdef TOOLS_ENABLED
if (exported) {
- ERR_PRINT("Write-only property (without getter) cannot be exported: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
+ ERR_PRINT("Cannot export a property without a setter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
}
#endif
return false;
@@ -2955,13 +2959,24 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
CRASH_COND(p_script->native == nullptr);
+ p_script->valid = true;
+
+ update_script_class_info(p_script);
+
+#ifdef TOOLS_ENABLED
+ p_script->_update_member_info_no_exports();
+#endif
+}
+
+// Extract information about the script using the mono class.
+void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
GDMonoClass *base = p_script->script_class->get_parent_class();
+ // `base` should only be set if the script is a user defined type.
if (base != p_script->native) {
p_script->base = base;
}
- p_script->valid = true;
p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
if (!p_script->tool) {
@@ -2996,17 +3011,74 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
- // Need to fetch method from base classes as well
+ p_script->rpc_functions.clear();
+ p_script->rpc_variables.clear();
+
GDMonoClass *top = p_script->script_class;
while (top && top != p_script->native) {
+ // Fetch methods from base classes as well
top->fetch_methods_with_godot_api_checks(p_script->native);
+
+ // Update RPC info
+ {
+ Vector<GDMonoMethod *> methods = top->get_all_methods();
+ for (int i = 0; i < methods.size(); i++) {
+ if (!methods[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = methods[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_functions.find(nd)) {
+ p_script->rpc_functions.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ Vector<GDMonoField *> fields = top->get_all_fields();
+ for (int i = 0; i < fields.size(); i++) {
+ if (!fields[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(fields[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = fields[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_variables.find(nd)) {
+ p_script->rpc_variables.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ Vector<GDMonoProperty *> properties = top->get_all_properties();
+ for (int i = 0; i < properties.size(); i++) {
+ if (!properties[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(properties[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = properties[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_variables.find(nd)) {
+ p_script->rpc_variables.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
top = top->get_parent_class();
}
+ // Sort so we are 100% that they are always the same.
+ p_script->rpc_functions.sort_custom<SortNetData>();
+ p_script->rpc_variables.sort_custom<SortNetData>();
+
p_script->load_script_signals(p_script->script_class, p_script->native);
-#ifdef TOOLS_ENABLED
- p_script->_update_member_info_no_exports();
-#endif
}
bool CSharpScript::can_instance() const {
@@ -3305,124 +3377,15 @@ Error CSharpScript::reload(bool p_keep_state) {
print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
#endif
- tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
-
- if (!tool) {
- GDMonoClass *nesting_class = script_class->get_nesting_class();
- tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
- }
-
-#if TOOLS_ENABLED
- if (!tool) {
- tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
- }
-#endif
-
native = GDMonoUtils::get_class_native_base(script_class);
CRASH_COND(native == nullptr);
- GDMonoClass *base_class = script_class->get_parent_class();
-
- if (base_class != native) {
- base = base_class;
- }
-
-#ifdef DEBUG_ENABLED
- // For debug builds, we must fetch from all native base methods as well.
- // Native base methods must be fetched before the current class.
- // Not needed if the script class itself is a native class.
-
- if (script_class != native) {
- GDMonoClass *native_top = native;
- while (native_top) {
- native_top->fetch_methods_with_godot_api_checks(native);
-
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- native_top = native_top->get_parent_class();
- }
- }
-#endif
-
- script_class->fetch_methods_with_godot_api_checks(native);
+ update_script_class_info(this);
- // Need to fetch method from base classes as well
- GDMonoClass *top = script_class;
- while (top && top != native) {
- top->fetch_methods_with_godot_api_checks(native);
- top = top->get_parent_class();
- }
-
- load_script_signals(script_class, native);
_update_exports();
}
- rpc_functions.clear();
- rpc_variables.clear();
-
- GDMonoClass *top = script_class;
- while (top && top != native) {
- {
- Vector<GDMonoMethod *> methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); i++) {
- if (!methods[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(methods[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = methods[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_functions.find(nd)) {
- rpc_functions.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoField *> fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- if (!fields[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(fields[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = fields[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoProperty *> properties = top->get_all_properties();
- for (int i = 0; i < properties.size(); i++) {
- if (!properties[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(properties[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = properties[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- top = top->get_parent_class();
- }
-
- // Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
- rpc_variables.sort_custom<SortNetData>();
-
return OK;
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f0b43a40f9..3e4e6c3f86 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -33,8 +33,8 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/object/script_language.h"
+#include "core/templates/self_list.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
@@ -164,6 +164,7 @@ private:
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
+ static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
@@ -287,7 +288,7 @@ public:
void mono_object_disposed(MonoObject *p_obj);
/*
- * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
+ * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index e1e9d1381f..45a6f991bf 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -8,7 +8,7 @@
See also [GodotSharp].
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
+ <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index 86a0a4393e..8304d9e321 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Description>MSBuild .NET Sdk for Godot projects.</Description>
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.NET.Sdk</PackageId>
<Version>4.0.0</Version>
- <PackageVersion>4.0.0-dev2</PackageVersion>
<PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
<PackageType>MSBuildSdk</PackageType>
<PackageTags>MSBuildSdk</PackageTags>
@@ -19,7 +19,13 @@
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
</PropertyGroup>
- <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
+ <Target Name="ReadGodotNETSdkVersion" BeforeTargets="BeforeBuild;BeforeRebuild;CoreCompile">
+ <PropertyGroup>
+ <PackageVersion>$([System.IO.File]::ReadAllText('$(ProjectDir)Godot.NET.Sdk_PackageVersion.txt').Trim())</PackageVersion>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') " DependsOnTargets="ReadGodotNETSdkVersion">
<PropertyGroup>
<NuspecProperties>
id=$(PackageId);
@@ -32,4 +38,13 @@
</NuspecProperties>
</PropertyGroup>
</Target>
+
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
+ </PropertyGroup>
+ <Copy SourceFiles="$(OutputPath)$(PackageId).$(PackageVersion).nupkg"
+ DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ </Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
index 5b5cefe80e..ba68a4da43 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
@@ -17,6 +17,6 @@
<repository url="$projecturl$" />
</metadata>
<files>
- <file src="Sdk\**" target="Sdk" />\
+ <file src="Sdk\**" target="Sdk" />
</files>
</package>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt
new file mode 100644
index 0000000000..34749489b9
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk_PackageVersion.txt
@@ -0,0 +1 @@
+4.0.0-dev3
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index dfc59e6ccb..5febcf3175 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -14,15 +14,15 @@
<GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
- <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.mono\temp\'. -->
- <BaseOutputPath>$(GodotProjectDir).mono\temp\bin\</BaseOutputPath>
- <OutputPath>$(GodotProjectDir).mono\temp\bin\$(Configuration)\</OutputPath>
+ <!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
+ <BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath>
+ <OutputPath>$(GodotProjectDir).godot\mono\temp\bin\$(Configuration)\</OutputPath>
<!--
Use custom IntermediateOutputPath and BaseIntermediateOutputPath only if it wasn't already set.
Otherwise the old values may have already been changed by MSBuild which can cause problems with NuGet.
-->
- <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\$(Configuration)\</IntermediateOutputPath>
- <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).mono\temp\obj\</BaseIntermediateOutputPath>
+ <IntermediateOutputPath Condition=" '$(IntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\$(Configuration)\</IntermediateOutputPath>
+ <BaseIntermediateOutputPath Condition=" '$(BaseIntermediateOutputPath)' == '' ">$(GodotProjectDir).godot\mono\temp\obj\</BaseIntermediateOutputPath>
<!-- Do not append the target framework name to the output path. -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
@@ -88,7 +88,7 @@
<PropertyGroup>
<!-- ExportDebug also defines DEBUG like Debug does. -->
<DefineConstants Condition=" '$(Configuration)' == 'ExportDebug' ">$(DefineConstants);DEBUG</DefineConstants>
- <!-- Debug defines TOOLS to differenciate between Debug and ExportDebug configurations. -->
+ <!-- Debug defines TOOLS to differentiate between Debug and ExportDebug configurations. -->
<DefineConstants Condition=" '$(Configuration)' == 'Debug' ">$(DefineConstants);TOOLS</DefineConstants>
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
@@ -102,11 +102,11 @@
-->
<Reference Include="GodotSharp">
<Private>false</Private>
- <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
+ <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
</Reference>
<Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' ">
<Private>false</Private>
- <HintPath>$(GodotProjectDir).mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
+ <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index c2549b4ad5..9b7b422276 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -15,14 +15,14 @@ namespace GodotTools.BuildLogger
public void Initialize(IEventSource eventSource)
{
if (null == Parameters)
- throw new LoggerException("Log directory was not set.");
+ throw new LoggerException("Log directory parameter not specified.");
var parameters = Parameters.Split(new[] { ';' });
string logDir = parameters[0];
if (string.IsNullOrEmpty(logDir))
- throw new LoggerException("Log directory was not set.");
+ throw new LoggerException("Log directory parameter is empty.");
if (parameters.Length > 1)
throw new LoggerException("Too many parameters passed.");
@@ -51,36 +51,46 @@ namespace GodotTools.BuildLogger
{
throw new LoggerException("Failed to create log file: " + ex.Message);
}
- else
- {
- // Unexpected failure
- throw;
- }
+
+ // Unexpected failure
+ throw;
}
eventSource.ProjectStarted += eventSource_ProjectStarted;
- eventSource.TaskStarted += eventSource_TaskStarted;
+ eventSource.ProjectFinished += eventSource_ProjectFinished;
eventSource.MessageRaised += eventSource_MessageRaised;
eventSource.WarningRaised += eventSource_WarningRaised;
eventSource.ErrorRaised += eventSource_ErrorRaised;
- eventSource.ProjectFinished += eventSource_ProjectFinished;
}
- void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
+ private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
+ {
+ WriteLine(e.Message);
+ indent++;
+ }
+
+ private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
+ {
+ indent--;
+ WriteLine(e.Message);
+ }
+
+ private void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
- if (e.ProjectFile.Length > 0)
+ if (!string.IsNullOrEmpty(e.ProjectFile))
line += $" [{e.ProjectFile}]";
WriteLine(line);
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
- $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(errorLine);
}
- void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
+ private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
@@ -89,8 +99,9 @@ namespace GodotTools.BuildLogger
WriteLine(line);
- string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
- $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
+ string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
+ $"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
+ $"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
issuesStreamWriter.WriteLine(warningLine);
}
@@ -106,40 +117,6 @@ namespace GodotTools.BuildLogger
}
}
- private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
- {
- // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
- // To keep this log clean, this logger will ignore these events.
- }
-
- private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
- {
- WriteLine(e.Message);
- indent++;
- }
-
- private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
- {
- indent--;
- WriteLine(e.Message);
- }
-
- /// <summary>
- /// Write a line to the log, adding the SenderName
- /// </summary>
- private void WriteLineWithSender(string line, BuildEventArgs e)
- {
- if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
- {
- // Well, if the sender name is MSBuild, let's leave it out for prettiness
- WriteLine(line);
- }
- else
- {
- WriteLine(e.SenderName + ": " + line);
- }
- }
-
/// <summary>
/// Write a line to the log, adding the SenderName and Message
/// (these parameters are on all MSBuild event argument objects)
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 012b69032e..b217ae4bf7 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Runtime.InteropServices;
namespace GodotTools.Core
{
@@ -14,14 +15,18 @@ namespace GodotTools.Core
if (Path.DirectorySeparatorChar == '\\')
dir = dir.Replace("/", "\\") + "\\";
- Uri fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
- Uri relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
+ var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
+ var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
- return relRoot.MakeRelativeUri(fullPath).ToString();
+ // MakeRelativeUri converts spaces to %20, hence why we need UnescapeDataString
+ return Uri.UnescapeDataString(relRoot.MakeRelativeUri(fullPath).ToString());
}
public static string NormalizePath(this string path)
{
+ if (string.IsNullOrEmpty(path))
+ return path;
+
bool rooted = path.IsAbsolutePath();
path = path.Replace('\\', '/');
@@ -31,7 +36,17 @@ namespace GodotTools.Core
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
- return rooted ? Path.DirectorySeparatorChar + path : path;
+ if (!rooted)
+ return path;
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ string maybeDrive = parts[0];
+ if (maybeDrive.Length == 2 && maybeDrive[1] == ':')
+ return path; // Already has drive letter
+ }
+
+ return Path.DirectorySeparatorChar + path;
}
private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
@@ -43,9 +58,9 @@ namespace GodotTools.Core
path.StartsWith(DriveRoot, StringComparison.Ordinal);
}
- public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
+ public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
{
- var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
+ var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
if (allowDirSeparator)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index 572c541412..0f50c90531 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -121,7 +121,7 @@ namespace GodotTools.IdeMessaging
this.messageHandler = messageHandler;
this.logger = logger;
- string projectMetadataDir = Path.Combine(godotProjectDir, ".mono", "metadata");
+ string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
index 6f318aab4a..cc0da44a13 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
@@ -2,6 +2,7 @@ using GodotTools.Core;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.RegularExpressions;
namespace GodotTools.ProjectEditor
@@ -88,7 +89,7 @@ namespace GodotTools.ProjectEditor
string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
- File.WriteAllText(solutionPath, content);
+ File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
}
public DotNetSolution(string name)
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 9cb50014b0..37123ba2b2 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -10,14 +10,23 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
+ <!--
+ The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
+ here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
+ We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
+ searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
+ -->
<ItemGroup>
- <!--
- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
- here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
- We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
- searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
- -->
<None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
</ItemGroup>
+ <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) ">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
+ </PropertyGroup>
+ <!-- Need to copy it here as well on Windows -->
+ <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
+ </Target>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 5541876f9e..7d49d251dd 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,15 +1,15 @@
using System;
using System.IO;
+using System.Text;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
+using GodotTools.Shared;
namespace GodotTools.ProjectEditor
{
public static class ProjectGenerator
{
- public const string GodotSdkVersionToUse = "4.0.0-dev2";
-
- public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
+ public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
public static ProjectRootElement GenGameProject(string name)
{
@@ -41,7 +41,8 @@ namespace GodotTools.ProjectEditor
var root = GenGameProject(name);
- root.Save(path);
+ // Save (without BOM)
+ root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
return Guid.NewGuid().ToString().ToUpper();
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 4041c56597..4e2c0f17cc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -61,10 +61,9 @@ namespace GodotTools.ProjectEditor
if (item.ItemType != itemType)
continue;
- string normalizedExclude = item.Exclude.NormalizePath();
-
- var glob = MSBuildGlob.Parse(normalizedExclude);
+ string normalizedRemove = item.Remove.NormalizePath();
+ var glob = MSBuildGlob.Parse(normalizedRemove);
excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile)));
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
new file mode 100644
index 0000000000..1d382dcb43
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -0,0 +1,36 @@
+<Project>
+ <!-- Generate C# file with the version of all the nupkgs bundled with Godot -->
+
+ <Target Name="SetPropertiesForGenerateGodotNupkgsVersions">
+ <PropertyGroup>
+ <GodotNETSdkPackageVersionFile>$(SolutionDir)..\Godot.NET.Sdk\Godot.NET.Sdk\Godot.NET.Sdk_PackageVersion.txt</GodotNETSdkPackageVersionFile>
+ <GeneratedGodotNupkgsVersionsFile>$(IntermediateOutputPath)GodotNupkgsVersions.g.cs</GeneratedGodotNupkgsVersionsFile>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="BeforeCompile;CoreCompile">
+ <ItemGroup>
+ <Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ <FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ </ItemGroup>
+ </Target>
+ <Target Name="_GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="SetPropertiesForGenerateGodotNupkgsVersions"
+ Inputs="$(MSBuildProjectFile);@(GodotNETSdkPackageVersionFile)"
+ Outputs="$(GeneratedGodotNupkgsVersionsFile)">
+ <PropertyGroup>
+ <GenerateGodotNupkgsVersionsCode><![CDATA[
+namespace $(RootNamespace) {
+ public class GeneratedGodotNupkgsVersions {
+ public const string GodotNETSdk = "$([System.IO.File]::ReadAllText('$(GodotNETSdkPackageVersionFile)').Trim())"%3b
+ }
+}
+]]></GenerateGodotNupkgsVersionsCode>
+ </PropertyGroup>
+ <WriteLinesToFile Lines="$(GenerateGodotNupkgsVersionsCode)"
+ File="$(GeneratedGodotNupkgsVersionsFile)"
+ Overwrite="True" WriteOnlyWhenDifferent="True" />
+ </Target>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
new file mode 100644
index 0000000000..3bc1698c15
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+ <Import Project="GenerateGodotNupkgsVersions.targets" />
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index ba5379e562..d3107a69db 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,5 +45,9 @@ Global
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
deleted file mode 100644
index 3ab669a9f3..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-using Godot;
-using System;
-using System.IO;
-using Godot.Collections;
-using GodotTools.Internals;
-using static GodotTools.Internals.Globals;
-using File = GodotTools.Utils.File;
-using Path = System.IO.Path;
-
-namespace GodotTools
-{
- public class BottomPanel : VBoxContainer
- {
- private EditorInterface editorInterface;
-
- private TabContainer panelTabs;
-
- private VBoxContainer panelBuildsTab;
-
- private ItemList buildTabsList;
- private TabContainer buildTabs;
-
- private Button warningsBtn;
- private Button errorsBtn;
- private Button viewLogBtn;
-
- private void _UpdateBuildTab(int index, int? currentTab)
- {
- var tab = (BuildTab)buildTabs.GetChild(index);
-
- string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
- itemName += " [" + tab.BuildInfo.Configuration + "]";
-
- buildTabsList.AddItem(itemName, tab.IconTexture);
-
- string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
- itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
- itemTooltip += "\nStatus: ";
-
- if (tab.BuildExited)
- itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
- else
- itemTooltip += "Running";
-
- if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
- itemTooltip += $"\nErrors: {tab.ErrorCount}";
-
- itemTooltip += $"\nWarnings: {tab.WarningCount}";
-
- buildTabsList.SetItemTooltip(index, itemTooltip);
-
- // If this tab was already selected before the changes or if no tab was selected
- if (currentTab == null || currentTab == index)
- {
- buildTabsList.Select(index);
- _BuildTabsItemSelected(index);
- }
- }
-
- private void _UpdateBuildTabsList()
- {
- buildTabsList.Clear();
-
- int? currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- currentTab = null;
-
- for (int i = 0; i < buildTabs.GetChildCount(); i++)
- _UpdateBuildTab(i, currentTab);
- }
-
- public BuildTab GetBuildTabFor(BuildInfo buildInfo)
- {
- foreach (var buildTab in new Array<BuildTab>(buildTabs.GetChildren()))
- {
- if (buildTab.BuildInfo.Equals(buildInfo))
- return buildTab;
- }
-
- var newBuildTab = new BuildTab(buildInfo);
- AddBuildTab(newBuildTab);
-
- return newBuildTab;
- }
-
- private void _BuildTabsItemSelected(int idx)
- {
- if (idx < 0 || idx >= buildTabs.GetTabCount())
- throw new IndexOutOfRangeException();
-
- buildTabs.CurrentTab = idx;
- if (!buildTabs.Visible)
- buildTabs.Visible = true;
-
- warningsBtn.Visible = true;
- errorsBtn.Visible = true;
- viewLogBtn.Visible = true;
- }
-
- private void _BuildTabsNothingSelected()
- {
- if (buildTabs.GetTabCount() != 0)
- {
- // just in case
- buildTabs.Visible = false;
-
- // This callback is called when clicking on the empty space of the list.
- // ItemList won't deselect the items automatically, so we must do it ourselves.
- buildTabsList.UnselectAll();
- }
-
- warningsBtn.Visible = false;
- errorsBtn.Visible = false;
- viewLogBtn.Visible = false;
- }
-
- private void _WarningsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.WarningsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- private void _ErrorsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.ErrorsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- public void BuildProjectPressed()
- {
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
-
- string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
- string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
-
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
-
- if (File.Exists(editorScriptsMetadataPath))
- {
- try
- {
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
- }
- catch (IOException e)
- {
- GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}");
- return;
- }
- }
-
- bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
-
- if (!buildSuccess)
- return;
-
- // Notify running game for hot-reload
- Internal.EditorDebuggerNodeReloadScripts();
-
- // Hot-reload in the editor
- GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
-
- if (Internal.IsAssembliesReloadingNeeded())
- Internal.ReloadAssemblies(softReload: false);
- }
-
- private void _ViewLogPressed()
- {
- if (!buildTabsList.IsAnythingSelected())
- return;
-
- var selectedItems = buildTabsList.GetSelectedItems();
-
- if (selectedItems.Length != 1)
- throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}");
-
- int selectedItem = selectedItems[0];
-
- var buildTab = (BuildTab)buildTabs.GetTabControl(selectedItem);
-
- OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, BuildManager.MsBuildLogFileName));
- }
-
- public override void _Notification(int what)
- {
- base._Notification(what);
-
- if (what == EditorSettings.NotificationEditorSettingsChanged)
- {
- var editorBaseControl = editorInterface.GetBaseControl();
- panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
- }
- }
-
- public void AddBuildTab(BuildTab buildTab)
- {
- buildTabs.AddChild(buildTab);
- RaiseBuildTab(buildTab);
- }
-
- public void RaiseBuildTab(BuildTab buildTab)
- {
- if (buildTab.GetParent() != buildTabs)
- throw new InvalidOperationException("Build tab is not in the tabs list");
-
- buildTabs.MoveChild(buildTab, 0);
- _UpdateBuildTabsList();
- }
-
- public void ShowBuildTab()
- {
- for (int i = 0; i < panelTabs.GetTabCount(); i++)
- {
- if (panelTabs.GetTabControl(i) == panelBuildsTab)
- {
- panelTabs.CurrentTab = i;
- GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this);
- return;
- }
- }
-
- GD.PushError("Builds tab not found");
- }
-
- public override void _Ready()
- {
- base._Ready();
-
- editorInterface = GodotSharpEditor.Instance.GetEditorInterface();
-
- var editorBaseControl = editorInterface.GetBaseControl();
-
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
- SetAnchorsAndMarginsPreset(LayoutPreset.Wide);
-
- panelTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- RectMinSize = new Vector2(0, 228) * EditorScale,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
- AddChild(panelTabs);
-
- {
- // Builds tab
- panelBuildsTab = new VBoxContainer
- {
- Name = "Builds".TTR(),
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddChild(panelBuildsTab);
-
- var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
- panelBuildsTab.AddChild(toolBarHBox);
-
- var buildProjectBtn = new Button
- {
- Text = "Build Project".TTR(),
- FocusMode = FocusModeEnum.None
- };
- buildProjectBtn.PressedSignal += BuildProjectPressed;
- toolBarHBox.AddChild(buildProjectBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- warningsBtn = new Button
- {
- Text = "Warnings".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- warningsBtn.Toggled += _WarningsToggled;
- toolBarHBox.AddChild(warningsBtn);
-
- errorsBtn = new Button
- {
- Text = "Errors".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- errorsBtn.Toggled += _ErrorsToggled;
- toolBarHBox.AddChild(errorsBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- viewLogBtn = new Button
- {
- Text = "View log".TTR(),
- FocusMode = FocusModeEnum.None,
- Visible = false
- };
- viewLogBtn.PressedSignal += _ViewLogPressed;
- toolBarHBox.AddChild(viewLogBtn);
-
- var hsc = new HSplitContainer
- {
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelBuildsTab.AddChild(hsc);
-
- buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
- buildTabsList.ItemSelected += _BuildTabsItemSelected;
- buildTabsList.NothingSelected += _BuildTabsNothingSelected;
- hsc.AddChild(buildTabsList);
-
- buildTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- TabsVisible = false
- };
- hsc.AddChild(buildTabs);
- }
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index ab090c46e7..51055dc9b9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,7 +4,7 @@ using Godot.Collections;
using GodotTools.Internals;
using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
[Serializable]
public sealed class BuildInfo : Reference // TODO Remove Reference once we have proper serialization
@@ -20,7 +20,9 @@ namespace GodotTools
public override bool Equals(object obj)
{
if (obj is BuildInfo other)
- return other.Solution == Solution && other.Configuration == Configuration;
+ return other.Solution == Solution && other.Targets == Targets &&
+ other.Configuration == Configuration && other.Restore == Restore &&
+ other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
return false;
}
@@ -31,7 +33,11 @@ namespace GodotTools
{
int hash = 17;
hash = hash * 29 + Solution.GetHashCode();
+ hash = hash * 29 + Targets.GetHashCode();
hash = hash * 29 + Configuration.GetHashCode();
+ hash = hash * 29 + Restore.GetHashCode();
+ hash = hash * 29 + CustomProperties.GetHashCode();
+ hash = hash * 29 + LogsDirPath.GetHashCode();
return hash;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 6399991b84..b96b0c8175 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,20 +1,18 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Build;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
-using GodotTools.Utils;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
+using OS = GodotTools.Utils.OS;
-namespace GodotTools
+namespace GodotTools.Build
{
public static class BuildManager
{
- private static readonly List<BuildInfo> BuildsInProgress = new List<BuildInfo>();
+ private static BuildInfo _buildInProgress;
public const string PropNameMSBuildMono = "MSBuild (Mono)";
public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
@@ -24,6 +22,14 @@ namespace GodotTools
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
public const string MsBuildLogFileName = "msbuild_log.txt";
+ public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
+
+ public static event BuildLaunchFailedEventHandler BuildLaunchFailed;
+ public static event Action<BuildInfo> BuildStarted;
+ public static event Action<BuildResult> BuildFinished;
+ public static event Action<string> StdOutputReceived;
+ public static event Action<string> StdErrorReceived;
+
private static void RemoveOldIssuesFile(BuildInfo buildInfo)
{
var issuesFile = GetIssuesFilePath(buildInfo);
@@ -36,12 +42,13 @@ namespace GodotTools
private static void ShowBuildErrorDialog(string message)
{
- GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error");
- GodotSharpEditor.Instance.BottomPanel.ShowBuildTab();
+ var plugin = GodotSharpEditor.Instance;
+ plugin.ShowErrorDialog(message, "Build error");
+ plugin.MakeBottomPanelItemVisible(plugin.MSBuildPanel);
}
- public static void RestartBuild(BuildTab buildTab) => throw new NotImplementedException();
- public static void StopBuild(BuildTab buildTab) => throw new NotImplementedException();
+ public static void RestartBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
+ public static void StopBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
private static string GetLogFilePath(BuildInfo buildInfo)
{
@@ -61,15 +68,14 @@ namespace GodotTools
public static bool Build(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
- buildTab.OnBuildStart();
+ BuildStarted?.Invoke(buildInfo);
// Required in order to update the build tasks list
Internal.GodotMainIteration();
@@ -80,44 +86,44 @@ namespace GodotTools
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = BuildSystem.Build(buildInfo);
+ int exitCode = BuildSystem.Build(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
public static async Task<bool> BuildAsync(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
+ BuildStarted?.Invoke(buildInfo);
try
{
@@ -125,43 +131,57 @@ namespace GodotTools
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = await BuildSystem.BuildAsync(buildInfo);
+ int exitCode = await BuildSystem.BuildAsync(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
+ public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return BuildProjectBlocking(buildInfo);
+ }
+
+ private static bool BuildProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug");
+ string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
@@ -173,15 +193,6 @@ namespace GodotTools
{
pr.Step("Building project solution", 0);
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
-
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
-
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
-
if (!Build(buildInfo))
{
ShowBuildErrorDialog("Failed to build project solution");
@@ -197,33 +208,51 @@ namespace GodotTools
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ GenerateEditorScriptMetadata();
+
+ if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
+ return true; // Requested play from an external editor/IDE which already built the project
+
+ return BuildProjectBlocking("Debug");
+ }
+
+ // NOTE: This will be replaced with C# source generators in 4.0
+ public static void GenerateEditorScriptMetadata()
+ {
string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
- if (File.Exists(editorScriptsMetadataPath))
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
-
- var currentPlayRequest = GodotSharpEditor.Instance.CurrentPlaySettings;
+ if (!File.Exists(editorScriptsMetadataPath))
+ return;
- if (currentPlayRequest != null)
+ try
{
- if (currentPlayRequest.Value.HasDebugger)
- {
- // Set the environment variable that will tell the player to connect to the IDE debugger
- // TODO: We should probably add a better way to do this
- Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
- "--debugger-agent=transport=dt_socket" +
- $",address={currentPlayRequest.Value.DebuggerHost}:{currentPlayRequest.Value.DebuggerPort}" +
- ",server=n");
- }
-
- if (!currentPlayRequest.Value.BuildBeforePlaying)
- return true; // Requested play from an external editor/IDE which already built the project
+ File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+ }
+ catch (IOException e)
+ {
+ throw new IOException("Failed to copy scripts metadata file.", innerException: e);
}
+ }
- return BuildProjectBlocking("Debug");
+ // NOTE: This will be replaced with C# source generators in 4.0
+ public static string GenerateExportedGameScriptMetadata(bool isDebug)
+ {
+ string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
+ CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
+ return scriptsMetadataPath;
}
public static void Initialize()
@@ -269,8 +298,6 @@ namespace GodotTools
["hint"] = Godot.PropertyHint.Enum,
["hint_string"] = hintString
});
-
- EditorDef("mono/builds/print_build_output", false);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index 8596cd24af..9514cc9622 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -5,16 +5,10 @@ using GodotTools.Internals;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
- public class BuildTab : VBoxContainer
+ public class BuildOutputView : VBoxContainer, ISerializationListener
{
- public enum BuildResults
- {
- Error,
- Success
- }
-
[Serializable]
private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization
{
@@ -29,10 +23,14 @@ namespace GodotTools
private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization
private ItemList issuesList;
+ private TextEdit buildLog;
+ private PopupMenu issuesListContextMenu;
- public bool BuildExited { get; private set; } = false;
+ [Signal] public event Action BuildStateChanged;
- public BuildResults? BuildResult { get; private set; } = null;
+ public bool HasBuildExited { get; private set; } = false;
+
+ public BuildResult? BuildResult { get; private set; } = null;
public int ErrorCount { get; private set; } = 0;
@@ -41,23 +39,31 @@ namespace GodotTools
public bool ErrorsVisible { get; set; } = true;
public bool WarningsVisible { get; set; } = true;
- public Texture2D IconTexture
+ public Texture2D BuildStateIcon
{
get
{
- if (!BuildExited)
+ if (!HasBuildExited)
return GetThemeIcon("Stop", "EditorIcons");
- if (BuildResult == BuildResults.Error)
- return GetThemeIcon("StatusError", "EditorIcons");
+ if (BuildResult == Build.BuildResult.Error)
+ return GetThemeIcon("Error", "EditorIcons");
+
+ if (WarningCount > 1)
+ return GetThemeIcon("Warning", "EditorIcons");
- return GetThemeIcon("StatusSuccess", "EditorIcons");
+ return null;
}
}
- public BuildInfo BuildInfo { get; private set; }
+ private BuildInfo BuildInfo { get; set; }
- private void _LoadIssuesFromFile(string csvFile)
+ public bool LogVisible
+ {
+ set => buildLog.Visible = value;
+ }
+
+ private void LoadIssuesFromFile(string csvFile)
{
using (var file = new Godot.File())
{
@@ -107,7 +113,7 @@ namespace GodotTools
}
}
- private void _IssueActivated(int idx)
+ private void IssueActivated(int idx)
{
if (idx < 0 || idx >= issuesList.GetItemCount())
throw new IndexOutOfRangeException("Item list index out of range");
@@ -190,49 +196,79 @@ namespace GodotTools
}
}
- public void OnBuildStart()
+ private void BuildLaunchFailed(BuildInfo buildInfo, string cause)
+ {
+ HasBuildExited = true;
+ BuildResult = Build.BuildResult.Error;
+
+ issuesList.Clear();
+
+ var issue = new BuildIssue {Message = cause, Warning = false};
+
+ ErrorCount += 1;
+ issues.Add(issue);
+
+ UpdateIssuesList();
+
+ EmitSignal(nameof(BuildStateChanged));
+ }
+
+ private void BuildStarted(BuildInfo buildInfo)
{
- BuildExited = false;
+ BuildInfo = buildInfo;
+ HasBuildExited = false;
issues.Clear();
WarningCount = 0;
ErrorCount = 0;
+ buildLog.Text = string.Empty;
+
UpdateIssuesList();
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ EmitSignal(nameof(BuildStateChanged));
}
- public void OnBuildExit(BuildResults result)
+ private void BuildFinished(BuildResult result)
{
- BuildExited = true;
+ HasBuildExited = true;
BuildResult = result;
- _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+ LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+
UpdateIssuesList();
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ EmitSignal(nameof(BuildStateChanged));
}
- public void OnBuildExecFailed(string cause)
+ private void StdOutputReceived(string text)
{
- BuildExited = true;
- BuildResult = BuildResults.Error;
-
- issuesList.Clear();
+ buildLog.Text += text + "\n";
+ ScrollToLastNonEmptyLogLine();
+ }
- var issue = new BuildIssue { Message = cause, Warning = false };
+ private void StdErrorReceived(string text)
+ {
+ buildLog.Text += text + "\n";
+ ScrollToLastNonEmptyLogLine();
+ }
- ErrorCount += 1;
- issues.Add(issue);
+ private void ScrollToLastNonEmptyLogLine()
+ {
+ int line;
+ for (line = buildLog.GetLineCount(); line > 0; line--)
+ {
+ string lineText = buildLog.GetLine(line);
- UpdateIssuesList();
+ if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim()))
+ break;
+ }
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
+ buildLog.CursorSetLine(line);
}
public void RestartBuild()
{
- if (!BuildExited)
+ if (!HasBuildExited)
throw new InvalidOperationException("Build already started");
BuildManager.RestartBuild(this);
@@ -240,28 +276,118 @@ namespace GodotTools
public void StopBuild()
{
- if (!BuildExited)
+ if (!HasBuildExited)
throw new InvalidOperationException("Build is not in progress");
BuildManager.StopBuild(this);
}
+ private enum IssuesContextMenuOption
+ {
+ Copy
+ }
+
+ private void IssuesListContextOptionPressed(int id)
+ {
+ switch ((IssuesContextMenuOption)id)
+ {
+ case IssuesContextMenuOption.Copy:
+ {
+ // We don't allow multi-selection but just in case that changes later...
+ string text = null;
+
+ foreach (int issueIndex in issuesList.GetSelectedItems())
+ {
+ if (text != null)
+ text += "\n";
+ text += issuesList.GetItemText(issueIndex);
+ }
+
+ if (text != null)
+ DisplayServer.ClipboardSet(text);
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid issue context menu option");
+ }
+ }
+
+ private void IssuesListRmbSelected(int index, Vector2 atPosition)
+ {
+ _ = index; // Unused
+
+ issuesListContextMenu.Clear();
+ issuesListContextMenu.Size = new Vector2i(1, 1);
+
+ if (issuesList.IsAnythingSelected())
+ {
+ // Add menu entries for the selected item
+ issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
+ label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy);
+ }
+
+ if (issuesListContextMenu.GetItemCount() > 0)
+ {
+ issuesListContextMenu.Position = (Vector2i)(issuesList.RectGlobalPosition + atPosition);
+ issuesListContextMenu.Popup();
+ }
+ }
+
public override void _Ready()
{
base._Ready();
- issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
- issuesList.ItemActivated += _IssueActivated;
- AddChild(issuesList);
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var hsc = new HSplitContainer
+ {
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill
+ };
+ AddChild(hsc);
+
+ issuesList = new ItemList
+ {
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log
+ };
+ issuesList.ItemActivated += IssueActivated;
+ issuesList.AllowRmbSelect = true;
+ issuesList.ItemRmbSelected += IssuesListRmbSelected;
+ hsc.AddChild(issuesList);
+
+ issuesListContextMenu = new PopupMenu();
+ issuesListContextMenu.IdPressed += IssuesListContextOptionPressed;
+ issuesList.AddChild(issuesListContextMenu);
+
+ buildLog = new TextEdit
+ {
+ Readonly = true,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
+ };
+ hsc.AddChild(buildLog);
+
+ AddBuildEventListeners();
}
- private BuildTab()
+ private void AddBuildEventListeners()
{
+ BuildManager.BuildLaunchFailed += BuildLaunchFailed;
+ BuildManager.BuildStarted += BuildStarted;
+ BuildManager.BuildFinished += BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived += line => CallDeferred(nameof(StdOutputReceived), line);
+ BuildManager.StdErrorReceived += line => CallDeferred(nameof(StdErrorReceived), line);
}
- public BuildTab(BuildInfo buildInfo)
+ public void OnBeforeSerialize()
{
- BuildInfo = buildInfo;
+ }
+
+ public void OnAfterDeserialize()
+ {
+ AddBuildEventListeners(); // Re-add them
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
new file mode 100644
index 0000000000..59055b60c3
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
@@ -0,0 +1,8 @@
+namespace GodotTools.Build
+{
+ public enum BuildResult
+ {
+ Error,
+ Success
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index d9862ae361..bac7a2e6db 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -44,10 +44,7 @@ namespace GodotTools.Build
}
}
- private static bool PrintBuildOutput =>
- (bool)EditorSettings.GetSetting("mono/builds/print_build_output");
-
- private static Process LaunchBuild(BuildInfo buildInfo)
+ private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
@@ -58,13 +55,13 @@ namespace GodotTools.Build
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
- bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
-
- if (!redirectOutput || Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
- startInfo.RedirectStandardOutput = redirectOutput;
- startInfo.RedirectStandardError = redirectOutput;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
if (UsingMonoMsBuildOnWindows)
@@ -82,20 +79,22 @@ namespace GodotTools.Build
var process = new Process {StartInfo = startInfo};
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+
process.Start();
- if (redirectOutput)
- {
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- }
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
return process;
}
- public static int Build(BuildInfo buildInfo)
+ public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,9 +102,9 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(BuildInfo buildInfo)
+ public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
await process.WaitForExitAsync();
@@ -152,10 +151,5 @@ namespace GodotTools.Build
foreach (string env in platformEnvironmentVariables)
environmentVariables.Remove(env);
}
-
- private static bool IsDebugMsBuildRequested()
- {
- return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
- }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
new file mode 100644
index 0000000000..708ec73454
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -0,0 +1,185 @@
+using System;
+using Godot;
+using GodotTools.Internals;
+using JetBrains.Annotations;
+using static GodotTools.Internals.Globals;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public class MSBuildPanel : VBoxContainer
+ {
+ public BuildOutputView BuildOutputView { get; private set; }
+
+ private Button errorsBtn;
+ private Button warningsBtn;
+ private Button viewLogBtn;
+
+ private void WarningsToggled(bool pressed)
+ {
+ BuildOutputView.WarningsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ private void ErrorsToggled(bool pressed)
+ {
+ BuildOutputView.ErrorsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ [UsedImplicitly]
+ public void BuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ BuildManager.GenerateEditorScriptMetadata();
+
+ if (!BuildManager.BuildProjectBlocking("Debug"))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.EditorDebuggerNodeReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void RebuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ BuildManager.GenerateEditorScriptMetadata();
+
+ if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Rebuild"}))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.EditorDebuggerNodeReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void CleanSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ BuildManager.BuildProjectBlocking("Debug", targets: new[] {"Clean"});
+ }
+
+ private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
+
+ private void BuildMenuOptionPressed(int id)
+ {
+ switch ((BuildMenuOptions)id)
+ {
+ case BuildMenuOptions.BuildSolution:
+ BuildSolution();
+ break;
+ case BuildMenuOptions.RebuildSolution:
+ RebuildSolution();
+ break;
+ case BuildMenuOptions.CleanSolution:
+ CleanSolution();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
+ }
+ }
+
+ private enum BuildMenuOptions
+ {
+ BuildSolution,
+ RebuildSolution,
+ CleanSolution
+ }
+
+ public override void _Ready()
+ {
+ base._Ready();
+
+ RectMinSize = new Vector2(0, 228) * EditorScale;
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
+ AddChild(toolBarHBox);
+
+ var buildMenuBtn = new MenuButton {Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons")};
+ toolBarHBox.AddChild(buildMenuBtn);
+
+ var buildMenu = buildMenuBtn.GetPopup();
+ buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution);
+ buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution);
+ buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution);
+ buildMenu.IdPressed += BuildMenuOptionPressed;
+
+ errorsBtn = new Button
+ {
+ HintTooltip = "Show Errors".TTR(),
+ Icon = GetThemeIcon("StatusError", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ errorsBtn.Toggled += ErrorsToggled;
+ toolBarHBox.AddChild(errorsBtn);
+
+ warningsBtn = new Button
+ {
+ HintTooltip = "Show Warnings".TTR(),
+ Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ warningsBtn.Toggled += WarningsToggled;
+ toolBarHBox.AddChild(warningsBtn);
+
+ viewLogBtn = new Button
+ {
+ Text = "Show Output".TTR(),
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ viewLogBtn.Toggled += ViewLogToggled;
+ toolBarHBox.AddChild(viewLogBtn);
+
+ BuildOutputView = new BuildOutputView();
+ AddChild(BuildOutputView);
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index f36e581a5f..e2feb66e35 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -31,7 +31,7 @@ namespace GodotTools.Build
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Visual Studio.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
goto case BuildTool.MsBuildVs;
}
case BuildTool.MsBuildVs:
@@ -89,7 +89,7 @@ namespace GodotTools.Build
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Mono.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
goto case BuildTool.MsBuildMono;
}
case BuildTool.MsBuildMono:
@@ -119,7 +119,7 @@ namespace GodotTools.Build
{
var result = new List<string>();
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
result.Add("/usr/local/var/homebrew/linked/mono/bin/");
@@ -161,8 +161,21 @@ namespace GodotTools.Build
// Try to find 15.0 with vswhere
- string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)");
- vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
+ var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"};
+
+ string vsWherePath = null;
+ foreach (var envName in envNames)
+ {
+ vsWherePath = Environment.GetEnvironmentVariable(envName);
+ if (!string.IsNullOrEmpty(vsWherePath))
+ {
+ vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
+ if (File.Exists(vsWherePath))
+ break;
+ }
+
+ vsWherePath = null;
+ }
var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
new file mode 100644
index 0000000000..793ef7fd71
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -0,0 +1,296 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+using Godot;
+using GodotTools.Internals;
+using GodotTools.Shared;
+using Directory = GodotTools.Utils.Directory;
+using Environment = System.Environment;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public static class NuGetUtils
+ {
+ public const string GodotFallbackFolderName = "Godot Offline Packages";
+
+ public static string GodotFallbackFolderPath
+ => Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
+
+ private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(nuGetConfigPath);
+
+ const string nuGetConfigRootName = "configuration";
+
+ var rootNode = xmlDoc.DocumentElement;
+
+ if (rootNode == null)
+ {
+ // No root node, create it
+ rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
+ xmlDoc.AppendChild(rootNode);
+
+ // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
+ XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
+ rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
+ }
+ else
+ {
+ // Check that the root node is the expected one
+ if (rootNode.Name != nuGetConfigRootName)
+ throw new Exception("Invalid root Xml node for NuGet.Config. " +
+ $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
+ }
+
+ var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
+ rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
+
+ // Check if it already has our fallback package folder
+ for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
+ {
+ if (xmlNode.NodeType != XmlNodeType.Element)
+ continue;
+
+ var xmlElement = (XmlElement)xmlNode;
+ if (xmlElement.Name == "add" &&
+ xmlElement.Attributes["key"]?.Value == name &&
+ xmlElement.Attributes["value"]?.Value == path)
+ {
+ return;
+ }
+ }
+
+ XmlElement newEntry = xmlDoc.CreateElement("add");
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
+
+ fallbackFoldersNode.AppendChild(newEntry);
+
+ xmlDoc.Save(nuGetConfigPath);
+ }
+
+ /// <summary>
+ /// Returns all the paths where the user NuGet.Config files can be found.
+ /// Does not determine whether the returned files exist or not.
+ /// </summary>
+ private static string[] GetAllUserNuGetConfigFilePaths()
+ {
+ // Where to find 'NuGet/NuGet.Config':
+ //
+ // - Mono/.NETFramework (standalone NuGet):
+ // Uses Environment.SpecialFolder.ApplicationData
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$HOME/.config'
+ // - CoreCLR (dotnet CLI NuGet):
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$DOTNET_CLI_HOME/.nuget' otherwise '$HOME/.nuget'
+
+ string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+
+ if (Utils.OS.IsWindows)
+ {
+ // %APPDATA% for both
+ return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
+ }
+
+ var paths = new string[2];
+
+ // CoreCLR (dotnet CLI NuGet)
+
+ string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
+ if (!string.IsNullOrEmpty(dotnetCliHome))
+ {
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ }
+ else
+ {
+ string home = Environment.GetEnvironmentVariable("HOME");
+ if (string.IsNullOrEmpty(home))
+ throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
+ }
+
+ // Mono/.NETFramework (standalone NuGet)
+
+ // ApplicationData is $HOME/.config on Linux/macOS
+ paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+
+ return paths;
+ }
+
+ // nupkg extraction
+ //
+ // Exclude: (NuGet.Client -> NuGet.Packaging.PackageHelper.ExcludePaths)
+ // package/
+ // _rels/
+ // [Content_Types].xml
+ //
+ // Don't ignore files that begin with a dot (.)
+ //
+ // The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
+
+ /// <summary>
+ /// Adds the specified fallback folder to the user NuGet.Config files,
+ /// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
+ /// </summary>
+ public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ {
+ foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ {
+ if (!System.IO.File.Exists(nuGetConfigPath))
+ {
+ // It doesn't exist, so we create a default one
+ const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
+<configuration>
+ <packageSources>
+ <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
+ </packageSources>
+</configuration>
+";
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
+ }
+
+ AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ }
+ }
+
+ private static void AddPackageToFallbackFolder(string fallbackFolder,
+ string nupkgPath, string packageId, string packageVersion)
+ {
+ // dotnet CLI provides no command for this, but we can do it manually.
+ //
+ // - The expected structure is as follows:
+ // fallback_folder/
+ // <package.name>/<version>/
+ // <package.name>.<version>.nupkg
+ // <package.name>.<version>.nupkg.sha512
+ // <package.name>.nuspec
+ // ... extracted nupkg files (check code for excluded files) ...
+ //
+ // - <package.name> and <version> must be in lower case.
+ // - The sha512 of the nupkg is base64 encoded.
+ // - We can get the nuspec from the nupkg which is a Zip file.
+
+ string packageIdLower = packageId.ToLower();
+ string packageVersionLower = packageVersion.ToLower();
+
+ string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
+ string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
+ string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+
+ if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
+ return; // Already added (for speed we don't check if every file is properly extracted)
+
+ Directory.CreateDirectory(destDir);
+
+ // Generate .nupkg.sha512 file
+
+ using (var alg = SHA512.Create())
+ {
+ alg.ComputeHash(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(alg.Hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+ }
+
+ // Extract nupkg
+ ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
+
+ // Copy .nupkg
+ File.Copy(nupkgPath, nupkgDestPath);
+ }
+
+ private static readonly string[] NupkgExcludePaths =
+ {
+ "_rels/",
+ "package/",
+ "[Content_Types].xml"
+ };
+
+ private static void ExtractNupkg(string destDir, string nupkgPath, string packageId, string packageVersion)
+ {
+ // NOTE: Must use SimplifyGodotPath to make sure we don't extract files outside the destination directory.
+
+ using (var archive = ZipFile.OpenRead(nupkgPath))
+ {
+ // Extract .nuspec manually as it needs to be in lower case
+
+ var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
+
+ if (nuspecEntry == null)
+ throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+
+ // Extract the other package files
+
+ foreach (var entry in archive.Entries)
+ {
+ // NOTE: SimplifyGodotPath() removes trailing slash and backslash,
+ // so we can't use the result to check if the entry is a directory.
+
+ string entryFullName = entry.FullName.Replace('\\', '/');
+
+ // Check if the file must be ignored
+ if ( // Excluded files.
+ NupkgExcludePaths.Any(e => entryFullName.StartsWith(e, StringComparison.OrdinalIgnoreCase)) ||
+ // Nupkg hash files and nupkg metadata files on all directory.
+ entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
+ entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
+ // Nuspec at root level. We already extracted it previously but in lower case.
+ entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
+ {
+ continue;
+ }
+
+ string entryFullNameSimplified = entryFullName.SimplifyGodotPath();
+ string destFilePath = Path.Combine(destDir, entryFullNameSimplified);
+ bool isDir = entryFullName.EndsWith("/");
+
+ if (isDir)
+ {
+ Directory.CreateDirectory(destFilePath);
+ }
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
+ entry.ExtractToFile(destFilePath, overwrite: true);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies and extracts all the Godot bundled packages to the Godot NuGet fallback folder.
+ /// Does nothing if the packages were already copied.
+ /// </summary>
+ public static void AddBundledPackagesToFallbackFolder(string fallbackFolder)
+ {
+ GD.Print("Copying Godot Offline Packages...");
+
+ string nupkgsLocation = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "nupkgs");
+
+ void AddPackage(string packageId, string packageVersion)
+ {
+ string nupkgPath = Path.Combine(nupkgsLocation, $"{packageId}.{packageVersion}.nupkg");
+ AddPackageToFallbackFolder(fallbackFolder, nupkgPath, packageId, packageVersion);
+ }
+
+ foreach (var (packageId, packageVersion) in PackagesToAdd)
+ AddPackage(packageId, packageVersion);
+ }
+
+ private static readonly (string packageId, string packageVersion)[] PackagesToAdd =
+ {
+ ("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk)
+ };
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
index a8afb38728..1d800b8151 100644
--- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
@@ -48,7 +48,7 @@ namespace GodotTools
var firstMatch = classes.FirstOrDefault(classDecl =>
classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object.
- classDecl.SearchName != searchName // Filter by the name we're looking for
+ classDecl.SearchName == searchName // Filter by the name we're looking for
);
if (firstMatch == null)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index f60e469503..5bb8d444c2 100755
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -120,7 +120,7 @@ namespace GodotTools.Export
string assemblyPath = assembly.Value;
string outputFileExtension = platform == OS.Platforms.Windows ? ".dll" :
- platform == OS.Platforms.OSX ? ".dylib" :
+ platform == OS.Platforms.MacOS ? ".dylib" :
".so";
string outputFileName = assemblyName + ".dll" + outputFileExtension;
@@ -132,7 +132,7 @@ namespace GodotTools.Export
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
- if (platform == OS.Platforms.OSX)
+ if (platform == OS.Platforms.MacOS)
{
exporter.AddSharedObject(tempOutputFilePath, tags: null);
}
@@ -328,7 +328,7 @@ MONO_AOT_MODE_LAST = 1000,
if (lipoExitCode != 0)
throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}");
- // TODO: Add the AOT lib and interpreter libs as device only to supress warnings when targeting the simulator
+ // TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator
// Add the fat AOT static library to the Xcode project
exporter.AddIosProjectStaticLib(fatOutputFilePath);
@@ -581,7 +581,7 @@ MONO_AOT_MODE_LAST = 1000,
string arch = bits == "64" ? "x86_64" : "i686";
return $"windows-{arch}";
}
- case OS.Platforms.OSX:
+ case OS.Platforms.MacOS:
{
Debug.Assert(bits == null || bits == "64");
string arch = "x86_64";
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 554763eecb..e18ed7f107 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
using JetBrains.Annotations;
@@ -19,7 +20,7 @@ namespace GodotTools.Export
public class ExportPlugin : EditorExportPlugin
{
[Flags]
- enum I18NCodesets
+ enum I18NCodesets : long
{
None = 0,
CJK = 1,
@@ -143,6 +144,8 @@ namespace GodotTools.Export
private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
+ _ = flags; // Unused
+
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return;
@@ -154,12 +157,10 @@ namespace GodotTools.Export
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
-
+ string scriptsMetadataPath = BuildManager.GenerateExportedGameScriptMetadata(isDebug);
AddFile(scriptsMetadataPath, scriptsMetadataPath);
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
+ if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
throw new Exception("Failed to build project");
// Add dependency assemblies
@@ -340,7 +341,7 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
- return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
+ return !new[] {OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
@@ -411,7 +412,7 @@ namespace GodotTools.Export
case OS.Platforms.Windows:
case OS.Platforms.UWP:
return "net_4_x_win";
- case OS.Platforms.OSX:
+ case OS.Platforms.MacOS:
case OS.Platforms.LinuxBSD:
case OS.Platforms.Server:
case OS.Platforms.Haiku:
@@ -430,7 +431,7 @@ namespace GodotTools.Export
private static string DetermineDataDirNameForProject()
{
var appName = (string)ProjectSettings.GetSetting("application/config/name");
- string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
+ string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index a363ecc920..58561c5097 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -4,9 +4,9 @@ using GodotTools.Export;
using GodotTools.Utils;
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using GodotTools.Build;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -19,7 +19,6 @@ using Path = System.IO.Path;
namespace GodotTools
{
- [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class GodotSharpEditor : EditorPlugin, ISerializationListener
{
private EditorSettings editorSettings;
@@ -31,20 +30,22 @@ namespace GodotTools
private CheckBox aboutDialogCheckBox;
private Button bottomPanelBtn;
+ private Button toolBarBuildButton;
public GodotIdeManager GodotIdeManager { get; private set; }
private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization
- public BottomPanel BottomPanel { get; private set; }
+ public MSBuildPanel MSBuildPanel { get; private set; }
- public PlaySettings? CurrentPlaySettings { get; set; }
+ public bool SkipBuildBeforePlaying { get; set; } = false;
public static string ProjectAssemblyName
{
get
{
var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
if (string.IsNullOrEmpty(projectAssemblyName))
projectAssemblyName = "UnnamedProject";
return projectAssemblyName;
@@ -126,6 +127,7 @@ namespace GodotTools
{
menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
bottomPanelBtn.Show();
+ toolBarBuildButton.Show();
}
private void _ShowAboutDialog()
@@ -145,12 +147,27 @@ namespace GodotTools
case MenuOptions.AboutCSharp:
_ShowAboutDialog();
break;
+ case MenuOptions.SetupGodotNugetFallbackFolder:
+ {
+ try
+ {
+ string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
+ }
+ catch (Exception e)
+ {
+ ShowErrorDialog("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ break;
+ }
default:
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
}
}
- private void _BuildSolutionPressed()
+ private void BuildSolutionPressed()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
{
@@ -158,23 +175,22 @@ namespace GodotTools
return; // Failed to create solution
}
- Instance.BottomPanel.BuildProjectPressed();
+ Instance.MSBuildPanel.BuildSolution();
}
- public override void _Notification(int what)
+ public override void _Ready()
{
- base._Notification(what);
+ base._Ready();
- if (what == NotificationReady)
+ MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
+
+ bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
+ if (showInfoDialog)
{
- bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- if (showInfoDialog)
- {
- aboutDialog.Exclusive = true;
- _ShowAboutDialog();
- // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
- aboutDialog.Exclusive = false;
- }
+ aboutDialog.Exclusive = true;
+ _ShowAboutDialog();
+ // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
+ aboutDialog.Exclusive = false;
}
}
@@ -182,6 +198,7 @@ namespace GodotTools
{
CreateSln,
AboutCSharp,
+ SetupGodotNugetFallbackFolder,
}
public void ShowErrorDialog(string message, string title = "Error")
@@ -269,7 +286,7 @@ namespace GodotTools
bool osxAppBundleInstalled = false;
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
// The package path is '/Applications/Visual Studio Code.app'
const string vscodeBundleId = "com.microsoft.VSCode";
@@ -309,7 +326,7 @@ namespace GodotTools
string command;
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
{
@@ -390,6 +407,12 @@ namespace GodotTools
}
}
+ private void BuildStateChanged()
+ {
+ if (bottomPanelBtn != null)
+ bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
+ }
+
public override void EnablePlugin()
{
base.EnablePlugin();
@@ -406,20 +429,20 @@ namespace GodotTools
errorDialog = new AcceptDialog();
editorBaseControl.AddChild(errorDialog);
- BottomPanel = new BottomPanel();
-
- bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR());
+ MSBuildPanel = new MSBuildPanel();
+ bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
menuPopup = new PopupMenu();
menuPopup.Hide();
- AddToolSubmenuItem("Mono", menuPopup);
+ AddToolSubmenuItem("C#", menuPopup);
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
{
menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp);
+ menuPopup.AddItem("Setup Godot NuGet Offline Packages".TTR(), (int)MenuOptions.SetupGodotNugetFallbackFolder);
aboutDialog = new AcceptDialog();
editorBaseControl.AddChild(aboutDialog);
aboutDialog.Title = "Important: C# support is not feature-complete";
@@ -467,6 +490,15 @@ namespace GodotTools
aboutVBox.AddChild(aboutDialogCheckBox);
}
+ toolBarBuildButton = new Button
+ {
+ Text = "Build",
+ HintTooltip = "Build solution",
+ FocusMode = Control.FocusModeEnum.None
+ };
+ toolBarBuildButton.PressedSignal += BuildSolutionPressed;
+ AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton);
+
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
ApplyNecessaryChangesToSolution();
@@ -474,20 +506,12 @@ namespace GodotTools
else
{
bottomPanelBtn.Hide();
+ toolBarBuildButton.Hide();
menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
}
menuPopup.IdPressed += _MenuOptionPressed;
- var buildButton = new Button
- {
- Text = "Build",
- HintTooltip = "Build solution",
- FocusMode = Control.FocusModeEnum.None
- };
- buildButton.PressedSignal += _BuildSolutionPressed;
- AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
-
// External editor settings
EditorDef("mono/editor/external_editor", ExternalEditorId.None);
@@ -500,7 +524,7 @@ namespace GodotTools
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
- else if (OS.IsOSX)
+ else if (OS.IsMacOS)
{
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
@@ -528,6 +552,16 @@ namespace GodotTools
exportPlugin.RegisterExportSettings();
exportPluginWeak = WeakRef(exportPlugin);
+ try
+ {
+ // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to add Godot NuGet Offline Packages to NuGet.Config: " + e.Message);
+ }
+
BuildManager.Initialize();
RiderPathManager.Initialize();
@@ -566,6 +600,7 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
+ [UsedImplicitly]
private GodotSharpEditor()
{
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index e4932ca217..451ce39f5c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -111,7 +111,7 @@ namespace GodotTools.Ides
{
MonoDevelop.Instance GetMonoDevelopInstance(string solutionPath)
{
- if (Utils.OS.IsOSX && editorId == ExternalEditorId.VisualStudioForMac)
+ if (Utils.OS.IsMacOS && editorId == ExternalEditorId.VisualStudioForMac)
{
vsForMacInstance = (vsForMacInstance?.IsDisposed ?? true ? null : vsForMacInstance) ??
new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 17f3339560..eb34a2d0f7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -330,9 +330,10 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings = new PlaySettings();
+ // TODO: Add BuildBeforePlaying flag to PlayRequest
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
});
return Task.FromResult<Response>(new PlayResponse());
}
@@ -341,10 +342,22 @@ namespace GodotTools.Ides
{
DispatchToMainThread(() =>
{
- GodotSharpEditor.Instance.CurrentPlaySettings =
- new PlaySettings(request.DebuggerHost, request.DebuggerPort, request.BuildBeforePlaying ?? true);
+ // Tell the build callback whether the editor already built the solution or not
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = !(request.BuildBeforePlaying ?? true);
+
+ // Pass the debugger agent settings to the player via an environment variables
+ // TODO: It would be better if this was an argument in EditorRunPlay instead
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT",
+ "--debugger-agent=transport=dt_socket" +
+ $",address={request.DebuggerHost}:{request.DebuggerPort}" +
+ ",server=n");
+
+ // Run the game
Internal.EditorRunPlay();
- GodotSharpEditor.Instance.CurrentPlaySettings = null;
+
+ // Restore normal settings
+ Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", "");
+ GodotSharpEditor.Instance.SkipBuildBeforePlaying = false;
});
return Task.FromResult<Response>(new DebugPlayResponse());
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
index d6fa2eeba7..fd7bbd5578 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -26,7 +26,7 @@ namespace GodotTools.Ides.MonoDevelop
string command;
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
string bundleId = BundleIds[editorId];
@@ -85,7 +85,7 @@ namespace GodotTools.Ides.MonoDevelop
public Instance(string solutionFile, EditorId editorId)
{
- if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX)
+ if (editorId == EditorId.VisualStudioForMac && !OS.IsMacOS)
throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");
this.solutionFile = solutionFile;
@@ -103,7 +103,7 @@ namespace GodotTools.Ides.MonoDevelop
static Instance()
{
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
ExecutableNames = new Dictionary<EditorId, string>
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index e22e9af919..94fc5da425 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -32,7 +32,7 @@ namespace GodotTools.Ides.Rider
{
return CollectRiderInfosWindows();
}
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
return CollectRiderInfosMac();
}
@@ -138,7 +138,7 @@ namespace GodotTools.Ides.Rider
return GetToolboxRiderRootPath(localAppData);
}
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
var home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
@@ -211,7 +211,7 @@ namespace GodotTools.Ides.Rider
{
if (OS.IsWindows || OS.IsUnixLike)
return "../../build.txt";
- if (OS.IsOSX)
+ if (OS.IsMacOS)
return "Contents/Resources/build.txt";
throw new Exception("Unknown OS.");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 6c05891f2c..e745966435 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -21,7 +21,7 @@ namespace GodotTools.Utils
public static class Names
{
public const string Windows = "Windows";
- public const string OSX = "OSX";
+ public const string MacOS = "macOS";
public const string Linux = "Linux";
public const string FreeBSD = "FreeBSD";
public const string NetBSD = "NetBSD";
@@ -37,7 +37,7 @@ namespace GodotTools.Utils
public static class Platforms
{
public const string Windows = "windows";
- public const string OSX = "osx";
+ public const string MacOS = "osx";
public const string LinuxBSD = "linuxbsd";
public const string Server = "server";
public const string UWP = "uwp";
@@ -50,7 +50,7 @@ namespace GodotTools.Utils
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
[Names.Windows] = Platforms.Windows,
- [Names.OSX] = Platforms.OSX,
+ [Names.MacOS] = Platforms.MacOS,
[Names.Linux] = Platforms.LinuxBSD,
[Names.FreeBSD] = Platforms.LinuxBSD,
[Names.NetBSD] = Platforms.LinuxBSD,
@@ -77,11 +77,11 @@ namespace GodotTools.Utils
new[] {Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD};
private static readonly IEnumerable<string> UnixLikePlatforms =
- new[] {Names.OSX, Names.Server, Names.Haiku, Names.Android, Names.iOS}
+ new[] {Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS}
.Concat(LinuxBSDPlatforms).ToArray();
private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
- private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX));
+ private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS));
private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms));
private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
@@ -92,7 +92,7 @@ namespace GodotTools.Utils
private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
public static bool IsWindows => _isWindows.Value || IsUWP;
- public static bool IsOSX => _isOSX.Value;
+ public static bool IsMacOS => _isMacOS.Value;
public static bool IsLinuxBSD => _isLinuxBSD.Value;
public static bool IsServer => _isServer.Value;
public static bool IsUWP => _isUWP.Value;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a17c371117..ff3122a77f 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -32,13 +32,13 @@
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
#include "core/io/compression.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/ucaps.h"
+#include "core/string/ucaps.h"
#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_defs.h"
@@ -97,7 +97,7 @@
#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(12)
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
@@ -1368,7 +1368,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.append("." + ctor_method);
- output.append("(this);\n" CLOSE_BLOCK_L2);
+ output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
output.append(MEMBER_BEGIN "internal ");
@@ -2426,7 +2426,7 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::VECTOR2:
case Variant::RECT2:
case Variant::VECTOR3:
- case Variant::_RID:
+ case Variant::RID:
case Variant::ARRAY:
case Variant::DICTIONARY:
case Variant::PACKED_BYTE_ARRAY:
@@ -2979,7 +2979,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "new %s()";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
break;
- case Variant::_RID:
+ case Variant::RID:
ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
"Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_RID) + "'.");
@@ -3399,7 +3399,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
}
void BindingsGenerator::_populate_global_constants() {
- int global_constants_count = GlobalConstants::get_global_constant_count();
+ int global_constants_count = CoreConstants::get_global_constant_count();
if (global_constants_count > 0) {
Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
@@ -3409,7 +3409,7 @@ void BindingsGenerator::_populate_global_constants() {
const DocData::ClassDoc &global_scope_doc = match->value();
for (int i = 0; i < global_constants_count; i++) {
- String constant_name = GlobalConstants::get_global_constant_name(i);
+ String constant_name = CoreConstants::get_global_constant_name(i);
const DocData::ConstantDoc *const_doc = nullptr;
for (int j = 0; j < global_scope_doc.constants.size(); j++) {
@@ -3421,8 +3421,8 @@ void BindingsGenerator::_populate_global_constants() {
}
}
- int constant_value = GlobalConstants::get_global_constant_value(i);
- StringName enum_name = GlobalConstants::get_global_constant_enum(i);
+ int constant_value = CoreConstants::get_global_constant_value(i);
+ StringName enum_name = CoreConstants::get_global_constant_enum(i);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
iconstant.const_doc = const_doc;
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 90c1c9f3ee..eeab518954 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -31,14 +31,14 @@
#ifndef BINDINGS_GENERATOR_H
#define BINDINGS_GENERATOR_H
-#include "core/class_db.h"
-#include "core/string_builder.h"
+#include "core/object/class_db.h"
+#include "core/string/string_builder.h"
#include "editor/doc_data.h"
#include "editor/editor_help.h"
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
class BindingsGenerator {
struct ConstantInterface {
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index 942c6d26a6..9defd65190 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -30,7 +30,7 @@
#include "code_completion.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#include "scene/gui/control.h"
diff --git a/modules/mono/editor/code_completion.h b/modules/mono/editor/code_completion.h
index 77673b766f..b9d22de0b3 100644
--- a/modules/mono/editor/code_completion.h
+++ b/modules/mono/editor/code_completion.h
@@ -31,8 +31,8 @@
#ifndef CODE_COMPLETION_H
#define CODE_COMPLETION_H
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/ustring.h"
+#include "core/variant/variant.h"
namespace gdmono {
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index b15e9b060a..4fa753ab8b 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -32,9 +32,9 @@
#include <mono/metadata/image.h>
+#include "core/config/project_settings.h"
#include "core/io/file_access_pack.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_assembly.h"
@@ -43,6 +43,16 @@
namespace GodotSharpExport {
+MonoAssemblyName *new_mono_assembly_name() {
+ // Mono has no public API to create an empty MonoAssemblyName and the struct is private.
+ // As such the only way to create it is with a stub name and then clear it.
+
+ MonoAssemblyName *aname = mono_assembly_name_new("stub");
+ CRASH_COND(aname == nullptr);
+ mono_assembly_name_free(aname); // Frees the string fields, not the struct
+ return aname;
+}
+
struct AssemblyRefInfo {
String name;
uint16_t major;
@@ -67,7 +77,7 @@ AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
};
}
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
+Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
MonoImage *image = p_assembly->get_image();
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
@@ -79,26 +89,16 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
continue;
}
- GDMonoAssembly *ref_assembly = nullptr;
-
- {
- MonoAssemblyName *ref_aname = mono_assembly_name_new("A"); // We can't allocate an empty MonoAssemblyName, hence "A"
- CRASH_COND(ref_aname == nullptr);
- SCOPE_EXIT {
- mono_assembly_name_free(ref_aname);
- mono_free(ref_aname);
- };
+ mono_assembly_get_assemblyref(image, i, reusable_aname);
- mono_assembly_get_assemblyref(image, i, ref_aname);
-
- if (!GDMono::get_singleton()->load_assembly(ref_name, ref_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
- ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
- }
-
- r_assembly_dependencies[ref_name] = ref_assembly->get_path();
+ GDMonoAssembly *ref_assembly = NULL;
+ if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
+ ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
}
- Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
+ r_assembly_dependencies[ref_name] = ref_assembly->get_path();
+
+ Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
}
@@ -130,7 +130,10 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
- Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies);
+ MonoAssemblyName *reusable_aname = new_mono_assembly_name();
+ SCOPE_EXIT { mono_free(reusable_aname); };
+
+ Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies);
if (err != OK) {
return err;
}
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
index 9ab57755de..bd0f86a74b 100644
--- a/modules/mono/editor/godotsharp_export.h
+++ b/modules/mono/editor/godotsharp_export.h
@@ -31,9 +31,9 @@
#ifndef GODOTSHARP_EXPORT_H
#define GODOTSHARP_EXPORT_H
-#include "core/dictionary.h"
-#include "core/error_list.h"
-#include "core/ustring.h"
+#include "core/error/error_list.h"
+#include "core/string/ustring.h"
+#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_header.h"
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
index 430c82953e..70940c279e 100644
--- a/modules/mono/editor/script_class_parser.cpp
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -30,8 +30,8 @@
#include "script_class_parser.h"
-#include "core/map.h"
#include "core/os/os.h"
+#include "core/templates/map.h"
#include "../utils/string_utils.h"
@@ -151,7 +151,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
case '"': {
bool verbatim = idx != 0 && code[idx - 1] == '@';
- CharType begin_str = code[idx];
+ char32_t begin_str = code[idx];
idx++;
String tk_string = String();
while (true) {
@@ -170,13 +170,13 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
} else if (code[idx] == '\\' && !verbatim) {
//escaped characters...
idx++;
- CharType next = code[idx];
+ char32_t next = code[idx];
if (next == 0) {
error_str = "Unterminated String";
error = true;
return TK_ERROR;
}
- CharType res = 0;
+ char32_t res = 0;
switch (next) {
case 'b':
@@ -234,7 +234,7 @@ ScriptClassParser::Token ScriptClassParser::get_token() {
if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
//a number
- const CharType *rptr;
+ const char32_t *rptr;
double number = String::to_float(&code[idx], &rptr);
idx += (rptr - &code[idx]);
value = number;
diff --git a/modules/mono/editor/script_class_parser.h b/modules/mono/editor/script_class_parser.h
index d611e8fb74..3c55fa07a7 100644
--- a/modules/mono/editor/script_class_parser.h
+++ b/modules/mono/editor/script_class_parser.h
@@ -31,9 +31,9 @@
#ifndef SCRIPT_CLASS_PARSER_H
#define SCRIPT_CLASS_PARSER_H
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
class ScriptClassParser {
public:
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index a963810d63..ce613f7ef9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -44,6 +44,15 @@ namespace Godot.Collections
Add(element);
}
+ public Array(params object[] array) : this()
+ {
+ if (array == null)
+ {
+ throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
+ }
+ safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ }
+
internal Array(ArraySafeHandle handle)
{
safeHandle = handle;
@@ -72,6 +81,16 @@ namespace Godot.Collections
return godot_icall_Array_Resize(GetPtr(), newSize);
}
+ public void Shuffle()
+ {
+ godot_icall_Array_Shuffle(GetPtr());
+ }
+
+ public static Array operator +(Array left, Array right)
+ {
+ return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
+ }
+
// IDisposable
public void Dispose()
@@ -155,6 +174,9 @@ namespace Godot.Collections
internal extern static IntPtr godot_icall_Array_Ctor();
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -176,6 +198,9 @@ namespace Godot.Collections
internal extern static void godot_icall_Array_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -200,6 +225,9 @@ namespace Godot.Collections
internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static Error godot_icall_Array_Shuffle(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -231,6 +259,15 @@ namespace Godot.Collections
objectArray = new Array(collection);
}
+ public Array(params T[] array) : this()
+ {
+ if (array == null)
+ {
+ throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
+ }
+ objectArray = new Array(array);
+ }
+
public Array(Array array)
{
objectArray = array;
@@ -266,6 +303,16 @@ namespace Godot.Collections
return objectArray.Resize(newSize);
}
+ public void Shuffle()
+ {
+ objectArray.Shuffle();
+ }
+
+ public static Array<T> operator +(Array<T> left, Array<T> right)
+ {
+ return new Array<T>(left.objectArray + right.objectArray);
+ }
+
// IList<T>
public T this[int index]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 5aba31c622..3f1120575f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -207,7 +207,7 @@ namespace Godot
}
}
- internal Quat RotationQuat()
+ public Quat RotationQuat()
{
Basis orthonormalizedBasis = Orthonormalized();
real_t det = orthonormalizedBasis.Determinant();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index d851abc6d3..90141928ca 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -257,20 +257,6 @@ namespace Godot
}
/// <summary>
- /// Returns the most contrasting color.
- /// </summary>
- /// <returns>The most contrasting color</returns>
- public Color Contrasted()
- {
- return new Color(
- (r + 0.5f) % 1.0f,
- (g + 0.5f) % 1.0f,
- (b + 0.5f) % 1.0f,
- a
- );
- }
-
- /// <summary>
/// Returns a new color resulting from making this color darker
/// by the specified ratio (on the range of 0 to 1).
/// </summary>
@@ -351,8 +337,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in ABGR format
- /// (each byte represents a component of the ABGR profile).
+ /// Returns the color converted to an unsigned 32-bit integer in ABGR
+ /// format (each byte represents a color channel).
/// ABGR is the reversed version of the default format.
/// </summary>
/// <returns>A uint representing this color in ABGR32 format.</returns>
@@ -370,8 +356,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in ABGR format
- /// (each word represents a component of the ABGR profile).
+ /// Returns the color converted to an unsigned 64-bit integer in ABGR
+ /// format (each word represents a color channel).
/// ABGR is the reversed version of the default format.
/// </summary>
/// <returns>A ulong representing this color in ABGR64 format.</returns>
@@ -389,8 +375,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in ARGB format
- /// (each byte represents a component of the ARGB profile).
+ /// Returns the color converted to an unsigned 32-bit integer in ARGB
+ /// format (each byte represents a color channel).
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
/// <returns>A uint representing this color in ARGB32 format.</returns>
@@ -408,8 +394,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in ARGB format
- /// (each word represents a component of the ARGB profile).
+ /// Returns the color converted to an unsigned 64-bit integer in ARGB
+ /// format (each word represents a color channel).
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
/// <returns>A ulong representing this color in ARGB64 format.</returns>
@@ -427,8 +413,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in RGBA format
- /// (each byte represents a component of the RGBA profile).
+ /// Returns the color converted to an unsigned 32-bit integer in RGBA
+ /// format (each byte represents a color channel).
/// RGBA is Godot's default and recommended format.
/// </summary>
/// <returns>A uint representing this color in RGBA32 format.</returns>
@@ -446,8 +432,8 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in RGBA format
- /// (each word represents a component of the RGBA profile).
+ /// Returns the color converted to an unsigned 64-bit integer in RGBA
+ /// format (each word represents a color channel).
/// RGBA is Godot's default and recommended format.
/// </summary>
/// <returns>A ulong representing this color in RGBA64 format.</returns>
@@ -486,7 +472,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from RGBA values on the range of 0 to 1.
+ /// Constructs a color from RGBA values, typically on the range of 0 to 1.
/// </summary>
/// <param name="r">The color's red component, typically on the range of 0 to 1.</param>
/// <param name="g">The color's green component, typically on the range of 0 to 1.</param>
@@ -514,8 +500,8 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from a 32-bit integer
- /// (each byte represents a component of the RGBA profile).
+ /// Constructs a color from an unsigned 32-bit integer in RGBA format
+ /// (each byte represents a color channel).
/// </summary>
/// <param name="rgba">The uint representing the color.</param>
public Color(uint rgba)
@@ -530,8 +516,8 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from a 64-bit integer
- /// (each word represents a component of the RGBA profile).
+ /// Constructs a color from an unsigned 64-bit integer in RGBA format
+ /// (each word represents a color channel).
/// </summary>
/// <param name="rgba">The ulong representing the color.</param>
public Color(ulong rgba)
@@ -565,6 +551,9 @@ namespace Godot
rgba = rgba.Substring(1);
}
+ // If enabled, use 1 hex digit per channel instead of 2.
+ // Other sizes aren't in the HTML/CSS spec but we could add them if desired.
+ bool isShorthand = rgba.Length < 5;
bool alpha;
if (rgba.Length == 8)
@@ -575,47 +564,60 @@ namespace Godot
{
alpha = false;
}
+ else if (rgba.Length == 4)
+ {
+ alpha = true;
+ }
+ else if (rgba.Length == 3)
+ {
+ alpha = false;
+ }
else
{
throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
}
- if (alpha)
+ a = 1.0f;
+ if (isShorthand)
{
- a = ParseCol8(rgba, 6) / 255f;
-
- if (a < 0)
+ r = ParseCol4(rgba, 0) / 15f;
+ g = ParseCol4(rgba, 1) / 15f;
+ b = ParseCol4(rgba, 2) / 15f;
+ if (alpha)
{
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ a = ParseCol4(rgba, 3) / 15f;
}
}
else
{
- a = 1.0f;
+ r = ParseCol8(rgba, 0) / 255f;
+ g = ParseCol8(rgba, 2) / 255f;
+ b = ParseCol8(rgba, 4) / 255f;
+ if (alpha)
+ {
+ a = ParseCol8(rgba, 6) / 255f;
+ }
}
- int from = alpha ? 2 : 0;
-
- r = ParseCol8(rgba, 0) / 255f;
-
if (r < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
}
- g = ParseCol8(rgba, 2) / 255f;
-
if (g < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
}
- b = ParseCol8(rgba, 4) / 255f;
-
if (b < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
}
+
+ if (a < 0)
+ {
+ throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ }
}
/// <summary>
@@ -751,72 +753,34 @@ namespace Godot
value = max;
}
- private static int ParseCol8(string str, int ofs)
+ private static int ParseCol4(string str, int ofs)
{
- int ig = 0;
+ char character = str[ofs];
- for (int i = 0; i < 2; i++)
+ if (character >= '0' && character <= '9')
{
- int c = str[i + ofs];
- int v;
-
- if (c >= '0' && c <= '9')
- {
- v = c - '0';
- }
- else if (c >= 'a' && c <= 'f')
- {
- v = c - 'a';
- v += 10;
- }
- else if (c >= 'A' && c <= 'F')
- {
- v = c - 'A';
- v += 10;
- }
- else
- {
- return -1;
- }
-
- if (i == 0)
- {
- ig += v * 16;
- }
- else
- {
- ig += v;
- }
+ return character - '0';
}
-
- return ig;
+ else if (character >= 'a' && character <= 'f')
+ {
+ return character + (10 - 'a');
+ }
+ else if (character >= 'A' && character <= 'F')
+ {
+ return character + (10 - 'A');
+ }
+ return -1;
}
- private String ToHex32(float val)
+ private static int ParseCol8(string str, int ofs)
{
- int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
-
- var ret = string.Empty;
-
- for (int i = 0; i < 2; i++)
- {
- char c;
- int lv = v & 0xF;
-
- if (lv < 10)
- {
- c = (char)('0' + lv);
- }
- else
- {
- c = (char)('a' + lv - 10);
- }
-
- v >>= 4;
- ret = c + ret;
- }
+ return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
+ }
- return ret;
+ private string ToHex32(float val)
+ {
+ byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
+ return b.HexEncode();
}
internal static bool HtmlIsValid(string color)
@@ -828,46 +792,24 @@ namespace Godot
if (color[0] == '#')
{
- color = color.Substring(1, color.Length - 1);
+ color = color.Substring(1);
}
- bool alpha;
-
- switch (color.Length)
+ // Check if the amount of hex digits is valid.
+ int len = color.Length;
+ if (!(len == 3 || len == 4 || len == 6 || len == 8))
{
- case 8:
- alpha = true;
- break;
- case 6:
- alpha = false;
- break;
- default:
- return false;
+ return false;
}
- if (alpha)
- {
- if (ParseCol8(color, 0) < 0)
+ // Check if each hex digit is valid.
+ for (int i = 0; i < len; i++) {
+ if (ParseCol4(color, i) == -1)
{
return false;
}
}
- int from = alpha ? 2 : 0;
-
- if (ParseCol8(color, from + 0) < 0)
- {
- return false;
- }
- if (ParseCol8(color, from + 2) < 0)
- {
- return false;
- }
- if (ParseCol8(color, from + 4) < 0)
- {
- return false;
- }
-
return true;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index e050d1fdd1..6699c5992c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -39,14 +39,6 @@ namespace Godot
return val * sgn;
}
- public static FuncRef FuncRef(Object instance, StringName funcName)
- {
- var ret = new FuncRef();
- ret.SetInstance(instance);
- ret.SetFunction(funcName);
- return ret;
- }
-
public static int Hash(object var)
{
return godot_icall_GD_hash(var);
@@ -129,7 +121,12 @@ namespace Godot
public static double RandRange(double from, double to)
{
- return godot_icall_GD_rand_range(from, to);
+ return godot_icall_GD_randf_range(from, to);
+ }
+
+ public static int RandRange(int from, int to)
+ {
+ return godot_icall_GD_randi_range(from, to);
}
public static uint RandSeed(ulong seed, out ulong newSeed)
@@ -238,9 +235,11 @@ namespace Godot
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_GD_randomize();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal extern static double godot_icall_GD_randf_range(double from, double to);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static double godot_icall_GD_rand_range(double from, double to);
+ internal extern static int godot_icall_GD_randi_range(int from, int to);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 42610c5ef7..48582d5ad8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -15,14 +15,13 @@ namespace Godot
public Object() : this(false)
{
if (ptr == IntPtr.Zero)
- {
ptr = godot_icall_Object_Ctor(this);
- }
- else
- {
- // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
- godot_icall_Object_ConnectEventSignals(ptr);
- }
+ _InitializeGodotScriptInstanceInternals();
+ }
+
+ internal void _InitializeGodotScriptInstanceInternals()
+ {
+ godot_icall_Object_ConnectEventSignals(ptr);
}
internal Object(bool memoryOwn)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 41b4e9367f..0700f197ff 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -322,6 +322,15 @@ namespace Godot
return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
+ /// <summary>Find the first occurrence of a char. Optionally, the search starting position can be passed.</summary>
+ /// <returns>The first instance of the char, or -1 if not found.</returns>
+ public static int Find(this string instance, char what, int from = 0, bool caseSensitive = true)
+ {
+ // TODO: Could be more efficient if we get a char version of `IndexOf`.
+ // See https://github.com/dotnet/runtime/issues/44116
+ return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
+
/// <summary>Find the last occurrence of a substring.</summary>
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindLast(this string instance, string what, bool caseSensitive = true)
@@ -393,6 +402,35 @@ namespace Godot
return instance.Substring(sep + 1);
}
+ /// <summary>
+ /// Converts the given byte array of ASCII encoded text to a string.
+ /// Faster alternative to <see cref="GetStringFromUTF8"/> if the
+ /// content is ASCII-only. Unlike the UTF-8 function this function
+ /// maps every byte to a character in the array. Multibyte sequences
+ /// will not be interpreted correctly. For parsing user input always
+ /// use <see cref="GetStringFromUTF8"/>.
+ /// </summary>
+ /// <param name="bytes">A byte array of ASCII characters (on the range of 0-127).</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromASCII(this byte[] bytes)
+ {
+ return Encoding.ASCII.GetString(bytes);
+ }
+
+ /// <summary>
+ /// Converts the given byte array of UTF-8 encoded text to a string.
+ /// Slower than <see cref="GetStringFromASCII"/> but supports UTF-8
+ /// encoded data. Use this function if you are unsure about the
+ /// source of the data. For user input this function
+ /// should always be preferred.
+ /// </summary>
+ /// <param name="bytes">A byte array of UTF-8 characters (a character may take up multiple bytes).</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromUTF8(this byte[] bytes)
+ {
+ return Encoding.UTF8.GetString(bytes);
+ }
+
// <summary>
// Hash the string and return a 32 bits integer.
// </summary>
@@ -408,6 +446,53 @@ namespace Godot
return hashv;
}
+ /// <summary>
+ /// Returns a hexadecimal representation of this byte as a string.
+ /// </summary>
+ /// <param name="bytes">The byte to encode.</param>
+ /// <returns>The hexadecimal representation of this byte.</returns>
+ internal static string HexEncode(this byte b)
+ {
+ var ret = string.Empty;
+
+ for (int i = 0; i < 2; i++)
+ {
+ char c;
+ int lv = b & 0xF;
+
+ if (lv < 10)
+ {
+ c = (char)('0' + lv);
+ }
+ else
+ {
+ c = (char)('a' + lv - 10);
+ }
+
+ b >>= 4;
+ ret = c + ret;
+ }
+
+ return ret;
+ }
+
+ /// <summary>
+ /// Returns a hexadecimal representation of this byte array as a string.
+ /// </summary>
+ /// <param name="bytes">The byte array to encode.</param>
+ /// <returns>The hexadecimal representation of this byte array.</returns>
+ public static string HexEncode(this byte[] bytes)
+ {
+ var ret = string.Empty;
+
+ foreach (byte b in bytes)
+ {
+ ret += b.HexEncode();
+ }
+
+ return ret;
+ }
+
// <summary>
// Convert a string containing an hexadecimal number into an int.
// </summary>
@@ -440,7 +525,12 @@ namespace Godot
// </summary>
public static bool IsAbsPath(this string instance)
{
- return System.IO.Path.IsPathRooted(instance);
+ if (string.IsNullOrEmpty(instance))
+ return false;
+ else if (instance.Length > 1)
+ return instance[0] == '/' || instance[0] == '\\' || instance.Contains(":/") || instance.Contains(":\\");
+ else
+ return instance[0] == '/' || instance[0] == '\\';
}
// <summary>
@@ -448,7 +538,7 @@ namespace Godot
// </summary>
public static bool IsRelPath(this string instance)
{
- return !System.IO.Path.IsPathRooted(instance);
+ return !IsAbsPath(instance);
}
// <summary>
@@ -624,41 +714,73 @@ namespace Godot
return instance.Length;
}
- // <summary>
- // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
- // </summary>
- public static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ /// <summary>
+ /// Returns a copy of the string with characters removed from the left.
+ /// </summary>
+ /// <param name="instance">The string to remove characters from.</param>
+ /// <param name="chars">The characters to be removed.</param>
+ /// <returns>A copy of the string with characters removed from the left.</returns>
+ public static string LStrip(this string instance, string chars)
{
- if (expr.Length == 0 || instance.Length == 0)
- return false;
+ int len = instance.Length;
+ int beg;
+
+ for (beg = 0; beg < len; beg++)
+ {
+ if (chars.Find(instance[beg]) == -1)
+ {
+ break;
+ }
+ }
+
+ if (beg == 0)
+ {
+ return instance;
+ }
+
+ return instance.Substr(beg, len - beg);
+ }
+
+ /// <summary>
+ /// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
+ /// </summary>
+ private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ {
+ // case '\0':
+ if (expr.Length == 0)
+ return instance.Length == 0;
switch (expr[0])
{
- case '\0':
- return instance[0] == 0;
case '*':
- return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive);
+ return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive));
case '?':
- return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive);
+ return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
- return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
- ExprMatch(expr + 1, instance + 1, caseSensitive);
+ if (instance.Length == 0) return false;
+ return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
- // <summary>
- // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
- // </summary>
+ /// <summary>
+ /// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// </summary>
public static bool Match(this string instance, string expr, bool caseSensitive = true)
{
+ if (instance.Length == 0 || expr.Length == 0)
+ return false;
+
return instance.ExprMatch(expr, caseSensitive);
}
- // <summary>
- // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
- // </summary>
+ /// <summary>
+ /// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// </summary>
public static bool MatchN(this string instance, string expr)
{
+ if (instance.Length == 0 || expr.Length == 0)
+ return false;
+
return instance.ExprMatch(expr, caseSensitive: false);
}
@@ -847,6 +969,33 @@ namespace Godot
return instance.Substring(pos, instance.Length - pos);
}
+ /// <summary>
+ /// Returns a copy of the string with characters removed from the right.
+ /// </summary>
+ /// <param name="instance">The string to remove characters from.</param>
+ /// <param name="chars">The characters to be removed.</param>
+ /// <returns>A copy of the string with characters removed from the right.</returns>
+ public static string RStrip(this string instance, string chars)
+ {
+ int len = instance.Length;
+ int end;
+
+ for (end = len - 1; end >= 0; end--)
+ {
+ if (chars.Find(instance[end]) == -1)
+ {
+ break;
+ }
+ }
+
+ if (end == len - 1)
+ {
+ return instance;
+ }
+
+ return instance.Substr(0, end + 1);
+ }
+
public static byte[] SHA256Buffer(this string instance)
{
return godot_icall_String_sha256_buffer(instance);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 06bbe98497..bc0f81b2a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -221,8 +221,7 @@ namespace Godot
real_t dot = v1.Dot(v2);
- // Clamp dot to [-1, 1]
- dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot);
+ dot = Mathf.Clamp(dot, -1.0f, 1.0f);
Vector2 v;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 26bd828a5b..d536b14eac 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -437,8 +437,11 @@ namespace Godot
/// <returns>The rotated vector.</returns>
public Vector2 Rotated(real_t phi)
{
- real_t rads = Angle() + phi;
- return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length();
+ real_t sine = Mathf.Sin(phi);
+ real_t cosi = Mathf.Cos(phi);
+ return new Vector2(
+ x * cosi - y * sine,
+ x * sine + y * cosi);
}
/// <summary>
@@ -670,41 +673,37 @@ namespace Godot
public static bool operator <(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y > right.y;
}
-
return left.x > right.x;
}
public static bool operator <=(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y <= right.y;
}
-
return left.x <= right.x;
}
public static bool operator >=(Vector2 left, Vector2 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
return left.y >= right.y;
}
-
return left.x >= right.x;
}
@@ -714,7 +713,6 @@ namespace Godot
{
return Equals((Vector2)obj);
}
-
return false;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index d9b16a6afd..4a4a2a43cd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -713,49 +713,53 @@ namespace Godot
public static bool operator <(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z < right.z;
+ }
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z > right.z;
+ }
return left.y > right.y;
}
-
return left.x > right.x;
}
public static bool operator <=(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z <= right.z;
+ }
return left.y < right.y;
}
-
return left.x < right.x;
}
public static bool operator >=(Vector3 left, Vector3 right)
{
- if (Mathf.IsEqualApprox(left.x, right.x))
+ if (left.x == right.x)
{
- if (Mathf.IsEqualApprox(left.y, right.y))
+ if (left.y == right.y)
+ {
return left.z >= right.z;
+ }
return left.y > right.y;
}
-
return left.x > right.x;
}
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index ebcd6d5e9c..544f414cba 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -30,10 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/class_db.h"
-#include "core/object.h"
-#include "core/reference.h"
-#include "core/string_name.h"
+#include "core/object/class_db.h"
+#include "core/object/reference.h"
+#include "core/string/string_name.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 766b00d612..bb3ea0f730 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -32,7 +32,7 @@
#include <mono/metadata/exception.h>
-#include "core/array.h"
+#include "core/variant/array.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
@@ -104,10 +104,31 @@ void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
}
}
+Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) {
+ Array *godot_array = memnew(Array);
+ unsigned int count = mono_array_length(mono_array);
+ godot_array->resize(count);
+ for (unsigned int i = 0; i < count; i++) {
+ MonoObject *item = mono_array_get(mono_array, MonoObject *, i);
+ godot_icall_Array_SetAt(godot_array, i, item);
+ }
+ return godot_array;
+}
+
Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
return memnew(Array(ptr->duplicate(deep)));
}
+Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
+ int count = left->size() + right->size();
+ Array *new_array = memnew(Array(left->duplicate(false)));
+ new_array->resize(count);
+ for (unsigned int i = 0; i < (unsigned int)right->size(); i++) {
+ new_array->operator[](i + left->size()) = right->operator[](i);
+ }
+ return new_array;
+}
+
int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
@@ -141,6 +162,10 @@ Error godot_icall_Array_Resize(Array *ptr, int new_size) {
return ptr->resize(new_size);
}
+void godot_icall_Array_Shuffle(Array *ptr) {
+ ptr->shuffle();
+}
+
void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
@@ -284,6 +309,7 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic);
@@ -291,6 +317,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
@@ -299,6 +326,7 @@ void godot_register_collections_icalls() {
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize);
+ mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", (void *)godot_icall_Array_Shuffle);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo);
mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString);
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 5e892b616b..58d8dceb25 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -30,12 +30,12 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/array.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/variant_parser.h"
+#include "core/string/ustring.h"
+#include "core/variant/array.h"
+#include "core/variant/variant.h"
+#include "core/variant/variant_parser.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -55,7 +55,8 @@ MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
const Variant *args[1] = { &what };
Callable::CallError ce;
- Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce);
+ Variant ret;
+ Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
return GDMonoMarshal::variant_to_mono_object(ret);
}
@@ -193,7 +194,11 @@ void godot_icall_GD_randomize() {
Math::randomize();
}
-double godot_icall_GD_rand_range(double from, double to) {
+double godot_icall_GD_randf_range(double from, double to) {
+ return Math::random(from, to);
+}
+
+int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
return Math::random(from, to);
}
@@ -298,7 +303,8 @@ void godot_register_gd_icalls() {
mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf);
mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi);
mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize);
- mono_add_internal_call("Godot.GD::godot_icall_GD_rand_range", (void *)godot_icall_GD_rand_range);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_randf_range", (void *)godot_icall_GD_randf_range);
+ mono_add_internal_call("Godot.GD::godot_icall_GD_randi_range", (void *)godot_icall_GD_randi_range);
mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed);
mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed);
mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index c1f1936711..f4263e286e 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -58,16 +58,15 @@ void godot_register_glue_header_icalls() {
// Used by the generated glue
-#include "core/array.h"
-#include "core/class_db.h"
-#include "core/dictionary.h"
-#include "core/engine.h"
-#include "core/method_bind.h"
-#include "core/node_path.h"
-#include "core/object.h"
-#include "core/reference.h"
+#include "core/config/engine.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/reference.h"
+#include "core/string/node_path.h"
+#include "core/string/ustring.h"
#include "core/typedefs.h"
-#include "core/ustring.h"
+#include "core/variant/array.h"
+#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index 2aa75dd309..09c6d8f482 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -30,8 +30,8 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/node_path.h"
-#include "core/ustring.h"
+#include "core/string/node_path.h"
+#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp
index 6d2e6b559f..cb4f26511f 100644
--- a/modules/mono/glue/rid_glue.cpp
+++ b/modules/mono/glue/rid_glue.cpp
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/object.h"
-#include "core/resource.h"
-#include "core/rid.h"
+#include "core/io/resource.h"
+#include "core/object/class_db.h"
+#include "core/templates/rid.h"
#include "../mono_gd/gd_mono_marshal.h"
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
index b43daccc1b..53d6c1436d 100644
--- a/modules/mono/glue/scene_tree_glue.cpp
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/array.h"
-#include "core/class_db.h"
-#include "core/string_name.h"
+#include "core/object/class_db.h"
+#include "core/string/string_name.h"
+#include "core/variant/array.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp
index 595b8d71f1..9271731573 100644
--- a/modules/mono/glue/string_glue.cpp
+++ b/modules/mono/glue/string_glue.cpp
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "../mono_gd/gd_mono_marshal.h"
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
index 4b2e88569b..9323e3bbb3 100644
--- a/modules/mono/glue/string_name_glue.cpp
+++ b/modules/mono/glue/string_name_glue.cpp
@@ -30,8 +30,8 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/string_name.h"
-#include "core/ustring.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 692da991c7..4233732bff 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -30,9 +30,9 @@
#include "godotsharp_dirs.h"
+#include "core/config/project_settings.h"
#include "core/os/dir_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "core/version.h"
@@ -122,7 +122,7 @@ public:
private:
_GodotSharpDirs() {
- res_data_dir = "res://.mono";
+ res_data_dir = "res://.godot/mono";
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index 2ab4b0e309..6391616419 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -31,7 +31,7 @@
#ifndef GODOTSHARP_DIRS_H
#define GODOTSHARP_DIRS_H
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace GodotSharpDirs {
diff --git a/modules/mono/icons/CSharpScript.svg b/modules/mono/icons/CSharpScript.svg
index 69664ca553..0b2cc840f8 100644
--- a/modules/mono/icons/CSharpScript.svg
+++ b/modules/mono/icons/CSharpScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-0.55228 0-1-0.4478-1-1 0-0.5523 0.44772-1 1-1h1v-2zm1-9-0.56445 2.2578c-0.23643 0.076-0.46689 0.1692-0.68945 0.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-0.11191 0.2211-0.20723 0.4502-0.28516 0.6855l-2.2539 0.5625v2h5.2715c-0.17677-0.3037-0.27041-0.6486-0.27148-1 9.6e-6 -1.1046 0.89543-2 2-2s2 0.8954 2 2c-4.817e-4 0.3512-0.093442 0.6961-0.26953 1h5.2695v-2l-2.2578-0.5645c-0.07594-0.2357-0.1693-0.4655-0.2793-0.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-0.22113-0.1119-0.45028-0.2073-0.68555-0.2852l-0.5625-2.2539zm4 9c-0.71466-1e-4 -1.3751 0.3811-1.7324 1-0.35727 0.6188-0.35727 1.3812 0 2 0.35733 0.6189 1.0178 1.0001 1.7324 1h-2v2h2c0.71466 1e-4 1.3751-0.3811 1.7324-1 0.35727-0.6188 0.35727-1.3812 0-2-0.35733-0.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-.55228 0-1-.4478-1-1 0-.5523.44772-1 1-1h1v-2zm1-9-.56445 2.2578c-.23643.076-.46689.1692-.68945.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-.11191.2211-.20723.4502-.28516.6855l-2.2539.5625v2h5.2715c-.17677-.3037-.27041-.6486-.27148-1 .0000096-1.1046.89543-2 2-2s2 .8954 2 2c-.0004817.3512-.093442.6961-.26953 1h5.2695v-2l-2.2578-.5645c-.07594-.2357-.1693-.4655-.2793-.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-.22113-.1119-.45028-.2073-.68555-.2852l-.5625-2.2539zm4 9c-.71466-.0001-1.3751.3811-1.7324 1-.35727.6188-.35727 1.3812 0 2 .35733.6189 1.0178 1.0001 1.7324 1h-2v2h2c.71466.0001 1.3751-.3811 1.7324-1 .35727-.6188.35727-1.3812 0-2-.35733-.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 -1036.4)"/></svg>
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 4f71e14a2f..bde1b41781 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -33,9 +33,9 @@
#include <mono/metadata/object.h>
-#include "core/callable.h"
#include "core/os/mutex.h"
-#include "core/self_list.h"
+#include "core/templates/self_list.h"
+#include "core/variant/callable.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono_method.h"
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index 13cfad4654..5c3a210e97 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -33,7 +33,7 @@
#include <mono/jit/jit.h>
-#include "core/reference.h"
+#include "core/object/reference.h"
namespace gdmono {
diff --git a/modules/mono/mono_gd/android_mono_config.h b/modules/mono/mono_gd/android_mono_config.h
index 93f708bba0..9e304939b2 100644
--- a/modules/mono/mono_gd/android_mono_config.h
+++ b/modules/mono/mono_gd/android_mono_config.h
@@ -33,7 +33,7 @@
#ifdef ANDROID_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
// This function is defined in an auto-generated source file
String get_godot_android_mono_config();
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index cf5ac33d20..0e335b3349 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -37,12 +37,12 @@
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/profiler.h>
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/project_settings.h"
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 9dbeee57ce..33628b3ce3 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -33,11 +33,11 @@
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
+#include "core/config/project_settings.h"
#include "core/io/file_access_pack.h"
-#include "core/list.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
+#include "core/templates/list.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_cache.h"
@@ -107,7 +107,7 @@ void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]]
GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
#ifdef GD_MONO_HOT_RELOAD
- const char *path = mono_image_get_filename(image);
+ String path = String::utf8(mono_image_get_filename(image));
if (FileAccess::exists(path)) {
gdassembly->modified_time = FileAccess::get_modified_time(path);
}
@@ -464,7 +464,9 @@ GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_a
if (!assembly) {
assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
- ERR_FAIL_NULL_V(assembly, nullptr);
+ if (!assembly) {
+ return nullptr;
+ }
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
@@ -487,7 +489,9 @@ GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_
if (!assembly) {
assembly = _real_load_assembly_from(p_path, p_refonly);
- ERR_FAIL_NULL_V(assembly, nullptr);
+ if (!assembly) {
+ return nullptr;
+ }
}
GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 63899dc9be..fc10480e07 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -34,9 +34,9 @@
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
-#include "core/hash_map.h"
-#include "core/map.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/map.h"
#include "gd_mono_utils.h"
class GDMonoAssembly {
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 44b146b87c..87db2fa033 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -31,8 +31,8 @@
#ifndef GD_MONO_CLASS_H
#define GD_MONO_CLASS_H
-#include "core/map.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
+#include "core/templates/map.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 563c45e71f..00a1e1e507 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -355,7 +355,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
}
if (CACHED_CLASS(RID) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID());
mono_field_set_value(p_object, mono_field, managed);
break;
}
@@ -450,8 +450,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::_RID: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
+ case Variant::RID: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID());
mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::OBJECT: {
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 0f4f888546..ffb56e7cec 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -31,7 +31,7 @@
#ifndef GD_MONO_HEADER_H
#define GD_MONO_HEADER_H
-#include "core/int_types.h"
+#include <cstdint>
#ifdef WIN32
#define GD_MONO_STDCALL __stdcall
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index fe1c2d28dd..0ed9e441ef 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -109,8 +109,6 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script, csharp_instance);
-
- csharp_instance->connect_event_signals();
}
void unhandled_exception(MonoException *p_exc) {
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index 038d17f782..d1d5eca263 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -35,7 +35,7 @@
#include "../utils/macros.h"
-#include "core/object.h"
+#include "core/object/class_db.h"
namespace GDMonoInternals {
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index c5a988b8c3..b8ee0204c4 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -64,25 +64,32 @@ static int get_log_level_id(const char *p_log_level) {
return -1;
}
+static String make_text(const char *log_domain, const char *log_level, const char *message) {
+ String text(message);
+ text += " (in domain ";
+ text += log_domain;
+ if (log_level) {
+ text += ", ";
+ text += log_level;
+ }
+ text += ")";
+ return text;
+}
+
void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) {
FileAccess *f = GDMonoLog::get_singleton()->log_file;
if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) {
- String text(message);
- text += " (in domain ";
- text += log_domain;
- if (log_level) {
- text += ", ";
- text += log_level;
- }
- text += ")\n";
+ String text = make_text(log_domain, log_level, message);
+ text += "\n";
f->seek_end();
f->store_string(text);
}
if (fatal) {
- ERR_PRINT("Mono: FATAL ERROR, ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
+ String text = make_text(log_domain, log_level, message);
+ ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
// Make sure to flush before aborting
f->flush();
f->close();
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 6d7d5f76cd..9d193ab8ab 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -204,7 +204,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
}
if (CACHED_CLASS(RID) == type_class) {
- return Variant::_RID;
+ return Variant::RID;
}
if (CACHED_CLASS(Dictionary) == type_class) {
@@ -311,44 +311,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
return false;
}
-String mono_to_utf8_string(MonoString *p_mono_string) {
- MonoError error;
- char *utf8 = mono_string_to_utf8_checked(p_mono_string, &error);
-
- if (!mono_error_ok(&error)) {
- ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&error) + "'.");
- mono_error_cleanup(&error);
- return String();
- }
-
- String ret = String::utf8(utf8);
-
- mono_free(utf8);
-
- return ret;
-}
-
-String mono_to_utf16_string(MonoString *p_mono_string) {
- int len = mono_string_length(p_mono_string);
- String ret;
-
- if (len == 0) {
- return ret;
- }
-
- ret.resize(len + 1);
- ret.set(len, 0);
-
- CharType *src = (CharType *)mono_string_chars(p_mono_string);
- CharType *dst = ret.ptrw();
-
- for (int i = 0; i < len; i++) {
- dst[i] = src[i];
- }
-
- return ret;
-}
-
MonoObject *variant_to_mono_object(const Variant *p_var) {
ManagedType type;
@@ -618,7 +580,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
}
if (CACHED_CLASS(RID) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator RID());
+ return GDMonoUtils::create_managed_from(p_var->operator ::RID());
}
// Godot.Collections.Dictionary or IDictionary
@@ -711,8 +673,8 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
return GDMonoUtils::create_managed_from(p_var->operator StringName());
case Variant::NODE_PATH:
return GDMonoUtils::create_managed_from(p_var->operator NodePath());
- case Variant::_RID:
- return GDMonoUtils::create_managed_from(p_var->operator RID());
+ case Variant::RID:
+ return GDMonoUtils::create_managed_from(p_var->operator ::RID());
case Variant::OBJECT:
return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
case Variant::CALLABLE: {
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 4ff330fd43..d2c564d67d 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -31,7 +31,7 @@
#ifndef GDMONOMARSHAL_H
#define GDMONOMARSHAL_H
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "../managed_callable.h"
#include "gd_mono.h"
@@ -69,15 +69,11 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
// String
-String mono_to_utf8_string(MonoString *p_mono_string);
-String mono_to_utf16_string(MonoString *p_mono_string);
-
_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
- if constexpr (sizeof(CharType) == 2) {
- return mono_to_utf16_string(p_mono_string);
- }
-
- return mono_to_utf8_string(p_mono_string);
+ char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string);
+ String ret = String(utf32);
+ mono_free(utf32);
+ return ret;
}
_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
@@ -88,20 +84,8 @@ _FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
return mono_string_to_godot_not_null(p_mono_string);
}
-_FORCE_INLINE_ MonoString *mono_from_utf8_string(const String &p_string) {
- return mono_string_new(mono_domain_get(), p_string.utf8().get_data());
-}
-
-_FORCE_INLINE_ MonoString *mono_from_utf16_string(const String &p_string) {
- return mono_string_from_utf16((mono_unichar2 *)p_string.c_str());
-}
-
_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
- if constexpr (sizeof(CharType) == 2) {
- return mono_from_utf16_string(p_string);
- }
-
- return mono_from_utf8_string(p_string);
+ return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data()));
}
// Variant
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 3f1155f430..2676165cbc 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -35,10 +35,10 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/object/reference.h"
#include "core/os/dir_access.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
-#include "core/reference.h"
#ifdef TOOLS_ENABLED
#include "editor/debugger/editor_debugger_node.h"
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 9db4a5f3f0..7088385c4f 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -37,14 +37,15 @@
#include "../utils/macros.h"
#include "gd_mono_header.h"
-#include "core/object.h"
-#include "core/reference.h"
+#include "core/object/class_db.h"
+#include "core/object/reference.h"
#define UNHANDLED_EXCEPTION(m_exc) \
if (unlikely(m_exc != nullptr)) { \
GDMonoUtils::debug_unhandled_exception(m_exc); \
GD_UNREACHABLE(); \
- }
+ } else \
+ ((void)0)
namespace GDMonoUtils {
@@ -162,20 +163,24 @@ StringName get_native_godot_class_name(GDMonoClass *p_class);
#define GD_MONO_BEGIN_RUNTIME_INVOKE \
int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
- _runtime_invoke_count_ref += 1;
+ _runtime_invoke_count_ref += 1; \
+ ((void)0)
-#define GD_MONO_END_RUNTIME_INVOKE \
- _runtime_invoke_count_ref -= 1;
+#define GD_MONO_END_RUNTIME_INVOKE \
+ _runtime_invoke_count_ref -= 1; \
+ ((void)0)
#define GD_MONO_SCOPE_THREAD_ATTACH \
GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \
- (void)__gdmono__scope__thread__attach__;
+ (void)__gdmono__scope__thread__attach__; \
+ ((void)0)
#ifdef DEBUG_ENABLED
-#define GD_MONO_ASSERT_THREAD_ATTACHED \
- { CRASH_COND(!GDMonoUtils::is_thread_attached()); }
+#define GD_MONO_ASSERT_THREAD_ATTACHED \
+ CRASH_COND(!GDMonoUtils::is_thread_attached()); \
+ ((void)0)
#else
-#define GD_MONO_ASSERT_THREAD_ATTACHED
+#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0)
#endif
#endif // GD_MONOUTILS_H
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 8bcdeec9dd..386e0576b3 100644
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -44,7 +44,7 @@
#endif
#include "core/os/os.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#include "platform/android/java_godot_wrapper.h"
#include "platform/android/os_android.h"
#include "platform/android/thread_jandroid.h"
diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/mono/mono_gd/support/android_support.h
index dc2e6c95ed..5947395a99 100755
--- a/modules/mono/mono_gd/support/android_support.h
+++ b/modules/mono/mono_gd/support/android_support.h
@@ -33,7 +33,7 @@
#if defined(ANDROID_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace gdmono {
namespace android {
diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h
index e28af120e3..ed251cb23a 100755
--- a/modules/mono/mono_gd/support/ios_support.h
+++ b/modules/mono/mono_gd/support/ios_support.h
@@ -33,7 +33,7 @@
#if defined(IPHONE_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace gdmono {
namespace ios {
diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm
index e3d1a647fd..dc23c06eba 100644
--- a/modules/mono/mono_gd/support/ios_support.mm
+++ b/modules/mono/mono_gd/support/ios_support.mm
@@ -131,8 +131,7 @@ GD_PINVOKE_EXPORT void *xamarin_timezone_get_data(const char *p_name, uint32_t *
NSTimeZone *tz = nil;
if (p_name) {
NSString *n = [[NSString alloc] initWithUTF8String:p_name];
- tz = [[[NSTimeZone alloc] initWithName:n] autorelease];
- [n release];
+ tz = [[NSTimeZone alloc] initWithName:n];
} else {
tz = [NSTimeZone localTimeZone];
}
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 98c3ba1324..f5c1bda18b 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "csharp_script.h"
diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h
index 7fd0d24eb0..e30d9a8abd 100644
--- a/modules/mono/register_types.h
+++ b/modules/mono/register_types.h
@@ -28,5 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef MONO_REGISTER_TYPES_H
+#define MONO_REGISTER_TYPES_H
+
void register_mono_types();
void unregister_mono_types();
+
+#endif // MONO_REGISTER_TYPES_H
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index c550315a23..18d1e43e14 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -31,7 +31,7 @@
#ifndef SIGNAL_AWAITER_UTILS_H
#define SIGNAL_AWAITER_UTILS_H
-#include "core/reference.h"
+#include "core/object/reference.h"
#include "csharp_script.h"
#include "mono_gc_handle.h"
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index dc542477f5..c76619cca4 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -46,7 +46,7 @@
#define GD_UNREACHABLE() \
CRASH_NOW(); \
do { \
- } while (true);
+ } while (true)
#endif
namespace gdmono {
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index e0cf916a01..a619f0b975 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -71,12 +71,12 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
buffer.resize(512);
DWORD dwBufferSize = buffer.size();
- LONG res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
if (res == ERROR_MORE_DATA) {
// dwBufferSize now contains the actual size
buffer.resize(dwBufferSize);
- res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
+ res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
}
if (res == ERROR_SUCCESS) {
@@ -90,7 +90,7 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)
LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) {
HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey);
+ LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
@@ -127,7 +127,7 @@ LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) {
String default_clr;
HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, p_subkey.c_str(), &hKey);
+ LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
if (res != ERROR_SUCCESS)
goto cleanup;
diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h
index 4ef876f2b6..cc3f1cb035 100644
--- a/modules/mono/utils/mono_reg_utils.h
+++ b/modules/mono/utils/mono_reg_utils.h
@@ -33,7 +33,7 @@
#ifdef WINDOWS_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
struct MonoRegInfo {
String version;
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index e68466b1cf..41be198bcf 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -32,7 +32,7 @@
#ifdef OSX_ENABLED
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/osx_utils.h
index 55002702f8..92faead0fb 100644
--- a/modules/mono/utils/osx_utils.h
+++ b/modules/mono/utils/osx_utils.h
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#ifndef OSX_UTILS_H
#define OSX_UTILS_H
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index ccfaf5aba7..eb0ba8c700 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -30,10 +30,10 @@
#include "path_utils.h"
+#include "core/config/project_settings.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef WINDOWS_ENABLED
#include <windows.h>
@@ -54,12 +54,16 @@ String cwd() {
#ifdef WINDOWS_ENABLED
const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr);
- String buffer;
+ Char16String buffer;
buffer.resize((int)expected_size);
- if (::GetCurrentDirectoryW(expected_size, buffer.ptrw()) == 0)
+ if (::GetCurrentDirectoryW(expected_size, (wchar_t *)buffer.ptrw()) == 0)
return ".";
- return buffer.simplify_path();
+ String result;
+ if (result.parse_utf16(buffer.ptr())) {
+ return ".";
+ }
+ return result.simplify_path();
#else
char buffer[PATH_MAX];
if (::getcwd(buffer, sizeof(buffer)) == nullptr) {
@@ -86,7 +90,7 @@ String abspath(const String &p_path) {
String realpath(const String &p_path) {
#ifdef WINDOWS_ENABLED
// Open file without read/write access
- HANDLE hFile = ::CreateFileW(p_path.c_str(), 0,
+ HANDLE hFile = ::CreateFileW((LPCWSTR)(p_path.utf16().get_data()), 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
@@ -100,12 +104,18 @@ String realpath(const String &p_path) {
return p_path;
}
- String buffer;
+ Char16String buffer;
buffer.resize((int)expected_size);
- ::GetFinalPathNameByHandleW(hFile, buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED);
+ ::GetFinalPathNameByHandleW(hFile, (wchar_t *)buffer.ptrw(), expected_size, FILE_NAME_NORMALIZED);
::CloseHandle(hFile);
- return buffer.simplify_path();
+
+ String result;
+ if (result.parse_utf16(buffer.ptr())) {
+ return p_path;
+ }
+
+ return result.simplify_path();
#elif UNIX_ENABLED
char *resolved_path = ::realpath(p_path.utf8().get_data(), nullptr);
@@ -130,7 +140,7 @@ String join(const String &p_a, const String &p_b) {
return p_b;
}
- const CharType a_last = p_a[p_a.length() - 1];
+ const char32_t a_last = p_a[p_a.length() - 1];
if ((a_last == '/' || a_last == '\\') ||
(p_b.size() > 0 && (p_b[0] == '/' || p_b[0] == '\\'))) {
return p_a + p_b;
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index bcd8af8bb9..458d1bb849 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -31,8 +31,8 @@
#ifndef PATH_UTILS_H
#define PATH_UTILS_H
-#include "core/string_builder.h"
-#include "core/ustring.h"
+#include "core/string/string_builder.h"
+#include "core/string/ustring.h"
namespace path {
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index f8d9804de4..65da4328f6 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -49,7 +49,7 @@ int sfind(const String &p_text, int p_from) {
return -1;
}
- const CharType *src = p_text.c_str();
+ const char32_t *src = p_text.get_data();
for (int i = p_from; i <= (len - src_len); i++) {
bool found = true;
@@ -64,7 +64,7 @@ int sfind(const String &p_text, int p_from) {
found = src[read_pos] == '%';
break;
case 1: {
- CharType c = src[read_pos];
+ char32_t c = src[read_pos];
found = src[read_pos] == 's' || (c >= '0' && c <= '4');
break;
}
@@ -121,7 +121,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
int result = 0;
while ((result = sfind(p_text, search_from)) >= 0) {
- CharType c = p_text[result + 1];
+ char32_t c = p_text[result + 1];
int req_index = (c == 's' ? findex++ : c - '0');
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 0318fec592..99f3548f31 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -31,8 +31,8 @@
#ifndef STRING_FORMAT_H
#define STRING_FORMAT_H
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/ustring.h"
+#include "core/variant/variant.h"
#include <stdarg.h>
diff --git a/modules/opensimplex/icons/NoiseTexture.svg b/modules/opensimplex/icons/NoiseTexture.svg
index 5908c2b2d4..479684cde2 100644
--- a/modules/opensimplex/icons/NoiseTexture.svg
+++ b/modules/opensimplex/icons/NoiseTexture.svg
@@ -1,3 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<path d="m2 1c-0.55228 0-1 0.44772-1 1v12c0 0.55228 0.44772 1 1 1h12c0.55228 0 1-0.44772 1-1v-12c0-0.55228-0.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/></svg>
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index 7357e54e35..73960ba85f 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -33,8 +33,8 @@
#include "open_simplex_noise.h"
-#include "core/image.h"
-#include "core/reference.h"
+#include "core/io/image.h"
+#include "core/object/reference.h"
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "editor/property_editor.h"
diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h
index dce62bc1f9..835f8ed35e 100644
--- a/modules/opensimplex/open_simplex_noise.h
+++ b/modules/opensimplex/open_simplex_noise.h
@@ -31,8 +31,8 @@
#ifndef OPEN_SIMPLEX_NOISE_H
#define OPEN_SIMPLEX_NOISE_H
-#include "core/image.h"
-#include "core/reference.h"
+#include "core/io/image.h"
+#include "core/object/reference.h"
#include "scene/resources/texture.h"
#include "thirdparty/misc/open-simplex-noise.h"
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 36c0913f62..050dce1aab 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -213,10 +213,12 @@ static void _compress_pvrtc4(Image *p_img) {
int ofs, size, w, h;
img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
Javelin::RgbaBitmap bm(w, h);
+ void *dst = (void *)bm.GetData();
+ copymem(dst, &r[ofs], size);
+ Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
for (int j = 0; j < size / 4; j++) {
- Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
- /* red and Green colors are swapped. */
- new (dp) Javelin::ColorRgba<unsigned char>(r[ofs + 4 * j + 2], r[ofs + 4 * j + 1], r[ofs + 4 * j], r[ofs + 4 * j + 3]);
+ /* red and blue colors are swapped. */
+ SWAP(dp[j].r, dp[j].b);
}
new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index 50ca01067b..c10a276eae 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -156,26 +156,13 @@ void RegExMatch::_bind_methods() {
}
void RegEx::_pattern_info(uint32_t what, void *where) const {
- if (sizeof(CharType) == 2) {
- pcre2_pattern_info_16((pcre2_code_16 *)code, what, where);
-
- } else {
- pcre2_pattern_info_32((pcre2_code_32 *)code, what, where);
- }
+ pcre2_pattern_info_32((pcre2_code_32 *)code, what, where);
}
void RegEx::clear() {
- if (sizeof(CharType) == 2) {
- if (code) {
- pcre2_code_free_16((pcre2_code_16 *)code);
- code = nullptr;
- }
-
- } else {
- if (code) {
- pcre2_code_free_32((pcre2_code_32 *)code);
- code = nullptr;
- }
+ if (code) {
+ pcre2_code_free_32((pcre2_code_32 *)code);
+ code = nullptr;
}
}
@@ -187,39 +174,20 @@ Error RegEx::compile(const String &p_pattern) {
PCRE2_SIZE offset;
uint32_t flags = PCRE2_DUPNAMES;
- if (sizeof(CharType) == 2) {
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_compile_context_16 *cctx = pcre2_compile_context_create_16(gctx);
- PCRE2_SPTR16 p = (PCRE2_SPTR16)pattern.c_str();
-
- code = pcre2_compile_16(p, pattern.length(), flags, &err, &offset, cctx);
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx);
+ PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.get_data();
- pcre2_compile_context_free_16(cctx);
+ code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx);
- if (!code) {
- PCRE2_UCHAR16 buf[256];
- pcre2_get_error_message_16(err, buf, 256);
- String message = String::num(offset) + ": " + String((const CharType *)buf);
- ERR_PRINT(message.utf8());
- return FAILED;
- }
+ pcre2_compile_context_free_32(cctx);
- } else {
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_compile_context_32 *cctx = pcre2_compile_context_create_32(gctx);
- PCRE2_SPTR32 p = (PCRE2_SPTR32)pattern.c_str();
-
- code = pcre2_compile_32(p, pattern.length(), flags, &err, &offset, cctx);
-
- pcre2_compile_context_free_32(cctx);
-
- if (!code) {
- PCRE2_UCHAR32 buf[256];
- pcre2_get_error_message_32(err, buf, 256);
- String message = String::num(offset) + ": " + String((const CharType *)buf);
- ERR_PRINT(message.utf8());
- return FAILED;
- }
+ if (!code) {
+ PCRE2_UCHAR32 buf[256];
+ pcre2_get_error_message_32(err, buf, 256);
+ String message = String::num(offset) + ": " + String((const char32_t *)buf);
+ ERR_PRINT(message.utf8());
+ return FAILED;
}
return OK;
}
@@ -234,69 +202,39 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
length = p_end;
}
- if (sizeof(CharType) == 2) {
- pcre2_code_16 *c = (pcre2_code_16 *)code;
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx);
- PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str();
-
- pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx);
-
- int res = pcre2_match_16(c, s, length, p_offset, 0, match, mctx);
+ pcre2_code_32 *c = (pcre2_code_32 *)code;
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
+ PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data();
- if (res < 0) {
- pcre2_match_data_free_16(match);
- return nullptr;
- }
-
- uint32_t size = pcre2_get_ovector_count_16(match);
- PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(match);
-
- result->data.resize(size);
-
- for (uint32_t i = 0; i < size; i++) {
- result->data.write[i].start = ovector[i * 2];
- result->data.write[i].end = ovector[i * 2 + 1];
- }
-
- pcre2_match_data_free_16(match);
- pcre2_match_context_free_16(mctx);
-
- } else {
- pcre2_code_32 *c = (pcre2_code_32 *)code;
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
- PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str();
+ pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
- pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
+ int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx);
- int res = pcre2_match_32(c, s, length, p_offset, 0, match, mctx);
-
- if (res < 0) {
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ if (res < 0) {
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
- return nullptr;
- }
+ return nullptr;
+ }
- uint32_t size = pcre2_get_ovector_count_32(match);
- PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match);
+ uint32_t size = pcre2_get_ovector_count_32(match);
+ PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_32(match);
- result->data.resize(size);
+ result->data.resize(size);
- for (uint32_t i = 0; i < size; i++) {
- result->data.write[i].start = ovector[i * 2];
- result->data.write[i].end = ovector[i * 2 + 1];
- }
-
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ for (uint32_t i = 0; i < size; i++) {
+ result->data.write[i].start = ovector[i * 2];
+ result->data.write[i].end = ovector[i * 2 + 1];
}
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
+
result->subject = p_subject;
uint32_t count;
- const CharType *table;
+ const char32_t *table;
uint32_t entry_size;
_pattern_info(PCRE2_INFO_NAMECOUNT, &count);
@@ -304,7 +242,7 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
_pattern_info(PCRE2_INFO_NAMEENTRYSIZE, &entry_size);
for (uint32_t i = 0; i < count; i++) {
- CharType id = table[i * entry_size];
+ char32_t id = table[i * entry_size];
if (result->data[id].start == -1) {
continue;
}
@@ -344,7 +282,7 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a
const int safety_zone = 1;
PCRE2_SIZE olength = p_subject.length() + 1; // space for output string and one terminating \0 character
- Vector<CharType> output;
+ Vector<char32_t> output;
output.resize(olength + safety_zone);
uint32_t flags = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH;
@@ -357,55 +295,28 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a
length = p_end;
}
- if (sizeof(CharType) == 2) {
- pcre2_code_16 *c = (pcre2_code_16 *)code;
- pcre2_general_context_16 *gctx = (pcre2_general_context_16 *)general_ctx;
- pcre2_match_context_16 *mctx = pcre2_match_context_create_16(gctx);
- PCRE2_SPTR16 s = (PCRE2_SPTR16)p_subject.c_str();
- PCRE2_SPTR16 r = (PCRE2_SPTR16)p_replacement.c_str();
- PCRE2_UCHAR16 *o = (PCRE2_UCHAR16 *)output.ptrw();
+ pcre2_code_32 *c = (pcre2_code_32 *)code;
+ pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
+ pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
+ PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.get_data();
+ PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.get_data();
+ PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw();
- pcre2_match_data_16 *match = pcre2_match_data_create_from_pattern_16(c, gctx);
-
- int res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
-
- if (res == PCRE2_ERROR_NOMEMORY) {
- output.resize(olength + safety_zone);
- o = (PCRE2_UCHAR16 *)output.ptrw();
- res = pcre2_substitute_16(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- }
+ pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
- pcre2_match_data_free_16(match);
- pcre2_match_context_free_16(mctx);
+ int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- if (res < 0) {
- return String();
- }
-
- } else {
- pcre2_code_32 *c = (pcre2_code_32 *)code;
- pcre2_general_context_32 *gctx = (pcre2_general_context_32 *)general_ctx;
- pcre2_match_context_32 *mctx = pcre2_match_context_create_32(gctx);
- PCRE2_SPTR32 s = (PCRE2_SPTR32)p_subject.c_str();
- PCRE2_SPTR32 r = (PCRE2_SPTR32)p_replacement.c_str();
- PCRE2_UCHAR32 *o = (PCRE2_UCHAR32 *)output.ptrw();
-
- pcre2_match_data_32 *match = pcre2_match_data_create_from_pattern_32(c, gctx);
-
- int res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
-
- if (res == PCRE2_ERROR_NOMEMORY) {
- output.resize(olength + safety_zone);
- o = (PCRE2_UCHAR32 *)output.ptrw();
- res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
- }
+ if (res == PCRE2_ERROR_NOMEMORY) {
+ output.resize(olength + safety_zone);
+ o = (PCRE2_UCHAR32 *)output.ptrw();
+ res = pcre2_substitute_32(c, s, length, p_offset, flags, match, mctx, r, p_replacement.length(), o, &olength);
+ }
- pcre2_match_data_free_32(match);
- pcre2_match_context_free_32(mctx);
+ pcre2_match_data_free_32(match);
+ pcre2_match_context_free_32(mctx);
- if (res < 0) {
- return String();
- }
+ if (res < 0) {
+ return String();
}
return String(output.ptr(), olength);
@@ -435,7 +346,7 @@ Array RegEx::get_names() const {
ERR_FAIL_COND_V(!is_valid(), result);
uint32_t count;
- const CharType *table;
+ const char32_t *table;
uint32_t entry_size;
_pattern_info(PCRE2_INFO_NAMECOUNT, &count);
@@ -453,39 +364,21 @@ Array RegEx::get_names() const {
}
RegEx::RegEx() {
- if (sizeof(CharType) == 2) {
- general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr);
-
- } else {
- general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- }
+ general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
code = nullptr;
}
RegEx::RegEx(const String &p_pattern) {
- if (sizeof(CharType) == 2) {
- general_ctx = pcre2_general_context_create_16(&_regex_malloc, &_regex_free, nullptr);
-
- } else {
- general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- }
+ general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
code = nullptr;
compile(p_pattern);
}
RegEx::~RegEx() {
- if (sizeof(CharType) == 2) {
- if (code) {
- pcre2_code_free_16((pcre2_code_16 *)code);
- }
- pcre2_general_context_free_16((pcre2_general_context_16 *)general_ctx);
-
- } else {
- if (code) {
- pcre2_code_free_32((pcre2_code_32 *)code);
- }
- pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx);
+ if (code) {
+ pcre2_code_free_32((pcre2_code_32 *)code);
}
+ pcre2_general_context_free_32((pcre2_general_context_32 *)general_ctx);
}
void RegEx::_bind_methods() {
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
index 52b49c783e..8cf19e1ca6 100644
--- a/modules/regex/regex.h
+++ b/modules/regex/regex.h
@@ -31,12 +31,12 @@
#ifndef REGEX_H
#define REGEX_H
-#include "core/array.h"
-#include "core/dictionary.h"
-#include "core/map.h"
-#include "core/reference.h"
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/object/reference.h"
+#include "core/string/ustring.h"
+#include "core/templates/map.h"
+#include "core/templates/vector.h"
+#include "core/variant/array.h"
+#include "core/variant/dictionary.h"
class RegExMatch : public Reference {
GDCLASS(RegExMatch, Reference);
diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp
index 5d4aeba2d7..d470fcdaeb 100644
--- a/modules/regex/register_types.cpp
+++ b/modules/regex/register_types.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "regex.h"
void register_regex_types() {
diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h
index 19e6d57474..11b9e1c833 100644
--- a/modules/squish/image_compress_squish.h
+++ b/modules/squish/image_compress_squish.h
@@ -31,7 +31,7 @@
#ifndef IMAGE_COMPRESS_SQUISH_H
#define IMAGE_COMPRESS_SQUISH_H
-#include "core/image.h"
+#include "core/io/image.h"
void image_compress_squish(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels);
void image_decompress_squish(Image *p_image);
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
index 3aceaf11c5..346833ab9c 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
@@ -158,14 +158,16 @@ void AudioStreamOGGVorbis::clear_data() {
void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) {
int src_data_len = p_data.size();
-#define MAX_TEST_MEM (1 << 20)
-
uint32_t alloc_try = 1024;
Vector<char> alloc_mem;
char *w;
stb_vorbis *ogg_stream = nullptr;
stb_vorbis_alloc ogg_alloc;
+ // Vorbis comments may be up to UINT32_MAX, but that's arguably pretty rare.
+ // Let's go with 2^30 so we don't risk going out of bounds.
+ const uint32_t MAX_TEST_MEM = 1 << 30;
+
while (alloc_try < MAX_TEST_MEM) {
alloc_mem.resize(alloc_try);
w = alloc_mem.ptrw();
@@ -205,6 +207,8 @@ void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) {
break;
}
}
+
+ ERR_FAIL_COND_MSG(alloc_try == MAX_TEST_MEM, vformat("Couldn't set vorbis data even with an alloc buffer of %d bytes, report bug.", MAX_TEST_MEM));
}
Vector<uint8_t> AudioStreamOGGVorbis::get_data() const {
diff --git a/modules/stb_vorbis/register_types.cpp b/modules/stb_vorbis/register_types.cpp
index 6669d30278..13c26fc8cc 100644
--- a/modules/stb_vorbis/register_types.cpp
+++ b/modules/stb_vorbis/register_types.cpp
@@ -33,7 +33,7 @@
#include "audio_stream_ogg_vorbis.h"
#ifdef TOOLS_ENABLED
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "resource_importer_ogg_vorbis.h"
#endif
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index ecaba5052b..8e478a40ce 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -32,7 +32,7 @@
#define IMAGE_LOADER_SVG_H
#include "core/io/image_loader.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
/**
@author Daniel Ramirez <djrmuv@gmail.com>
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 1475d24792..aa8b3122f4 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -30,10 +30,10 @@
#include "image_loader_tga.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "core/io/file_access_memory.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size) {
Error error;
@@ -313,9 +313,9 @@ void ImageLoaderTGA::get_recognized_extensions(List<String> *p_extensions) const
p_extensions->push_back("tga");
}
-static Ref<Image> _tga_mem_loader_func(const uint8_t *p_png, int p_size) {
+static Ref<Image> _tga_mem_loader_func(const uint8_t *p_tga, int p_size) {
FileAccessMemory memfile;
- Error open_memfile_error = memfile.open_custom(p_png, p_size);
+ Error open_memfile_error = memfile.open_custom(p_tga, p_size);
ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for TGA image buffer.");
Ref<Image> img;
img.instance();
diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml
index 92244a4d28..cb8852d5ef 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -4,7 +4,8 @@
[VideoStream] resource for Ogg Theora videos.
</brief_description>
<description>
- [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension.
+ [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is less efficient than [VideoStreamWebm]'s VP8 and VP9, but it requires less CPU resources to decode. The Theora codec is decoded on the CPU.
+ [b]Note:[/b] While Ogg Theora videos can also have an [code].ogg[/code] extension, you will have to rename the extension to [code].ogv[/code] to use those videos within Godot.
</description>
<tutorials>
</tutorials>
@@ -22,7 +23,7 @@
<argument index="0" name="file" type="String">
</argument>
<description>
- Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].o[/code] extension.
+ Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension.
</description>
</method>
</methods>
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 498391e44a..4f33f58ed1 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -30,8 +30,8 @@
#include "video_stream_theora.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "thirdparty/misc/yuv2rgb.h"
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 84f816acf8..867f464038 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -35,7 +35,7 @@
#include "core/os/file_access.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
#include "scene/resources/video_stream.h"
#include "servers/audio_server.h"
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 9e7266b95a..75dc16c39a 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -31,7 +31,7 @@
#include "image_loader_tinyexr.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "thirdparty/tinyexr/tinyexr.h"
@@ -73,8 +73,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
}
// Read HALF channel as FLOAT. (GH-13490)
+ bool use_float16 = false;
for (int i = 0; i < exr_header.num_channels; i++) {
if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
+ use_float16 = true;
exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
}
}
@@ -102,33 +104,10 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
idxB = c;
} else if (strcmp(exr_header.channels[c].name, "A") == 0) {
idxA = c;
- }
- }
-
- if (exr_header.num_channels == 1) {
- // Grayscale channel only.
- idxR = 0;
- idxG = 0;
- idxB = 0;
- idxA = 0;
- } else {
- // Assume RGB(A)
- if (idxR == -1) {
- ERR_PRINT("TinyEXR: R channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
- }
-
- if (idxG == -1) {
- ERR_PRINT("TinyEXR: G channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
- }
-
- if (idxB == -1) {
- ERR_PRINT("TinyEXR: B channel not found.");
- // @todo { free exr_image }
- return ERR_FILE_CORRUPT;
+ } else if (strcmp(exr_header.channels[c].name, "Y") == 0) {
+ idxR = c;
+ idxG = c;
+ idxB = c;
}
}
@@ -138,14 +117,27 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
Image::Format format;
int output_channels = 0;
+ int channel_size = use_float16 ? 2 : 4;
if (idxA != -1) {
- imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16
- format = Image::FORMAT_RGBAH;
+ imgdata.resize(exr_image.width * exr_image.height * 4 * channel_size); //RGBA
+ format = use_float16 ? Image::FORMAT_RGBAH : Image::FORMAT_RGBAF;
output_channels = 4;
- } else {
- imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16
- format = Image::FORMAT_RGBH;
+ } else if (idxB != -1) {
+ ERR_FAIL_COND_V(idxG == -1, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 3 * channel_size); //RGB
+ format = use_float16 ? Image::FORMAT_RGBH : Image::FORMAT_RGBF;
output_channels = 3;
+ } else if (idxG != -1) {
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 2 * channel_size); //RG
+ format = use_float16 ? Image::FORMAT_RGH : Image::FORMAT_RGF;
+ output_channels = 2;
+ } else {
+ ERR_FAIL_COND_V(idxR == -1, ERR_FILE_CORRUPT);
+ imgdata.resize(exr_image.width * exr_image.height * 1 * channel_size); //R
+ format = use_float16 ? Image::FORMAT_RH : Image::FORMAT_RF;
+ output_channels = 1;
}
EXRTile single_image_tile;
@@ -175,9 +167,11 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
exr_tiles = exr_image.tiles;
}
+ //print_line("reading format: " + Image::get_format_name(format));
{
uint8_t *wd = imgdata.ptrw();
- uint16_t *iw = (uint16_t *)wd;
+ uint16_t *iw16 = (uint16_t *)wd;
+ float *iw32 = (float *)wd;
// Assume `out_rgba` have enough memory allocated.
for (int tile_index = 0; tile_index < num_tiles; tile_index++) {
@@ -187,41 +181,99 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_f
int th = tile.height;
const float *r_channel_start = reinterpret_cast<const float *>(tile.images[idxR]);
- const float *g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);
- const float *b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);
+ const float *g_channel_start = nullptr;
+ const float *b_channel_start = nullptr;
const float *a_channel_start = nullptr;
+ if (idxG != -1) {
+ g_channel_start = reinterpret_cast<const float *>(tile.images[idxG]);
+ }
+ if (idxB != -1) {
+ b_channel_start = reinterpret_cast<const float *>(tile.images[idxB]);
+ }
if (idxA != -1) {
a_channel_start = reinterpret_cast<const float *>(tile.images[idxA]);
}
- uint16_t *first_row_w = iw + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
+ uint16_t *first_row_w16 = iw16 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
+ float *first_row_w32 = iw32 + (tile.offset_y * tile_height * exr_image.width + tile.offset_x * tile_width) * output_channels;
for (int y = 0; y < th; y++) {
const float *r_channel = r_channel_start + y * tile_width;
- const float *g_channel = g_channel_start + y * tile_width;
- const float *b_channel = b_channel_start + y * tile_width;
+ const float *g_channel = nullptr;
+ const float *b_channel = nullptr;
const float *a_channel = nullptr;
-
+ if (g_channel_start) {
+ g_channel = g_channel_start + y * tile_width;
+ }
+ if (b_channel_start) {
+ b_channel = b_channel_start + y * tile_width;
+ }
if (a_channel_start) {
a_channel = a_channel_start + y * tile_width;
}
- uint16_t *row_w = first_row_w + (y * exr_image.width * output_channels);
-
- for (int x = 0; x < tw; x++) {
- Color color(*r_channel++, *g_channel++, *b_channel++);
-
- if (p_force_linear) {
- color = color.to_linear();
+ if (use_float16) {
+ uint16_t *row_w = first_row_w16 + (y * exr_image.width * output_channels);
+
+ for (int x = 0; x < tw; x++) {
+ Color color;
+ color.r = *r_channel++;
+ if (g_channel) {
+ color.g = *g_channel++;
+ }
+ if (b_channel) {
+ color.b = *b_channel++;
+ }
+ if (a_channel) {
+ color.a = *a_channel++;
+ }
+
+ if (p_force_linear) {
+ color = color.to_linear();
+ }
+
+ *row_w++ = Math::make_half_float(color.r);
+ if (g_channel) {
+ *row_w++ = Math::make_half_float(color.g);
+ }
+ if (b_channel) {
+ *row_w++ = Math::make_half_float(color.b);
+ }
+ if (a_channel) {
+ *row_w++ = Math::make_half_float(color.a);
+ }
}
-
- *row_w++ = Math::make_half_float(color.r);
- *row_w++ = Math::make_half_float(color.g);
- *row_w++ = Math::make_half_float(color.b);
-
- if (idxA != -1) {
- *row_w++ = Math::make_half_float(*a_channel++);
+ } else {
+ float *row_w = first_row_w32 + (y * exr_image.width * output_channels);
+
+ for (int x = 0; x < tw; x++) {
+ Color color;
+ color.r = *r_channel++;
+ if (g_channel) {
+ color.g = *g_channel++;
+ }
+ if (b_channel) {
+ color.b = *b_channel++;
+ }
+ if (a_channel) {
+ color.a = *a_channel++;
+ }
+
+ if (p_force_linear) {
+ color = color.to_linear();
+ }
+
+ *row_w++ = color.r;
+ if (g_channel) {
+ *row_w++ = color.g;
+ }
+ if (b_channel) {
+ *row_w++ = color.b;
+ }
+ if (a_channel) {
+ *row_w++ = color.a;
+ }
}
}
}
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
index 34900206de..e66bc9d11a 100644
--- a/modules/upnp/register_types.cpp
+++ b/modules/upnp/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "upnp.h"
#include "upnp_device.h"
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
index 1c4b5549f4..e87f93e697 100644
--- a/modules/upnp/upnp.h
+++ b/modules/upnp/upnp.h
@@ -31,7 +31,7 @@
#ifndef GODOT_UPNP_H
#define GODOT_UPNP_H
-#include "core/reference.h"
+#include "core/object/reference.h"
#include "upnp_device.h"
diff --git a/modules/upnp/upnp_device.h b/modules/upnp/upnp_device.h
index 4b519fce32..a287c99b0d 100644
--- a/modules/upnp/upnp_device.h
+++ b/modules/upnp/upnp_device.h
@@ -31,7 +31,7 @@
#ifndef GODOT_UPNP_DEVICE_H
#define GODOT_UPNP_DEVICE_H
-#include "core/reference.h"
+#include "core/object/reference.h"
class UPNPDevice : public Reference {
GDCLASS(UPNPDevice, Reference);
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index db1ef2adc6..088d84d2ec 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- <link>https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
+ <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
</tutorials>
<methods>
<method name="add_custom_signal">
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index ef4183e6f6..000fbd0140 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -111,61 +111,61 @@
<constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc">
Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
</constant>
- <constant name="MATH_RAND" value="32" enum="BuiltinFunc">
+ <constant name="MATH_RANDI" value="32" enum="BuiltinFunc">
Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
</constant>
<constant name="MATH_RANDF" value="33" enum="BuiltinFunc">
Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
</constant>
- <constant name="MATH_RANDOM" value="34" enum="BuiltinFunc">
+ <constant name="MATH_RANDF_RANGE" value="34" enum="BuiltinFunc">
Return a random floating-point value between the two inputs.
</constant>
- <constant name="MATH_SEED" value="35" enum="BuiltinFunc">
+ <constant name="MATH_RANDI_RANGE" value="35" enum="BuiltinFunc">
+ Return a random 32-bit integer value between the two inputs.
+ </constant>
+ <constant name="MATH_SEED" value="36" enum="BuiltinFunc">
Set the seed for the random number generator.
</constant>
- <constant name="MATH_RANDSEED" value="36" enum="BuiltinFunc">
+ <constant name="MATH_RANDSEED" value="37" enum="BuiltinFunc">
Return a random value from the given seed, along with the new seed.
</constant>
- <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc">
+ <constant name="MATH_DEG2RAD" value="38" enum="BuiltinFunc">
Convert the input from degrees to radians.
</constant>
- <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc">
+ <constant name="MATH_RAD2DEG" value="39" enum="BuiltinFunc">
Convert the input from radians to degrees.
</constant>
- <constant name="MATH_LINEAR2DB" value="39" enum="BuiltinFunc">
+ <constant name="MATH_LINEAR2DB" value="40" enum="BuiltinFunc">
Convert the input from linear volume to decibel volume.
</constant>
- <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc">
+ <constant name="MATH_DB2LINEAR" value="41" enum="BuiltinFunc">
Convert the input from decibel volume to linear volume.
</constant>
- <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc">
+ <constant name="MATH_POLAR2CARTESIAN" value="42" enum="BuiltinFunc">
Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis).
</constant>
- <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc">
+ <constant name="MATH_CARTESIAN2POLAR" value="43" enum="BuiltinFunc">
Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle).
</constant>
- <constant name="MATH_WRAP" value="43" enum="BuiltinFunc">
+ <constant name="MATH_WRAP" value="44" enum="BuiltinFunc">
</constant>
- <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc">
+ <constant name="MATH_WRAPF" value="45" enum="BuiltinFunc">
</constant>
- <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc">
+ <constant name="LOGIC_MAX" value="46" enum="BuiltinFunc">
Return the greater of the two numbers, also known as their maximum.
</constant>
- <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc">
+ <constant name="LOGIC_MIN" value="47" enum="BuiltinFunc">
Return the lesser of the two numbers, also known as their minimum.
</constant>
- <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc">
+ <constant name="LOGIC_CLAMP" value="48" enum="BuiltinFunc">
Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
</constant>
- <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc">
+ <constant name="LOGIC_NEAREST_PO2" value="49" enum="BuiltinFunc">
Return the nearest power of 2 to the input.
</constant>
- <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc">
+ <constant name="OBJ_WEAKREF" value="50" enum="BuiltinFunc">
Create a [WeakRef] from the input.
</constant>
- <constant name="FUNC_FUNCREF" value="50" enum="BuiltinFunc">
- Create a [FuncRef] from the input.
- </constant>
<constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc">
Convert between types.
</constant>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
index f13d449064..1c22070ab1 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
@@ -31,5 +31,7 @@
</constant>
<constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
</constant>
+ <constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
+ </constant>
</constants>
</class>
diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg
index f6475d590e..2352ba5d87 100644
--- a/modules/visual_script/icons/VisualScript.svg
+++ b/modules/visual_script/icons/VisualScript.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<ellipse cx="3" cy="1039.4" r="2" fill="#6e6e6e"/>
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2h5.2715a2 2 0 0 1 -0.27148 -1 2 2 0 0 1 2 -2 2 2 0 0 1 2 2 2 2 0 0 1 -0.26953 1h5.2695v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3 -3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324 -1 2 2 0 0 0 0 -2 2 2 0 0 0 -1.7324 -1h2v-2h-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 8afed1229f..0172f29923 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/io/resource_loader.h"
#include "visual_script.h"
#include "visual_script_builtin_funcs.h"
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 1af4b46e22..1c7d5472cb 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -30,9 +30,9 @@
#include "visual_script.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/node.h"
#include "visual_script_nodes.h"
@@ -84,10 +84,10 @@ void VisualScriptNode::validate_input_default_values() {
Callable::CallError ce;
Variant existing = default_input_values[i];
const Variant *existingp = &existing;
- default_input_values[i] = Variant::construct(expected, &existingp, 1, ce, false);
+ Variant::construct(expected, default_input_values[i], &existingp, 1, ce);
if (ce.error != Callable::CallError::CALL_OK) {
//could not convert? force..
- default_input_values[i] = Variant::construct(expected, nullptr, 0, ce, false);
+ Variant::construct(expected, default_input_values[i], nullptr, 0, ce);
}
}
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index cb5ed37ba1..85dab4e6cf 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -33,8 +33,8 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/object/script_language.h"
#include "core/os/thread.h"
-#include "core/script_language.h"
class VisualScriptInstance;
class VisualScriptNodeInstance;
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index a0dcd76d10..a27307aec2 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -30,13 +30,12 @@
#include "visual_script_builtin_funcs.h"
-#include "core/class_db.h"
-#include "core/func_ref.h"
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
+#include "core/object/class_db.h"
+#include "core/object/reference.h"
#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX] = {
"sin",
@@ -73,7 +72,8 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"randomize",
"randi",
"randf",
- "rand_range",
+ "randf_range",
+ "randi_range",
"seed",
"rand_seed",
"deg2rad",
@@ -89,7 +89,6 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"clamp",
"nearest_po2",
"weakref",
- "funcref",
"convert",
"typeof",
"type_exists",
@@ -143,7 +142,7 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
switch (p_func) {
case MATH_RANDOMIZE:
- case MATH_RAND:
+ case MATH_RANDI:
case MATH_RANDF:
return 0;
case MATH_SIN:
@@ -194,12 +193,12 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case MATH_POW:
case MATH_EASE:
case MATH_STEPIFY:
- case MATH_RANDOM:
+ case MATH_RANDF_RANGE:
+ case MATH_RANDI_RANGE:
case MATH_POLAR2CARTESIAN:
case MATH_CARTESIAN2POLAR:
case LOGIC_MAX:
case LOGIC_MIN:
- case FUNC_FUNCREF:
case TYPE_CONVERT:
case COLORN:
return 2;
@@ -361,16 +360,23 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
}
} break;
case MATH_RANDOMIZE:
- case MATH_RAND:
+ case MATH_RANDI:
case MATH_RANDF: {
} break;
- case MATH_RANDOM: {
+ case MATH_RANDF_RANGE: {
if (p_idx == 0) {
return PropertyInfo(Variant::FLOAT, "from");
} else {
return PropertyInfo(Variant::FLOAT, "to");
}
} break;
+ case MATH_RANDI_RANGE: {
+ if (p_idx == 0) {
+ return PropertyInfo(Variant::INT, "from");
+ } else {
+ return PropertyInfo(Variant::INT, "to");
+ }
+ } break;
case MATH_SEED:
case MATH_RANDSEED: {
return PropertyInfo(Variant::INT, "seed");
@@ -426,13 +432,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case OBJ_WEAKREF: {
return PropertyInfo(Variant::OBJECT, "source");
} break;
- case FUNC_FUNCREF: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::OBJECT, "instance");
- } else {
- return PropertyInfo(Variant::STRING, "funcname");
- }
- } break;
case TYPE_CONVERT: {
if (p_idx == 0) {
return PropertyInfo(Variant::NIL, "what");
@@ -551,13 +550,16 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
} break;
case MATH_RANDOMIZE: {
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
t = Variant::INT;
} break;
case MATH_RANDF:
- case MATH_RANDOM: {
+ case MATH_RANDF_RANGE: {
t = Variant::FLOAT;
} break;
+ case MATH_RANDI_RANGE: {
+ t = Variant::INT;
+ } break;
case MATH_SEED: {
} break;
case MATH_RANDSEED: {
@@ -593,10 +595,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t = Variant::OBJECT;
} break;
- case FUNC_FUNCREF: {
- t = Variant::OBJECT;
-
- } break;
case TYPE_CONVERT: {
} break;
case TEXT_ORD:
@@ -861,17 +859,22 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
Math::randomize();
} break;
- case VisualScriptBuiltinFunc::MATH_RAND: {
+ case VisualScriptBuiltinFunc::MATH_RANDI: {
*r_return = Math::rand();
} break;
case VisualScriptBuiltinFunc::MATH_RANDF: {
*r_return = Math::randf();
} break;
- case VisualScriptBuiltinFunc::MATH_RANDOM: {
+ case VisualScriptBuiltinFunc::MATH_RANDF_RANGE: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
*r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]);
} break;
+ case VisualScriptBuiltinFunc::MATH_RANDI_RANGE: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return = Math::random((int)*p_inputs[0], (int)*p_inputs[1]);
+ } break;
case VisualScriptBuiltinFunc::MATH_SEED: {
VALIDATE_ARG_NUM(0);
uint64_t seed = *p_inputs[0];
@@ -1013,30 +1016,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
}
} break;
- case VisualScriptBuiltinFunc::FUNC_FUNCREF: {
- if (p_inputs[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
-
- return;
- }
- if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING;
-
- return;
- }
-
- Ref<FuncRef> fr = memnew(FuncRef);
-
- fr->set_instance(*p_inputs[0]);
- fr->set_function(*p_inputs[1]);
-
- *r_return = fr;
-
- } break;
case VisualScriptBuiltinFunc::TYPE_CONVERT: {
VALIDATE_ARG_NUM(1);
int type = *p_inputs[1];
@@ -1048,7 +1027,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
return;
} else {
- *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error);
+ Variant::construct(Variant::Type(type), *r_return, p_inputs, 1, r_error);
}
} break;
case VisualScriptBuiltinFunc::TYPE_OF: {
@@ -1060,7 +1039,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
} break;
case VisualScriptBuiltinFunc::TEXT_CHAR: {
- CharType result[2] = { *p_inputs[0], 0 };
+ char32_t result[2] = { *p_inputs[0], 0 };
*r_return = String(result);
@@ -1283,9 +1262,10 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD);
BIND_ENUM_CONSTANT(MATH_DECTIME);
BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
- BIND_ENUM_CONSTANT(MATH_RAND);
+ BIND_ENUM_CONSTANT(MATH_RANDI);
BIND_ENUM_CONSTANT(MATH_RANDF);
- BIND_ENUM_CONSTANT(MATH_RANDOM);
+ BIND_ENUM_CONSTANT(MATH_RANDF_RANGE);
+ BIND_ENUM_CONSTANT(MATH_RANDI_RANGE);
BIND_ENUM_CONSTANT(MATH_SEED);
BIND_ENUM_CONSTANT(MATH_RANDSEED);
BIND_ENUM_CONSTANT(MATH_DEG2RAD);
@@ -1301,7 +1281,6 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(LOGIC_CLAMP);
BIND_ENUM_CONSTANT(LOGIC_NEAREST_PO2);
BIND_ENUM_CONSTANT(OBJ_WEAKREF);
- BIND_ENUM_CONSTANT(FUNC_FUNCREF);
BIND_ENUM_CONSTANT(TYPE_CONVERT);
BIND_ENUM_CONSTANT(TYPE_OF);
BIND_ENUM_CONSTANT(TYPE_EXISTS);
@@ -1375,9 +1354,10 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/random", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOM>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF_RANGE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI_RANGE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
@@ -1396,7 +1376,6 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref", create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/funcref", create_builtin_func_node<VisualScriptBuiltinFunc::FUNC_FUNCREF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index aee2ed79ce..361b445e30 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -70,9 +70,10 @@ public:
MATH_MOVE_TOWARD,
MATH_DECTIME,
MATH_RANDOMIZE,
- MATH_RAND,
+ MATH_RANDI,
MATH_RANDF,
- MATH_RANDOM,
+ MATH_RANDF_RANGE,
+ MATH_RANDI_RANGE,
MATH_SEED,
MATH_RANDSEED,
MATH_DEG2RAD,
@@ -88,7 +89,6 @@ public:
LOGIC_CLAMP,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
- FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 5581ea9318..1bb96a47f3 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -31,10 +31,10 @@
#include "visual_script_editor.h"
#include "core/input/input.h"
-#include "core/object.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
-#include "core/script_language.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "editor/editor_node.h"
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
@@ -388,7 +388,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::NODE_PATH:
color = Color(0.41, 0.58, 0.93);
break;
- case Variant::_RID:
+ case Variant::RID:
color = Color(0.41, 0.93, 0.6);
break;
case Variant::OBJECT:
@@ -494,7 +494,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::NODE_PATH:
color = Color(0.41, 0.58, 0.93);
break;
- case Variant::_RID:
+ case Variant::RID:
color = Color(0.17, 0.9, 0.45);
break;
case Variant::OBJECT:
@@ -885,7 +885,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
//not the same, reconvert
Callable::CallError ce;
const Variant *existingp = &value;
- value = Variant::construct(left_type, &existingp, 1, ce, false);
+ Variant::construct(left_type, value, &existingp, 1, ce);
}
if (left_type == Variant::COLOR) {
@@ -1124,8 +1124,8 @@ void VisualScriptEditor::_update_members() {
TreeItem *ti = members->create_item(variables);
ti->set_text(0, E->get());
- Variant var = script->get_variable_default_value(E->get());
- ti->set_suffix(0, "= " + String(var));
+
+ ti->set_suffix(0, "= " + _sanitized_variant_text(E->get()));
ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]);
ti->set_selectable(0, true);
@@ -1167,6 +1167,20 @@ void VisualScriptEditor::_update_members() {
updating_members = false;
}
+String VisualScriptEditor::_sanitized_variant_text(const StringName &property_name) {
+ Variant var = script->get_variable_default_value(property_name);
+
+ if (script->get_variable_info(property_name).type != Variant::NIL) {
+ Callable::CallError ce;
+ const Variant *converted = &var;
+ Variant n;
+ Variant::construct(script->get_variable_info(property_name).type, n, &converted, 1, ce);
+ var = n;
+ }
+
+ return String(var);
+}
+
void VisualScriptEditor::_member_selected() {
if (updating_members) {
return;
@@ -2682,7 +2696,8 @@ void VisualScriptEditor::reload(bool p_soft) {
_update_graph();
}
-void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
+Array VisualScriptEditor::get_breakpoints() {
+ Array breakpoints;
List<StringName> functions;
script->get_function_list(&functions);
for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
@@ -2691,10 +2706,11 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
for (List<int>::Element *F = nodes.front(); F; F = F->next()) {
Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get());
if (vsn->is_breakpoint()) {
- p_breakpoints->push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
+ breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
}
}
}
+ return breakpoints;
}
void VisualScriptEditor::add_callback(const String &p_function, PackedStringArray p_args) {
@@ -3945,8 +3961,9 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i
Variant existing = vsn->get_default_input_value(p_input_port);
if (pinfo.type != Variant::NIL && existing.get_type() != pinfo.type) {
Callable::CallError ce;
- const Variant *existingp = &existing;
- existing = Variant::construct(pinfo.type, &existingp, 1, ce, false);
+ Variant e = existing;
+ const Variant *existingp = &e;
+ Variant::construct(pinfo.type, existing, &existingp, 1, ce);
}
default_value_edit->set_position(Object::cast_to<Control>(p_button)->get_global_position() + Vector2(0, Object::cast_to<Control>(p_button)->get_size().y));
@@ -4769,8 +4786,8 @@ VisualScriptEditor::VisualScriptEditor() {
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected));
- graph->connect("_begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move));
- graph->connect("_end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move));
+ graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move));
+ graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move));
graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete));
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate));
graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input));
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 0c5665cee8..66e435741f 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -146,6 +146,7 @@ class VisualScriptEditor : public ScriptEditorBase {
bool updating_members;
void _update_members();
+ String _sanitized_variant_text(const StringName &property_name);
StringName selected;
@@ -312,7 +313,7 @@ public:
virtual void ensure_focus() override;
virtual void tag_saved_version() override;
virtual void reload(bool p_soft) override;
- virtual void get_breakpoints(List<int> *p_breakpoints) override;
+ virtual Array get_breakpoints() override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
virtual void update_settings() override;
virtual bool show_members_overview() override;
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 2ac7793b8c..10a18dfd5e 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -187,7 +187,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
while (true) {
#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
- CharType cchar = GET_CHAR();
+ char32_t cchar = GET_CHAR();
if (cchar == 0) {
r_token.type = TK_EOF;
return OK;
@@ -329,7 +329,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case '"': {
String str;
while (true) {
- CharType ch = GET_CHAR();
+ char32_t ch = GET_CHAR();
if (ch == 0) {
_set_error("Unterminated String");
@@ -340,13 +340,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} else if (ch == '\\') {
//escaped characters...
- CharType next = GET_CHAR();
+ char32_t next = GET_CHAR();
if (next == 0) {
_set_error("Unterminated String");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- CharType res = 0;
+ char32_t res = 0;
switch (next) {
case 'b':
@@ -367,7 +367,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
case 'u': {
// hex number
for (int j = 0; j < 4; j++) {
- CharType c = GET_CHAR();
+ char32_t c = GET_CHAR();
if (c == 0) {
_set_error("Unterminated String");
@@ -379,7 +379,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- CharType v;
+ char32_t v;
if (c >= '0' && c <= '9') {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
@@ -431,7 +431,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
#define READING_DONE 4
int reading = READING_INT;
- CharType c = cchar;
+ char32_t c = cchar;
bool exp_sign = false;
bool exp_beg = false;
bool is_float = false;
@@ -1341,7 +1341,7 @@ public:
}
bool valid;
- r_ret = base.get_named(index->name, &valid);
+ r_ret = base.get_named(index->name, valid);
if (!valid) {
r_error_str = "Invalid index '" + String(index->name) + "' for base of type " + Variant::get_type_name(base.get_type()) + ".";
return true;
@@ -1405,7 +1405,7 @@ public:
argp.write[i] = &arr[i];
}
- r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce);
+ Variant::construct(constructor->data_type, r_ret, (const Variant **)argp.ptr(), argp.size(), ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "Invalid arguments to construct '" + Variant::get_type_name(constructor->data_type) + "'.";
@@ -1463,7 +1463,7 @@ public:
argp.write[i] = &arr[i];
}
- r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce);
+ base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "On call to '" + String(call->method) + "':";
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 3ed20fab35..36c756fc58 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -30,9 +30,9 @@
#include "visual_script_flow_control.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/os/keyboard.h"
-#include "core/project_settings.h"
//////////////////////////////////////////
////////////////RETURN////////////////////
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index f13377f3c8..34a1cfc4fc 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -30,7 +30,7 @@
#include "visual_script_func_nodes.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/io/resource_loader.h"
#include "core/os/os.h"
#include "scene/main/node.h"
@@ -42,7 +42,7 @@
//////////////////////////////////////////
int VisualScriptFunctionCall::get_output_sequence_port_count() const {
- if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) {
+ if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function))) {
return 0;
} else {
return 1;
@@ -50,7 +50,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const {
}
bool VisualScriptFunctionCall::has_input_sequence_port() const {
- return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)));
+ return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function)));
}
#ifdef TOOLS_ENABLED
@@ -130,7 +130,11 @@ StringName VisualScriptFunctionCall::_get_base_type() const {
int VisualScriptFunctionCall::get_input_value_port_count() const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type, function);
+ Vector<Variant::Type> types;
+ int argc = Variant::get_builtin_method_argument_count(basic_type, function);
+ for (int i = 0; i < argc; i++) {
+ types.push_back(Variant::get_builtin_method_argument_type(basic_type, function, i));
+ }
return types.size() + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) + 1;
} else {
@@ -147,8 +151,7 @@ int VisualScriptFunctionCall::get_input_value_port_count() const {
int VisualScriptFunctionCall::get_output_value_port_count() const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- bool returns = false;
- Variant::get_method_return_type(basic_type, function, &returns);
+ bool returns = Variant::has_builtin_method_return_value(basic_type, function);
return returns ? 1 : 0;
} else {
@@ -195,10 +198,7 @@ PropertyInfo VisualScriptFunctionCall::get_input_value_port_info(int p_idx) cons
#ifdef DEBUG_METHODS_ENABLED
if (call_mode == CALL_MODE_BASIC_TYPE) {
- Vector<StringName> names = Variant::get_method_argument_names(basic_type, function);
- Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type, function);
- return PropertyInfo(types[p_idx], names[p_idx]);
-
+ return PropertyInfo(Variant::get_builtin_method_argument_type(basic_type, function, p_idx), Variant::get_builtin_method_argument_name(basic_type, function, p_idx));
} else {
MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
if (mb) {
@@ -220,7 +220,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
#ifdef DEBUG_METHODS_ENABLED
if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(Variant::get_method_return_type(basic_type, function), "");
+ return PropertyInfo(Variant::get_builtin_method_return_type(basic_type, function), "");
} else {
if (call_mode == CALL_MODE_INSTANCE) {
if (p_idx == 0) {
@@ -419,7 +419,7 @@ void VisualScriptFunctionCall::set_function(const StringName &p_type) {
function = p_type;
if (call_mode == CALL_MODE_BASIC_TYPE) {
- use_default_args = Variant::get_method_default_arguments(basic_type, function).size();
+ use_default_args = Variant::get_builtin_method_default_arguments(basic_type, function).size();
} else {
//update all caches
@@ -606,7 +606,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
int mc = 0;
if (call_mode == CALL_MODE_BASIC_TYPE) {
- mc = Variant::get_method_default_arguments(basic_type, function).size();
+ mc = Variant::get_builtin_method_default_arguments(basic_type, function).size();
} else {
MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
if (mb) {
@@ -805,19 +805,21 @@ public:
} else if (returns) {
if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
if (returns >= 2) {
- *p_outputs[1] = v.call(function, p_inputs + 1, input_args, r_error);
+ v.call(function, p_inputs + 1, input_args, *p_outputs[1], r_error);
} else if (returns == 1) {
- v.call(function, p_inputs + 1, input_args, r_error);
+ Variant ret;
+ v.call(function, p_inputs + 1, input_args, ret, r_error);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_error_str = "Invalid returns count for call_mode == CALL_MODE_INSTANCE";
return 0;
}
} else {
- *p_outputs[0] = v.call(function, p_inputs + 1, input_args, r_error);
+ v.call(function, p_inputs + 1, input_args, *p_outputs[0], r_error);
}
} else {
- v.call(function, p_inputs + 1, input_args, r_error);
+ Variant ret;
+ v.call(function, p_inputs + 1, input_args, ret, r_error);
}
if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
@@ -976,7 +978,7 @@ void VisualScriptPropertySet::_adjust_input_index(PropertyInfo &pinfo) const {
if (index != StringName()) {
Variant v;
Callable::CallError ce;
- v = Variant::construct(pinfo.type, nullptr, 0, ce);
+ Variant::construct(pinfo.type, v, nullptr, 0, ce);
Variant i = v.get(index);
pinfo.type = i.get_type();
}
@@ -1117,7 +1119,7 @@ void VisualScriptPropertySet::_update_cache() {
Variant v;
Callable::CallError ce;
- v = Variant::construct(basic_type, nullptr, 0, ce);
+ Variant::construct(basic_type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
@@ -1336,7 +1338,8 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
if (property.name == "index") {
Callable::CallError ce;
- Variant v = Variant::construct(type_cache.type, nullptr, 0, ce);
+ Variant v;
+ Variant::construct(type_cache.type, v, nullptr, 0, ce);
List<PropertyInfo> plist;
v.get_property_list(&plist);
String options = "";
@@ -1449,11 +1452,11 @@ public:
_FORCE_INLINE_ void _process_get(Variant &source, const Variant &p_argument, bool &valid) {
if (index != StringName() && assign_op == VisualScriptPropertySet::ASSIGN_OP_NONE) {
- source.set_named(index, p_argument, &valid);
+ source.set_named(index, p_argument, valid);
} else {
Variant value;
if (index != StringName()) {
- value = source.get_named(index, &valid);
+ value = source.get_named(index, valid);
} else {
value = source;
}
@@ -1497,7 +1500,7 @@ public:
}
if (index != StringName()) {
- source.set_named(index, value, &valid);
+ source.set_named(index, value, valid);
} else {
source = value;
}
@@ -1562,12 +1565,12 @@ public:
bool valid;
if (needs_get) {
- Variant value = v.get_named(property, &valid);
+ Variant value = v.get_named(property, valid);
_process_get(value, *p_inputs[1], valid);
- v.set_named(property, value, &valid);
+ v.set_named(property, value, valid);
} else {
- v.set_named(property, *p_inputs[1], &valid);
+ v.set_named(property, *p_inputs[1], valid);
}
if (!valid) {
@@ -1786,7 +1789,7 @@ void VisualScriptPropertyGet::_update_cache() {
Variant v;
Callable::CallError ce;
- v = Variant::construct(basic_type, nullptr, 0, ce);
+ Variant::construct(basic_type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
@@ -2012,7 +2015,8 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
if (property.name == "index") {
Callable::CallError ce;
- Variant v = Variant::construct(type_cache, nullptr, 0, ce);
+ Variant v;
+ Variant::construct(type_cache, v, nullptr, 0, ce);
List<PropertyInfo> plist;
v.get_property_list(&plist);
String options = "";
@@ -2088,6 +2092,7 @@ void VisualScriptPropertyGet::_bind_methods() {
BIND_ENUM_CONSTANT(CALL_MODE_SELF);
BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
+ BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
}
class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance {
@@ -2110,7 +2115,7 @@ public:
*p_outputs[0] = object->get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[0] = p_outputs[0]->get_named(index, valid);
}
if (!valid) {
@@ -2139,7 +2144,7 @@ public:
*p_outputs[0] = another->get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[0] = p_outputs[0]->get_named(index, valid);
}
if (!valid) {
@@ -2155,7 +2160,7 @@ public:
*p_outputs[0] = v.get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[0] = p_outputs[0]->get_named(index, valid);
}
if (!valid) {
@@ -2367,7 +2372,8 @@ void register_visual_script_func_nodes() {
Variant::Type t = Variant::Type(i);
String type_name = Variant::get_type_name(t);
Callable::CallError ce;
- Variant vt = Variant::construct(t, nullptr, 0, ce);
+ Variant vt;
+ Variant::construct(t, vt, nullptr, 0, ce);
List<MethodInfo> ml;
vt.get_method_list(&ml);
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 87aa64211e..edec270adc 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -30,11 +30,11 @@
#include "visual_script_nodes.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@@ -828,7 +828,6 @@ PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const {
{ Variant::NIL, Variant::NIL }, //OP_NEGATE,
{ Variant::NIL, Variant::NIL }, //OP_POSITIVE,
{ Variant::INT, Variant::INT }, //OP_MODULE,
- { Variant::STRING, Variant::STRING }, //OP_STRING_CONCAT,
//bitwise
{ Variant::INT, Variant::INT }, //OP_SHIFT_LEFT,
{ Variant::INT, Variant::INT }, //OP_SHIFT_RIGHT,
@@ -873,7 +872,6 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
Variant::NIL, //OP_NEGATE,
Variant::NIL, //OP_POSITIVE,
Variant::INT, //OP_MODULE,
- Variant::STRING, //OP_STRING_CONCAT,
//bitwise
Variant::INT, //OP_SHIFT_LEFT,
Variant::INT, //OP_SHIFT_RIGHT,
@@ -933,36 +931,36 @@ static const char *op_names[] = {
};
String VisualScriptOperator::get_caption() const {
- static const wchar_t *op_names[] = {
+ static const char32_t *op_names[] = {
//comparison
- L"A = B", //OP_EQUAL,
- L"A \u2260 B", //OP_NOT_EQUAL,
- L"A < B", //OP_LESS,
- L"A \u2264 B", //OP_LESS_EQUAL,
- L"A > B", //OP_GREATER,
- L"A \u2265 B", //OP_GREATER_EQUAL,
+ U"A = B", //OP_EQUAL,
+ U"A \u2260 B", //OP_NOT_EQUAL,
+ U"A < B", //OP_LESS,
+ U"A \u2264 B", //OP_LESS_EQUAL,
+ U"A > B", //OP_GREATER,
+ U"A \u2265 B", //OP_GREATER_EQUAL,
//mathematic
- L"A + B", //OP_ADD,
- L"A - B", //OP_SUBTRACT,
- L"A \u00D7 B", //OP_MULTIPLY,
- L"A \u00F7 B", //OP_DIVIDE,
- L"\u00AC A", //OP_NEGATE,
- L"+ A", //OP_POSITIVE,
- L"A mod B", //OP_MODULE,
- L"A .. B", //OP_STRING_CONCAT,
+ U"A + B", //OP_ADD,
+ U"A - B", //OP_SUBTRACT,
+ U"A \u00D7 B", //OP_MULTIPLY,
+ U"A \u00F7 B", //OP_DIVIDE,
+ U"\u00AC A", //OP_NEGATE,
+ U"+ A", //OP_POSITIVE,
+ U"A mod B", //OP_MODULE,
+ U"A .. B", //OP_STRING_CONCAT,
//bitwise
- L"A << B", //OP_SHIFT_LEFT,
- L"A >> B", //OP_SHIFT_RIGHT,
- L"A & B", //OP_BIT_AND,
- L"A | B", //OP_BIT_OR,
- L"A ^ B", //OP_BIT_XOR,
- L"~A", //OP_BIT_NEGATE,
+ U"A << B", //OP_SHIFT_LEFT,
+ U"A >> B", //OP_SHIFT_RIGHT,
+ U"A & B", //OP_BIT_AND,
+ U"A | B", //OP_BIT_OR,
+ U"A ^ B", //OP_BIT_XOR,
+ U"~A", //OP_BIT_NEGATE,
//logic
- L"A and B", //OP_AND,
- L"A or B", //OP_OR,
- L"A xor B", //OP_XOR,
- L"not A", //OP_NOT,
- L"A in B", //OP_IN,
+ U"A and B", //OP_AND,
+ U"A or B", //OP_OR,
+ U"A xor B", //OP_XOR,
+ U"not A", //OP_NOT,
+ U"A in B", //OP_IN,
};
return op_names[op];
@@ -1433,7 +1431,7 @@ void VisualScriptConstant::set_constant_type(Variant::Type p_type) {
type = p_type;
Callable::CallError ce;
- value = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, value, nullptr, 0, ce);
ports_changed_notify();
_change_notify();
}
@@ -1706,8 +1704,10 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
bool valid;
+ // *p_output[0] points to the same place as *p_inputs[2] so we need a temp to store the value before the change in the next line
+ Variant temp = *p_inputs[2];
*p_outputs[0] = *p_inputs[0];
- p_outputs[0]->set(*p_inputs[1], *p_inputs[2], &valid);
+ p_outputs[0]->set(*p_inputs[1], temp, &valid);
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
@@ -1754,7 +1754,7 @@ PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) co
}
PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const {
- String name = GlobalConstants::get_global_constant_name(index);
+ String name = CoreConstants::get_global_constant_name(index);
return PropertyInfo(Variant::INT, name);
}
@@ -1778,7 +1778,7 @@ public:
//virtual int get_working_memory_size() const { return 0; }
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
- *p_outputs[0] = GlobalConstants::get_global_constant_value(index);
+ *p_outputs[0] = CoreConstants::get_global_constant_value(index);
return 0;
}
};
@@ -1795,11 +1795,11 @@ void VisualScriptGlobalConstant::_bind_methods() {
String cc;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
if (i > 0) {
cc += ",";
}
- cc += GlobalConstants::get_global_constant_name(i);
+ cc += CoreConstants::get_global_constant_name(i);
}
ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_global_constant", "get_global_constant");
}
@@ -3255,7 +3255,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
Callable::CallError ce;
- *p_outputs[0] = Variant::construct(type, p_inputs, argcount, ce);
+ Variant::construct(type, *p_outputs[0], p_inputs, argcount, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "Invalid arguments for constructor";
}
@@ -3727,7 +3727,7 @@ void VisualScriptDeconstruct::_update_elements() {
elements.clear();
Variant v;
Callable::CallError ce;
- v = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
@@ -3879,7 +3879,6 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("operators/math/negate", create_op_node<Variant::OP_NEGATE>);
VisualScriptLanguage::singleton->add_register_func("operators/math/positive", create_op_node<Variant::OP_POSITIVE>);
VisualScriptLanguage::singleton->add_register_func("operators/math/remainder", create_op_node<Variant::OP_MODULE>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/string_concat", create_op_node<Variant::OP_STRING_CONCAT>);
//bitwise
VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_left", create_op_node<Variant::OP_SHIFT_LEFT>);
VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_right", create_op_node<Variant::OP_SHIFT_RIGHT>);
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp
index 3c44faab90..875270e74f 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/visual_script_property_selector.cpp
@@ -196,7 +196,7 @@ void VisualScriptPropertySelector::_update_search() {
if (type != Variant::NIL) {
Variant v;
Callable::CallError ce;
- v = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, v, nullptr, 0, ce);
v.get_method_list(&methods);
} else {
Object *obj = ObjectDB::get_instance(script);
diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml
index dfa04720cf..2edbc08cc8 100644
--- a/modules/webm/doc_classes/VideoStreamWebm.xml
+++ b/modules/webm/doc_classes/VideoStreamWebm.xml
@@ -4,7 +4,8 @@
[VideoStream] resource for WebM videos.
</brief_description>
<description>
- [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension.
+ [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU.
+ [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora]
</description>
<tutorials>
</tutorials>
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index 832e14d91a..2128b82e3f 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -30,9 +30,9 @@
#include "video_stream_webm.h"
+#include "core/config/project_settings.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "servers/audio_server.h"
#include "thirdparty/misc/yuv2rgb.h"
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index d5c80e7909..b169687f25 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -32,7 +32,7 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <stdlib.h>
#include <webp/decode.h>
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index 20b4c8f8d2..4f870ddb2f 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -12,4 +12,8 @@ if use_gdnative: # GDNative is retained in Javascript for export compatibility
env_webrtc.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"])
env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"])
+if env["platform"] == "javascript":
+ # Our JavaScript/C++ interface.
+ env.AddJSLibraries(["library_godot_webrtc.js"])
+
env_webrtc.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index 2054276655..e21dee8eff 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -40,7 +40,6 @@
<argument index="0" name="label" type="String">
</argument>
<argument index="1" name="options" type="Dictionary" default="{
-
}">
</argument>
<description>
@@ -82,7 +81,6 @@
<return type="int" enum="Error">
</return>
<argument index="0" name="configuration" type="Dictionary" default="{
-
}">
</argument>
<description>
@@ -97,7 +95,7 @@
{
"urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers.
"username": "a_username", # Optional username for the TURN server.
- "credentials": "a_password", # Optional password for the TURN server.
+ "credential": "a_password", # Optional password for the TURN server.
}
]
}
diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js
new file mode 100644
index 0000000000..b75996b1f3
--- /dev/null
+++ b/modules/webrtc/library_godot_webrtc.js
@@ -0,0 +1,407 @@
+/*************************************************************************/
+/* library_godot_webrtc.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+var GodotRTCDataChannel = {
+ // Our socket implementation that forwards events to C++.
+ $GodotRTCDataChannel__deps: ['$IDHandler', '$GodotOS'],
+ $GodotRTCDataChannel: {
+
+ connect: function(p_id, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+
+ ref.binaryType = 'arraybuffer';
+ ref.onopen = function (event) {
+ p_on_open();
+ };
+ ref.onclose = function (event) {
+ p_on_close();
+ };
+ ref.onerror = function (event) {
+ p_on_error();
+ };
+ ref.onmessage = function(event) {
+ var buffer;
+ var is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ console.error("Blob type not supported");
+ return;
+ } else if (typeof event.data === "string") {
+ is_string = 1;
+ var enc = new TextEncoder("utf-8");
+ buffer = new Uint8Array(enc.encode(event.data));
+ } else {
+ console.error("Unknown message type");
+ return;
+ }
+ var len = buffer.length*buffer.BYTES_PER_ELEMENT;
+ var out = _malloc(len);
+ HEAPU8.set(buffer, out);
+ p_on_message(out, len, is_string);
+ _free(out);
+ }
+ },
+
+ close: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.onopen = null;
+ ref.onmessage = null;
+ ref.onerror = null;
+ ref.onclose = null;
+ ref.close();
+ },
+
+ get_prop: function(p_id, p_prop, p_def) {
+ const ref = IDHandler.get(p_id);
+ return (ref && ref[p_prop] !== undefined) ? ref[p_prop] : p_def;
+ },
+ },
+
+ godot_js_rtc_datachannel_ready_state_get: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 3; // CLOSED
+ }
+
+ switch(ref.readyState) {
+ case "connecting":
+ return 0;
+ case "open":
+ return 1;
+ case "closing":
+ return 2;
+ case "closed":
+ return 3;
+ }
+ return 3; // CLOSED
+ },
+
+ godot_js_rtc_datachannel_send: function(p_id, p_buffer, p_length, p_raw) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 1;
+ }
+
+ const bytes_array = new Uint8Array(p_length);
+ for (var i = 0; i < p_length; i++) {
+ bytes_array[i] = getValue(p_buffer + i, 'i8');
+ }
+
+ if (p_raw) {
+ ref.send(bytes_array.buffer);
+ } else {
+ const string = new TextDecoder('utf-8').decode(bytes_array);
+ ref.send(string);
+ }
+ },
+
+ godot_js_rtc_datachannel_is_ordered: function(p_id) {
+ return IDHandler.get_prop(p_id, 'ordered', true);
+ },
+
+ godot_js_rtc_datachannel_id_get: function(p_id) {
+ return IDHandler.get_prop(p_id, 'id', 65535);
+ },
+
+ godot_js_rtc_datachannel_max_packet_lifetime_get: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 65535;
+ }
+ if (ref['maxPacketLifeTime'] !== undefined) {
+ return ref['maxPacketLifeTime'];
+ } else if (ref['maxRetransmitTime'] !== undefined) {
+ // Guess someone didn't appreciate the standardization process.
+ return ref['maxRetransmitTime'];
+ }
+ return 65535;
+ },
+
+ godot_js_rtc_datachannel_max_retransmits_get: function(p_id) {
+ return IDHandler.get_prop(p_id, 'maxRetransmits', 65535);
+ },
+
+ godot_js_rtc_datachannel_is_negotiated: function(p_id, p_def) {
+ return IDHandler.get_prop(p_id, 'negotiated', 65535);
+ },
+
+ godot_js_rtc_datachannel_label_get: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !ref.label) {
+ return 0;
+ }
+ return GodotOS.allocString(ref.label);
+ },
+
+ godot_js_rtc_datachannel_protocol_get: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !ref.protocol) {
+ return 0;
+ }
+ return GodotOS.allocString(ref.protocol);
+ },
+
+ godot_js_rtc_datachannel_destroy: function(p_id) {
+ GodotRTCDataChannel.close(p_id);
+ IDHandler.remove(p_id);
+ },
+
+ godot_js_rtc_datachannel_connect: function(p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const onopen = GodotOS.get_func(p_on_open).bind(null, p_ref);
+ const onmessage = GodotOS.get_func(p_on_message).bind(null, p_ref);
+ const onerror = GodotOS.get_func(p_on_error).bind(null, p_ref);
+ const onclose = GodotOS.get_func(p_on_close).bind(null, p_ref);
+ GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
+ },
+
+ godot_js_rtc_datachannel_close: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ GodotRTCDataChannel.close(p_id);
+ },
+};
+
+autoAddDeps(GodotRTCDataChannel, '$GodotRTCDataChannel');
+mergeInto(LibraryManager.library, GodotRTCDataChannel);
+
+var GodotRTCPeerConnection = {
+
+ $GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotOS', '$GodotRTCDataChannel'],
+ $GodotRTCPeerConnection: {
+ onstatechange: function(p_id, p_conn, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ var state = 5; // CLOSED
+ switch(p_conn.iceConnectionState) {
+ case "new":
+ state = 0;
+ case "checking":
+ state = 1;
+ case "connected":
+ case "completed":
+ state = 2;
+ case "disconnected":
+ state = 3;
+ case "failed":
+ state = 4;
+ case "closed":
+ state = 5;
+ }
+ callback(state);
+ },
+
+ onicecandidate: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !event.candidate) {
+ return;
+ }
+
+ let c = event.candidate;
+ let candidate_str = GodotOS.allocString(c.candidate);
+ let mid_str = GodotOS.allocString(c.sdpMid);
+ callback(mid_str, c.sdpMLineIndex, candidate_str);
+ _free(candidate_str);
+ _free(mid_str);
+ },
+
+ ondatachannel: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+
+ const cid = IDHandler.add(event.channel);
+ callback(cid);
+ },
+
+ onsession: function(p_id, callback, session) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ let type_str = GodotOS.allocString(session.type);
+ let sdp_str = GodotOS.allocString(session.sdp);
+ callback(type_str, sdp_str);
+ _free(type_str);
+ _free(sdp_str);
+ },
+
+ onerror: function(p_id, callback, error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ console.error(error);
+ callback();
+ },
+ },
+
+ godot_js_rtc_pc_create: function(p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
+ const onstatechange = GodotOS.get_func(p_on_state_change).bind(null, p_ref);
+ const oncandidate = GodotOS.get_func(p_on_candidate).bind(null, p_ref);
+ const ondatachannel = GodotOS.get_func(p_on_datachannel).bind(null, p_ref);
+
+ var config = JSON.parse(UTF8ToString(p_config));
+ var conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ console.error(e);
+ return 0;
+ }
+
+ const base = GodotRTCPeerConnection;
+ const id = IDHandler.add(conn);
+ conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
+ conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
+ conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
+ return id;
+ },
+
+ godot_js_rtc_pc_close: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.close();
+ },
+
+ godot_js_rtc_pc_destroy: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.oniceconnectionstatechange = null;
+ ref.onicecandidate = null;
+ ref.ondatachannel = null;
+ IDHandler.remove(p_id);
+ },
+
+ godot_js_rtc_pc_offer_create: function(p_id, p_obj, p_on_session, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const onsession = GodotOS.get_func(p_on_session).bind(null, p_obj);
+ const onerror = GodotOS.get_func(p_on_error).bind(null, p_obj);
+ ref.createOffer().then(function(session) {
+ GodotRTCPeerConnection.onsession(p_id, onsession, session);
+ }).catch(function(error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_local_description_set: function(p_id, p_type, p_sdp, p_obj, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const type = UTF8ToString(p_type);
+ const sdp = UTF8ToString(p_sdp);
+ const onerror = GodotOS.get_func(p_on_error).bind(null, p_obj);
+ ref.setLocalDescription({
+ 'sdp': sdp,
+ 'type': type
+ }).catch(function(error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_remote_description_set: function(p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const type = UTF8ToString(p_type);
+ const sdp = UTF8ToString(p_sdp);
+ const onerror = GodotOS.get_func(p_on_error).bind(null, p_obj);
+ const onsession = GodotOS.get_func(p_session_created).bind(null, p_obj);
+ ref.setRemoteDescription({
+ 'sdp': sdp,
+ 'type': type
+ }).then(function() {
+ if (type != 'offer') {
+ return;
+ }
+ return ref.createAnswer().then(function(session) {
+ GodotRTCPeerConnection.onsession(p_id, onsession, session);
+ });
+ }).catch(function(error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_ice_candidate_add: function(p_id, p_mid_name, p_mline_idx, p_sdp) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ var sdpMidName = UTF8ToString(p_mid_name);
+ var sdpName = UTF8ToString(p_sdp);
+ ref.addIceCandidate(new RTCIceCandidate({
+ "candidate": sdpName,
+ "sdpMid": sdpMidName,
+ "sdpMlineIndex": p_mline_idx,
+ }));
+ },
+
+ godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
+ godot_js_rtc_pc_datachannel_create: function(p_id, p_label, p_config) {
+ try {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 0;
+ }
+
+ const label = UTF8ToString(p_label);
+ const config = JSON.parse(UTF8ToString(p_config));
+
+ const channel = ref.createDataChannel(label, config);
+ return IDHandler.add(channel);
+ } catch (e) {
+ console.error(e);
+ return 0;
+ }
+ },
+};
+
+autoAddDeps(GodotRTCPeerConnection, '$GodotRTCPeerConnection')
+mergeInto(LibraryManager.library, GodotRTCPeerConnection);
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 5b296b1ac6..0e830f0540 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "webrtc_data_channel.h"
#include "webrtc_peer_connection.h"
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
index 7566532982..cd9e77aff8 100644
--- a/modules/webrtc/webrtc_data_channel.cpp
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "webrtc_data_channel.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
void WebRTCDataChannel::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll);
diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h
index b578802250..03396d207d 100644
--- a/modules/webrtc/webrtc_data_channel_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_gdnative.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H
#define WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_data_channel.h"
@@ -75,6 +75,6 @@ public:
~WebRTCDataChannelGDNative();
};
-#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
-
#endif // WEBRTC_GDNATIVE_ENABLED
+
+#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
index 2c648ba9f9..3a63001a56 100644
--- a/modules/webrtc/webrtc_data_channel_js.cpp
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -34,65 +34,58 @@
#include "emscripten.h"
extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_error();
-}
+typedef void (*RTCChOnOpen)(void *p_obj);
+typedef void (*RTCChOnMessage)(void *p_obj, const uint8_t *p_buffer, int p_size, int p_is_string);
+typedef void (*RTCChOnClose)(void *p_obj);
+typedef void (*RTCChOnError)(void *p_obj);
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_open();
+extern int godot_js_rtc_datachannel_ready_state_get(int p_id);
+extern int godot_js_rtc_datachannel_send(int p_id, const uint8_t *p_buffer, int p_length, int p_raw);
+extern int godot_js_rtc_datachannel_is_ordered(int p_id);
+extern int godot_js_rtc_datachannel_id_get(int p_id);
+extern int godot_js_rtc_datachannel_max_packet_lifetime_get(int p_id);
+extern int godot_js_rtc_datachannel_max_retransmits_get(int p_id);
+extern int godot_js_rtc_datachannel_is_negotiated(int p_id);
+extern char *godot_js_rtc_datachannel_label_get(int p_id); // Must free the returned string.
+extern char *godot_js_rtc_datachannel_protocol_get(int p_id); // Must free the returned string.
+extern void godot_js_rtc_datachannel_destroy(int p_id);
+extern void godot_js_rtc_datachannel_connect(int p_id, void *p_obj, RTCChOnOpen p_on_open, RTCChOnMessage p_on_message, RTCChOnError p_on_error, RTCChOnClose p_on_close);
+extern void godot_js_rtc_datachannel_close(int p_id);
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_close();
+void WebRTCDataChannelJS::_on_open(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->in_buffer.resize(peer->_in_buffer_shift);
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_message(p_data, p_size, p_is_string);
-}
+void WebRTCDataChannelJS::_on_close(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->close();
}
-void WebRTCDataChannelJS::_on_open() {
- in_buffer.resize(_in_buffer_shift);
+void WebRTCDataChannelJS::_on_error(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->close();
}
-void WebRTCDataChannelJS::_on_close() {
- close();
-}
+void WebRTCDataChannelJS::_on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ RingBuffer<uint8_t> &in_buffer = peer->in_buffer;
-void WebRTCDataChannelJS::_on_error() {
- close();
-}
-
-void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
ERR_FAIL_COND_MSG(in_buffer.space_left() < (int)(p_size + 5), "Buffer full! Dropping data.");
uint8_t is_string = p_is_string ? 1 : 0;
in_buffer.write((uint8_t *)&p_size, 4);
in_buffer.write((uint8_t *)&is_string, 1);
in_buffer.write(p_data, p_size);
- queue_count++;
+ peer->queue_count++;
}
void WebRTCDataChannelJS::close() {
in_buffer.resize(0);
queue_count = 0;
_was_string = false;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- var channel = dict["channel"];
- channel.onopen = null;
- channel.onclose = null;
- channel.onerror = null;
- channel.onmessage = null;
- channel.close();
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_datachannel_close(_js_id);
}
Error WebRTCDataChannelJS::poll() {
@@ -100,24 +93,7 @@ Error WebRTCDataChannelJS::poll() {
}
WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const {
- /* clang-format off */
- return (ChannelState) EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict) return 3; // CLOSED
- var channel = dict["channel"];
- switch(channel.readyState) {
- case "connecting":
- return 0;
- case "open":
- return 1;
- case "closing":
- return 2;
- case "closed":
- return 3;
- }
- return 3; // CLOSED
- }, _js_id);
- /* clang-format on */
+ return (ChannelState)godot_js_rtc_datachannel_ready_state_get(_js_id);
}
int WebRTCDataChannelJS::get_available_packet_count() const {
@@ -157,27 +133,7 @@ Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size
ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0;
-
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- if ($3) {
- channel.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- channel.send(string);
- }
- }, _js_id, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
-
+ godot_js_rtc_datachannel_send(_js_id, p_buffer, p_buffer_size, is_bin);
return OK;
}
@@ -201,46 +157,20 @@ String WebRTCDataChannelJS::get_label() const {
return _label;
}
-/* clang-format off */
-#define _JS_GET(PROP, DEF) \
-EM_ASM_INT({ \
- var dict = Module.IDHandler.get($0); \
- if (!dict || !dict["channel"]) { \
- return DEF; \
- } \
- var out = dict["channel"].PROP; \
- return out === null ? DEF : out; \
-}, _js_id)
-/* clang-format on */
-
bool WebRTCDataChannelJS::is_ordered() const {
- return _JS_GET(ordered, true);
+ return godot_js_rtc_datachannel_is_ordered(_js_id);
}
int WebRTCDataChannelJS::get_id() const {
- return _JS_GET(id, 65535);
+ return godot_js_rtc_datachannel_id_get(_js_id);
}
int WebRTCDataChannelJS::get_max_packet_life_time() const {
- // Can't use macro, webkit workaround.
- /* clang-format off */
- return EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) {
- return 65535;
- }
- if (dict["channel"].maxRetransmitTime !== undefined) {
- // Guess someone didn't appreciate the standardization process.
- return dict["channel"].maxRetransmitTime;
- }
- var out = dict["channel"].maxPacketLifeTime;
- return out === null ? 65535 : out;
- }, _js_id);
- /* clang-format on */
+ return godot_js_rtc_datachannel_max_packet_lifetime_get(_js_id);
}
int WebRTCDataChannelJS::get_max_retransmits() const {
- return _JS_GET(maxRetransmits, 65535);
+ return godot_js_rtc_datachannel_max_retransmits_get(_js_id);
}
String WebRTCDataChannelJS::get_protocol() const {
@@ -248,7 +178,7 @@ String WebRTCDataChannelJS::get_protocol() const {
}
bool WebRTCDataChannelJS::is_negotiated() const {
- return _JS_GET(negotiated, false);
+ return godot_js_rtc_datachannel_is_negotiated(_js_id);
}
WebRTCDataChannelJS::WebRTCDataChannelJS() {
@@ -264,101 +194,22 @@ WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
_write_mode = WRITE_MODE_BINARY;
_js_id = js_id;
- /* clang-format off */
- EM_ASM({
- var c_ptr = $0;
- var dict = Module.IDHandler.get($1);
- if (!dict) return;
- var channel = dict["channel"];
- dict["ptr"] = c_ptr;
-
- channel.binaryType = "arraybuffer";
- channel.onopen = function (evt) {
- ccall("_emrtc_on_ch_open",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onclose = function (evt) {
- ccall("_emrtc_on_ch_close",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onerror = function (evt) {
- ccall("_emrtc_on_ch_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onmessage = function(event) {
- var buffer;
- var is_string = 0;
- if (event.data instanceof ArrayBuffer) {
- buffer = new Uint8Array(event.data);
- } else if (event.data instanceof Blob) {
- console.error("Blob type not supported");
- return;
- } else if (typeof event.data === "string") {
- is_string = 1;
- var enc = new TextEncoder("utf-8");
- buffer = new Uint8Array(enc.encode(event.data));
- } else {
- console.error("Unknown message type");
- return;
- }
- var len = buffer.length*buffer.BYTES_PER_ELEMENT;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_emrtc_on_ch_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- _free(out);
- }
-
- }, this, js_id);
+ godot_js_rtc_datachannel_connect(js_id, this, &_on_open, &_on_message, &_on_error, &_on_close);
// Parse label
- char *str;
- str = (char *)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) return 0;
- var str = dict["channel"].label;
- var len = lengthBytesUTF8(str)+1;
- var ptr = _malloc(str);
- stringToUTF8(str, ptr, len+1);
- return ptr;
- }, js_id);
- if(str != nullptr) {
- _label.parse_utf8(str);
- EM_ASM({ _free($0) }, str);
+ char *label = godot_js_rtc_datachannel_label_get(js_id);
+ if (label) {
+ _label.parse_utf8(label);
+ free(label);
}
- str = (char *)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) return 0;
- var str = dict["channel"].protocol;
- var len = lengthBytesUTF8(str)+1;
- var ptr = _malloc(str);
- stringToUTF8(str, ptr, len+1);
- return ptr;
- }, js_id);
- if(str != nullptr) {
- _protocol.parse_utf8(str);
- EM_ASM({ _free($0) }, str);
+ char *protocol = godot_js_rtc_datachannel_protocol_get(js_id);
+ if (protocol) {
+ _protocol.parse_utf8(protocol);
+ free(protocol);
}
- /* clang-format on */
}
WebRTCDataChannelJS::~WebRTCDataChannelJS() {
close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
+ godot_js_rtc_datachannel_destroy(_js_id);
+}
#endif
diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h
index 455866cbf1..e251760019 100644
--- a/modules/webrtc/webrtc_data_channel_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef JAVASCRIPT_ENABLED
-
#ifndef WEBRTC_DATA_CHANNEL_JS_H
#define WEBRTC_DATA_CHANNEL_JS_H
+#ifdef JAVASCRIPT_ENABLED
+
#include "webrtc_data_channel.h"
class WebRTCDataChannelJS : public WebRTCDataChannel {
@@ -54,12 +54,12 @@ private:
int queue_count;
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
-public:
- void _on_open();
- void _on_close();
- void _on_error();
- void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ static void _on_open(void *p_obj);
+ static void _on_close(void *p_obj);
+ static void _on_error(void *p_obj);
+ static void _on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string);
+public:
virtual void set_write_mode(WriteMode mode) override;
virtual WriteMode get_write_mode() const override;
virtual bool was_string_packet() const override;
@@ -88,6 +88,6 @@ public:
~WebRTCDataChannelJS();
};
-#endif // WEBRTC_DATA_CHANNEL_JS_H
-
#endif // JAVASCRIPT_ENABLED
+
+#endif // WEBRTC_DATA_CHANNEL_JS_H
diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h
index bfdcf6daa1..fb37bd7722 100644
--- a/modules/webrtc/webrtc_multiplayer.h
+++ b/modules/webrtc/webrtc_multiplayer.h
@@ -112,4 +112,4 @@ public:
ConnectionStatus get_connection_status() const override;
};
-#endif
+#endif // WEBRTC_MULTIPLAYER_H
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h
index 74b7db1307..846b65c466 100644
--- a/modules/webrtc/webrtc_peer_connection_gdnative.h
+++ b/modules/webrtc/webrtc_peer_connection_gdnative.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H
#define WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#ifdef WEBRTC_GDNATIVE_ENABLED
+
#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_peer_connection.h"
@@ -68,6 +68,6 @@ public:
~WebRTCPeerConnectionGDNative();
};
-#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
-
#endif // WEBRTC_GDNATIVE_ENABLED
+
+#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index 593c3a5162..ad9b46a8af 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -37,116 +37,32 @@
#include "core/io/json.h"
#include "emscripten.h"
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName));
+void WebRTCPeerConnectionJS::_on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->emit_signal("ice_candidate_created", String(p_mid_name), p_mline_idx, String(p_candidate));
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->emit_signal("session_description_created", String(p_type), String(p_offer));
+void WebRTCPeerConnectionJS::_on_session_created(void *p_obj, const char *p_type, const char *p_session) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->emit_signal("session_description_created", String(p_type), String(p_session));
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->_on_connection_state_changed();
+void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_conn_state = (ConnectionState)p_state;
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() {
+void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
ERR_PRINT("RTCPeerConnection error!");
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
+void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
}
-}
-
-void _emrtc_create_pc(int p_id, const Dictionary &p_config) {
- String config = JSON::print(p_config);
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var c_ptr = dict["ptr"];
- var config = JSON.parse(UTF8ToString($1));
- // Setup local connaction
- var conn = null;
- try {
- conn = new RTCPeerConnection(config);
- } catch (e) {
- console.log(e);
- return;
- }
- conn.oniceconnectionstatechange = function(event) {
- if (!Module.IDHandler.get($0)) return;
- ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]);
- };
- conn.onicecandidate = function(event) {
- if (!Module.IDHandler.get($0)) return;
- if (!event.candidate) return;
-
- var c = event.candidate;
- // should emit on ice candidate
- ccall("_emrtc_on_ice_candidate",
- "void",
- ["number", "string", "number", "string"],
- [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
- );
- };
- conn.ondatachannel = function (evt) {
- var dict = Module.IDHandler.get($0);
- if (!dict) {
- return;
- }
- var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null});
- ccall("_emrtc_emit_channel",
- "void",
- ["number", "number"],
- [c_ptr, id]
- );
- };
- dict["conn"] = conn;
- }, p_id, config.utf8().get_data());
- /* clang-format on */
-}
-
-void WebRTCPeerConnectionJS::_on_connection_state_changed() {
- /* clang-format off */
- _conn_state = (ConnectionState)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict) return 5; // CLOSED
- var conn = dict["conn"];
- switch(conn.iceConnectionState) {
- case "new":
- return 0;
- case "checking":
- return 1;
- case "connected":
- case "completed":
- return 2;
- case "disconnected":
- return 3;
- case "failed":
- return 4;
- case "closed":
- return 5;
- }
- return 5; // CLOSED
- }, _js_id);
- /* clang-format on */
-}
void WebRTCPeerConnectionJS::close() {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- if (dict["conn"]) {
- dict["conn"].close();
- }
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_pc_close(_js_id);
_conn_state = STATE_CLOSED;
}
@@ -154,46 +70,12 @@ Error WebRTCPeerConnectionJS::create_offer() {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- var onCreated = function(offer) {
- ccall("_emrtc_session_description_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- conn.createOffer().then(onCreated).catch(onError);
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_pc_offer_create(_js_id, this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- conn.setLocalDescription({
- "sdp": sdp,
- "type": type
- }).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_local_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_error);
return OK;
}
@@ -202,83 +84,32 @@ Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
}
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
-
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- var onCreated = function(offer) {
- ccall("_emrtc_session_description_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- var onSet = function() {
- if (type != "offer") {
- return;
- }
- conn.createAnswer().then(onCreated);
- };
- conn.setRemoteDescription({
- "sdp": sdp,
- "type": type
- }).then(onSet).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_remote_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var sdpMidName = UTF8ToString($1);
- var sdpMlineIndexName = UTF8ToString($2);
- var sdpName = UTF8ToString($3);
- conn.addIceCandidate(new RTCIceCandidate({
- "candidate": sdpName,
- "sdpMid": sdpMidName,
- "sdpMlineIndex": sdpMlineIndexName
- }));
- }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_ice_candidate_add(_js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
return OK;
}
Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
- _emrtc_create_pc(_js_id, p_config);
- return OK;
+ if (_js_id) {
+ godot_js_rtc_pc_destroy(_js_id);
+ _js_id = 0;
+ }
+ _conn_state = STATE_NEW;
+
+ String config = JSON::print(p_config);
+ _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_ice_candidate, &_on_data_channel);
+ return _js_id ? OK : FAILED;
}
Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) {
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, nullptr);
+
String config = JSON::print(p_channel_config);
- /* clang-format off */
- int id = EM_ASM_INT({
- try {
- var dict = Module.IDHandler.get($0);
- if (!dict) return 0;
- var label = UTF8ToString($1);
- var config = JSON.parse(UTF8ToString($2));
- var conn = dict["conn"];
- return Module.IDHandler.add({
- "channel": conn.createDataChannel(label, config),
- "ptr": null
- })
- } catch (e) {
- return 0;
- }
- }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data());
- /* clang-format on */
+ int id = godot_js_rtc_pc_datachannel_create(_js_id, p_channel.utf8().get_data(), config.utf8().get_data());
ERR_FAIL_COND_V(id == 0, nullptr);
return memnew(WebRTCDataChannelJS(id));
}
@@ -293,22 +124,17 @@ WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_sta
WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
_conn_state = STATE_NEW;
+ _js_id = 0;
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add({"conn": null, "ptr": $0});
- }, this);
- /* clang-format on */
Dictionary config;
- _emrtc_create_pc(_js_id, config);
+ initialize(config);
}
WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() {
close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
+ if (_js_id) {
+ godot_js_rtc_pc_destroy(_js_id);
+ _js_id = 0;
+ }
};
#endif
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index cdaf3068e3..e33dd5f259 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -35,16 +35,37 @@
#include "webrtc_peer_connection.h"
+extern "C" {
+typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
+typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
+typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
+typedef void (*RTCOnError)(void *p_obj);
+extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
+extern void godot_js_rtc_pc_close(int p_id);
+extern void godot_js_rtc_pc_destroy(int p_id);
+extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_local_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_remote_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_ice_candidate_add(int p_id, const char *p_mid_name, int p_mline_idx, const char *p_sdo);
+extern int godot_js_rtc_pc_datachannel_create(int p_id, const char *p_label, const char *p_config);
+}
+
class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
private:
int _js_id;
ConnectionState _conn_state;
+ static void _on_connection_state_changed(void *p_obj, int p_state);
+ static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
+ static void _on_data_channel(void *p_obj, int p_channel);
+ static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
+ static void _on_error(void *p_obj);
+
public:
static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); }
static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; }
- void _on_connection_state_changed();
virtual ConnectionState get_connection_state() const;
virtual Error initialize(Dictionary configuration = Dictionary());
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index af60055855..13e51a39c0 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -3,11 +3,13 @@
Import("env")
Import("env_modules")
-# Thirdparty source files
-
env_ws = env_modules.Clone()
-if env["builtin_wslay"] and not env["platform"] == "javascript": # already builtin for javascript
+if env["platform"] == "javascript":
+ # Our JavaScript/C++ interface.
+ env.AddJSLibraries(["library_godot_websocket.js"])
+elif env["builtin_wslay"]:
+ # Thirdparty source files
wslay_dir = "#thirdparty/wslay/"
wslay_sources = [
"wslay_net.c",
diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor_debugger_server_websocket.cpp
index 95ea7ceafa..8eb975b323 100644
--- a/modules/websocket/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor_debugger_server_websocket.cpp
@@ -30,7 +30,7 @@
#include "editor_debugger_server_websocket.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_settings.h"
#include "modules/websocket/remote_debugger_peer_websocket.h"
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 7c31449709..d6e00a26af 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -31,18 +31,17 @@
#ifdef JAVASCRIPT_ENABLED
#include "emws_client.h"
+#include "core/config/project_settings.h"
#include "core/io/ip.h"
-#include "core/project_settings.h"
#include "emscripten.h"
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
+void EMWSClient::_esws_on_connect(void *obj, char *proto) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_connect(String(proto));
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
+void EMWSClient::_esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
@@ -50,21 +49,26 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_dat
client->_on_peer_packet();
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
+void EMWSClient::_esws_on_error(void *obj) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_error();
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) {
+void EMWSClient::_esws_on_close(void *obj, int code, const char *reason, int was_clean) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_on_close_request(code, String(reason));
client->_is_connecting = false;
+ client->disconnect_from_host();
client->_on_disconnect(was_clean != 0);
}
-}
Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
+ if (_js_id) {
+ godot_js_websocket_destroy(_js_id);
+ _js_id = 0;
+ }
+
String proto_string;
for (int i = 0; i < p_protocols.size(); i++) {
if (i != 0)
@@ -84,106 +88,17 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
}
str += p_host + ":" + itos(p_port) + p_path;
-
_is_connecting = true;
- /* clang-format off */
- int peer_sock = EM_ASM_INT({
- var proto_str = UTF8ToString($2);
- var socket = null;
- try {
- if (proto_str) {
- socket = new WebSocket(UTF8ToString($1), proto_str.split(","));
- } else {
- socket = new WebSocket(UTF8ToString($1));
- }
- } catch (e) {
- return -1;
- }
- var c_ptr = Module.IDHandler.get($0);
- socket.binaryType = "arraybuffer";
-
- // Connection opened
- socket.addEventListener("open", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- ccall("_esws_on_connect",
- "void",
- ["number", "string"],
- [c_ptr, socket.protocol]
- );
- });
-
- // Listen for messages
- socket.addEventListener("message", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- var buffer;
- var is_string = 0;
- if (event.data instanceof ArrayBuffer) {
-
- buffer = new Uint8Array(event.data);
-
- } else if (event.data instanceof Blob) {
-
- alert("Blob type not supported");
- return;
-
- } else if (typeof event.data === "string") {
-
- is_string = 1;
- var enc = new TextEncoder("utf-8");
- buffer = new Uint8Array(enc.encode(event.data));
-
- } else {
-
- alert("Unknown message type");
- return;
-
- }
- var len = buffer.length*buffer.BYTES_PER_ELEMENT;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_esws_on_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- _free(out);
- });
-
- socket.addEventListener("error", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- ccall("_esws_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- });
-
- socket.addEventListener("close", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- var was_clean = 0;
- if (event.wasClean)
- was_clean = 1;
- ccall("_esws_on_close",
- "void",
- ["number", "number", "string", "number"],
- [c_ptr, event.code, event.reason, was_clean]
- );
- });
-
- return Module.IDHandler.add(socket);
- }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
- /* clang-format on */
- if (peer_sock == -1)
+
+ _js_id = godot_js_websocket_create(this, str.utf8().get_data(), proto_string.utf8().get_data(), &_esws_on_connect, &_esws_on_message, &_esws_on_error, &_esws_on_close);
+ if (!_js_id) {
return FAILED;
+ }
- static_cast<Ref<EMWSPeer>>(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size);
+ static_cast<Ref<EMWSPeer>>(_peer)->set_sock(_js_id, _in_buf_size, _in_pkt_size);
return OK;
-};
+}
void EMWSClient::poll() {
}
@@ -200,19 +115,19 @@ NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() c
}
return CONNECTION_DISCONNECTED;
-};
+}
void EMWSClient::disconnect_from_host(int p_code, String p_reason) {
_peer->close(p_code, p_reason);
-};
+}
IP_Address EMWSClient::get_connected_host() const {
ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
-};
+}
uint16_t EMWSClient::get_connected_port() const {
ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
-};
+}
int EMWSClient::get_max_packet_size() const {
return (1 << _in_buf_size) - PROTO_SIZE;
@@ -227,24 +142,17 @@ Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffe
EMWSClient::EMWSClient() {
_in_buf_size = DEF_BUF_SHIFT;
_in_pkt_size = DEF_PKT_SHIFT;
-
_is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add($0);
- }, this);
- /* clang-format on */
-};
+ _js_id = 0;
+}
EMWSClient::~EMWSClient() {
disconnect_from_host();
_peer = Ref<EMWSPeer>();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
+ if (_js_id) {
+ godot_js_websocket_destroy(_js_id);
+ }
+}
#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index ab8a0612bb..0123c37457 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -33,7 +33,7 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "emws_peer.h"
#include "websocket_client.h"
@@ -41,13 +41,17 @@ class EMWSClient : public WebSocketClient {
GDCIIMPL(EMWSClient, WebSocketClient);
private:
+ int _js_id;
+ bool _is_connecting;
int _in_buf_size;
int _in_pkt_size;
- int _js_id;
-public:
- bool _is_connecting;
+ static void _esws_on_connect(void *obj, char *proto);
+ static void _esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string);
+ static void _esws_on_error(void *obj);
+ static void _esws_on_close(void *obj, int code, const char *reason, int was_clean);
+public:
Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>());
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 749f45451a..5dcfba5567 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -47,38 +47,14 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
return write_mode;
}
-Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string) {
uint8_t is_string = p_is_string ? 1 : 0;
return _in_buffer.write_packet(p_data, p_size, &is_string);
}
Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0;
-
- /* clang-format off */
- EM_ASM({
- var sock = Module.IDHandler.get($0);
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- try {
- if ($3) {
- sock.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- sock.send(string);
- }
- } catch (e) {
- return 1;
- }
- return 0;
- }, peer_sock, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
-
+ godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin);
return OK;
};
@@ -110,15 +86,7 @@ bool EMWSPeer::is_connected_to_host() const {
void EMWSPeer::close(int p_code, String p_reason) {
if (peer_sock != -1) {
- /* clang-format off */
- EM_ASM({
- var sock = Module.IDHandler.get($0);
- var code = $1;
- var reason = UTF8ToString($2);
- sock.close(code, reason);
- Module.IDHandler.remove($0);
- }, peer_sock, p_code, p_reason.utf8().get_data());
- /* clang-format on */
+ godot_js_websocket_close(peer_sock, p_code, p_reason.utf8().get_data());
}
_is_string = 0;
_in_buffer.clear();
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index 9c00f2d58b..2291a32bbc 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -33,13 +33,25 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
#include "emscripten.h"
#include "packet_buffer.h"
#include "websocket_peer.h"
+extern "C" {
+typedef void (*WSOnOpen)(void *p_ref, char *p_protocol);
+typedef void (*WSOnMessage)(void *p_ref, const uint8_t *p_buf, int p_buf_len, int p_is_string);
+typedef void (*WSOnClose)(void *p_ref, int p_code, const char *p_reason, int p_is_clean);
+typedef void (*WSOnError)(void *p_ref);
+
+extern int godot_js_websocket_create(void *p_ref, const char *p_url, const char *p_proto, WSOnOpen p_on_open, WSOnMessage p_on_message, WSOnError p_on_error, WSOnClose p_on_close);
+extern int godot_js_websocket_send(int p_id, const uint8_t *p_buf, int p_buf_len, int p_raw);
+extern void godot_js_websocket_close(int p_id, int p_code, const char *p_reason);
+extern void godot_js_websocket_destroy(int p_id);
+}
+
class EMWSPeer : public WebSocketPeer {
GDCIIMPL(EMWSPeer, WebSocketPeer);
@@ -52,7 +64,7 @@ private:
uint8_t _is_string;
public:
- Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ Error read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string);
void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size);
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index bb6f35a711..1ce17855fe 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -33,7 +33,7 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/reference.h"
+#include "core/object/reference.h"
#include "emws_peer.h"
#include "websocket_server.h"
diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js
new file mode 100644
index 0000000000..f7d3195807
--- /dev/null
+++ b/modules/websocket/library_godot_websocket.js
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* library_godot_websocket.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+
+var GodotWebSocket = {
+
+ // Our socket implementation that forwards events to C++.
+ $GodotWebSocket__deps: ['$IDHandler'],
+ $GodotWebSocket: {
+ // Connection opened, report selected protocol
+ _onopen: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ let c_str = GodotOS.allocString(ref.protocol);
+ callback(c_str);
+ _free(c_str);
+ },
+
+ // Message received, report content and type (UTF8 vs binary)
+ _onmessage: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ var buffer;
+ var is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ alert("Blob type not supported");
+ return;
+ } else if (typeof event.data === "string") {
+ is_string = 1;
+ var enc = new TextEncoder("utf-8");
+ buffer = new Uint8Array(enc.encode(event.data));
+ } else {
+ alert("Unknown message type");
+ return;
+ }
+ var len = buffer.length*buffer.BYTES_PER_ELEMENT;
+ var out = _malloc(len);
+ HEAPU8.set(buffer, out);
+ callback(out, len, is_string);
+ _free(out);
+ },
+
+ // An error happened, 'onclose' will be called after this.
+ _onerror: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ callback();
+ },
+
+ // Connection is closed, this is always fired. Report close code, reason, and clean status.
+ _onclose: function(p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ let c_str = GodotOS.allocString(event.reason);
+ callback(event.code, c_str, event.wasClean ? 1 : 0);
+ _free(c_str);
+ },
+
+ // Send a message
+ send: function(p_id, p_data) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || ref.readyState != ref.OPEN) {
+ return 1; // Godot object is gone or socket is not in a ready state.
+ }
+ ref.send(p_data);
+ return 0;
+ },
+
+ create: function(socket, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const id = IDHandler.add(socket);
+ socket.onopen = GodotWebSocket._onopen.bind(null, id, p_on_open);
+ socket.onmessage = GodotWebSocket._onmessage.bind(null, id, p_on_message);
+ socket.onerror = GodotWebSocket._onerror.bind(null, id, p_on_error);
+ socket.onclose = GodotWebSocket._onclose.bind(null, id, p_on_close);
+ return id;
+ },
+
+ // Closes the JavaScript WebSocket (if not already closing) associated to a given C++ object.
+ close: function(p_id, p_code, p_reason) {
+ const ref = IDHandler.get(p_id);
+ if (ref && ref.readyState < ref.CLOSING) {
+ const code = p_code;
+ const reason = UTF8ToString(p_reason);
+ ref.close(code, reason);
+ }
+ },
+
+ // Deletes the reference to a C++ object (closing any connected socket if necessary).
+ destroy: function(p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ GodotWebSocket.close(p_id, 1001, '');
+ IDHandler.remove(p_id);
+ ref.onopen = null;
+ ref.onmessage = null;
+ ref.onerror = null;
+ ref.onclose = null;
+ },
+ },
+
+ godot_js_websocket_create: function(p_ref, p_url, p_proto, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const on_open = GodotOS.get_func(p_on_open).bind(null, p_ref);
+ const on_message = GodotOS.get_func(p_on_message).bind(null, p_ref);
+ const on_error = GodotOS.get_func(p_on_error).bind(null, p_ref);
+ const on_close = GodotOS.get_func(p_on_close).bind(null, p_ref);
+ const url = UTF8ToString(p_url);
+ const protos = UTF8ToString(p_proto);
+ var socket = null;
+ try {
+ if (protos) {
+ socket = new WebSocket(url, protos.split(","));
+ } else {
+ socket = new WebSocket(url);
+ }
+ } catch (e) {
+ return 0;
+ }
+ socket.binaryType = "arraybuffer";
+ return GodotWebSocket.create(socket, on_open, on_message, on_error, on_close);
+ },
+
+ godot_js_websocket_send: function(p_id, p_buf, p_buf_len, p_raw) {
+ var bytes_array = new Uint8Array(p_buf_len);
+ var i = 0;
+ for(i = 0; i < p_buf_len; i++) {
+ bytes_array[i] = getValue(p_buf + i, 'i8');
+ }
+ var out = bytes_array;
+ if (p_raw) {
+ out = bytes_array.buffer;
+ } else {
+ out = new TextDecoder("utf-8").decode(bytes_array);
+ }
+ return GodotWebSocket.send(p_id, out);
+ },
+
+ godot_js_websocket_close: function(p_id, p_code, p_reason) {
+ const code = p_code;
+ const reason = UTF8ToString(p_reason);
+ GodotWebSocket.close(p_id, code, reason);
+ },
+
+ godot_js_websocket_destroy: function(p_id) {
+ GodotWebSocket.destroy(p_id);
+ },
+};
+
+autoAddDeps(GodotWebSocket, '$GodotWebSocket')
+mergeInto(LibraryManager.library, GodotWebSocket);
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
index 9973efe297..18b47b8d50 100644
--- a/modules/websocket/packet_buffer.h
+++ b/modules/websocket/packet_buffer.h
@@ -32,7 +32,7 @@
#define PACKET_BUFFER_H
#include "core/os/copymem.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
template <class T>
class PacketBuffer {
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index bc50de414e..8979a09619 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/error_macros.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/error/error_macros.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp
index a67a959e31..9a72e460e2 100644
--- a/modules/websocket/remote_debugger_peer_websocket.cpp
+++ b/modules/websocket/remote_debugger_peer_websocket.cpp
@@ -30,7 +30,7 @@
#include "remote_debugger_peer_websocket.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
Error RemoteDebuggerPeerWebSocket::connect_to_host(const String &p_uri) {
Vector<String> protocols;
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 1053be22e3..2966dc480b 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -32,7 +32,7 @@
#define WEBSOCKET_CLIENT_H
#include "core/crypto/crypto.h"
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index af26aef21e..54daae23a6 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -31,9 +31,9 @@
#ifndef WEBSOCKET_MULTIPLAYER_PEER_H
#define WEBSOCKET_MULTIPLAYER_PEER_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/networked_multiplayer_peer.h"
-#include "core/list.h"
+#include "core/templates/list.h"
#include "websocket_peer.h"
class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer {
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index a61943b615..729fdfd340 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -31,7 +31,7 @@
#ifndef WEBSOCKETPEER_H
#define WEBSOCKETPEER_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
#include "websocket_macros.h"
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 064ad4f179..34ae52a1ee 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -32,7 +32,7 @@
#define WEBSOCKET_H
#include "core/crypto/crypto.h"
-#include "core/reference.h"
+#include "core/object/reference.h"
#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index 7d16c2e99f..a2b81438df 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -31,8 +31,8 @@
#ifndef JAVASCRIPT_ENABLED
#include "wsl_client.h"
+#include "core/config/project_settings.h"
#include "core/io/ip.h"
-#include "core/project_settings.h"
void WSLClient::_do_handshake() {
if (_requested < _request.size() - 1) {
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 8355a5a737..0141ea93ea 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -33,7 +33,7 @@
#ifndef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/stream_peer_ssl.h"
#include "core/io/stream_peer_tcp.h"
#include "websocket_client.h"
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index fe4abfb64c..35ac18615a 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -33,10 +33,10 @@
#ifndef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
#include "core/io/stream_peer_tcp.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
#include "packet_buffer.h"
#include "websocket_peer.h"
#include "wslay/wslay.h"
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index da7bfc70c0..9a05967e4e 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -31,8 +31,8 @@
#ifndef JAVASCRIPT_ENABLED
#include "wsl_server.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
WSLServer::PendingPeer::PendingPeer() {
use_ssl = false;
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index 6242009f67..65350518c3 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "core/crypto/crypto_core.h"