summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/basis_universal/register_types.cpp12
-rw-r--r--modules/basis_universal/register_types.h6
-rw-r--r--modules/bmp/register_types.cpp12
-rw-r--r--modules/bmp/register_types.h6
-rw-r--r--modules/camera/SCsub4
-rw-r--r--modules/camera/camera_macos.h (renamed from modules/camera/camera_osx.h)12
-rw-r--r--modules/camera/camera_macos.mm (renamed from modules/camera/camera_osx.mm)36
-rw-r--r--modules/camera/camera_win.h6
-rw-r--r--modules/camera/config.py2
-rw-r--r--modules/camera/register_types.cpp19
-rw-r--r--modules/camera/register_types.h6
-rw-r--r--modules/csg/csg.cpp25
-rw-r--r--modules/csg/csg.h16
-rw-r--r--modules/csg/csg_shape.cpp62
-rw-r--r--modules/csg/csg_shape.h12
-rw-r--r--modules/csg/doc_classes/CSGPrimitive3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGSphere3D.xml2
-rw-r--r--modules/csg/editor/csg_gizmos.cpp8
-rw-r--r--modules/csg/register_types.cpp32
-rw-r--r--modules/csg/register_types.h6
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp42
-rw-r--r--modules/cvtt/register_types.cpp12
-rw-r--r--modules/cvtt/register_types.h6
-rw-r--r--modules/dds/register_types.cpp12
-rw-r--r--modules/dds/register_types.h6
-rw-r--r--modules/dds/texture_loader_dds.cpp26
-rw-r--r--modules/dds/texture_loader_dds.h2
-rw-r--r--modules/denoise/config.py2
-rw-r--r--modules/denoise/register_types.cpp11
-rw-r--r--modules/denoise/register_types.h6
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp6
-rw-r--r--modules/enet/enet_multiplayer_peer.h12
-rw-r--r--modules/enet/register_types.cpp12
-rw-r--r--modules/enet/register_types.h6
-rw-r--r--modules/etcpak/register_types.cpp11
-rw-r--r--modules/etcpak/register_types.h8
-rw-r--r--modules/freetype/SCsub3
-rw-r--r--modules/freetype/register_types.cpp12
-rw-r--r--modules/freetype/register_types.h6
-rw-r--r--modules/freetype/uwpdef.h5
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml188
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp55
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h6
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h8
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd9
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd7
-rw-r--r--modules/gdscript/gdscript.cpp225
-rw-r--r--modules/gdscript/gdscript.h89
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp127
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp34
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h52
-rw-r--r--modules/gdscript/gdscript_cache.cpp8
-rw-r--r--modules/gdscript/gdscript_cache.h4
-rw-r--r--modules/gdscript/gdscript_codegen.h11
-rw-r--r--modules/gdscript/gdscript_compiler.cpp296
-rw-r--r--modules/gdscript/gdscript_compiler.h14
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp14
-rw-r--r--modules/gdscript/gdscript_editor.cpp161
-rw-r--r--modules/gdscript/gdscript_function.cpp9
-rw-r--r--modules/gdscript/gdscript_function.h13
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp6
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h6
-rw-r--r--modules/gdscript/gdscript_parser.cpp639
-rw-r--r--modules/gdscript/gdscript_parser.h68
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp8
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h8
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp10
-rw-r--r--modules/gdscript/gdscript_tokenizer.h12
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp3
-rw-r--r--modules/gdscript/gdscript_vm.cpp105
-rw-r--r--modules/gdscript/gdscript_warning.cpp16
-rw-r--r--modules/gdscript/gdscript_warning.h16
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp46
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h4
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp32
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h2
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp34
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp82
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h10
-rw-r--r--modules/gdscript/language_server/godot_lsp.h (renamed from modules/gdscript/language_server/lsp.hpp)16
-rw-r--r--modules/gdscript/register_types.cpp74
-rw-r--r--modules/gdscript/register_types.h6
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp18
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd49
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out19
-rw-r--r--modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/if_after_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd43
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.out15
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd26
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_dictionary.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd16
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out7
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.out6
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp46
-rw-r--r--modules/glslang/register_types.cpp47
-rw-r--r--modules/glslang/register_types.h7
-rw-r--r--modules/gltf/README.md11
-rw-r--r--modules/gltf/SCsub2
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml69
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp14
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h4
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.cpp6
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.h4
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp3
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.h5
-rw-r--r--modules/gltf/extensions/gltf_light.cpp (renamed from modules/gltf/gltf_light.cpp)0
-rw-r--r--modules/gltf/extensions/gltf_light.h (renamed from modules/gltf/gltf_light.h)1
-rw-r--r--modules/gltf/extensions/gltf_spec_gloss.cpp (renamed from modules/gltf/gltf_spec_gloss.cpp)0
-rw-r--r--modules/gltf/extensions/gltf_spec_gloss.h (renamed from modules/gltf/gltf_spec_gloss.h)1
-rw-r--r--modules/gltf/gltf_defines.h87
-rw-r--r--modules/gltf/gltf_document.cpp382
-rw-r--r--modules/gltf/gltf_document.h103
-rw-r--r--modules/gltf/gltf_document_extension.cpp100
-rw-r--r--modules/gltf/gltf_document_extension.h37
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.cpp45
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.h17
-rw-r--r--modules/gltf/gltf_state.cpp81
-rw-r--r--modules/gltf/gltf_state.h56
-rw-r--r--modules/gltf/gltf_template_convert.h94
-rw-r--r--modules/gltf/register_types.cpp95
-rw-r--r--modules/gltf/register_types.h6
-rw-r--r--modules/gltf/structures/gltf_accessor.cpp (renamed from modules/gltf/gltf_accessor.cpp)6
-rw-r--r--modules/gltf/structures/gltf_accessor.h (renamed from modules/gltf/gltf_accessor.h)6
-rw-r--r--modules/gltf/structures/gltf_animation.cpp (renamed from modules/gltf/gltf_animation.cpp)2
-rw-r--r--modules/gltf/structures/gltf_animation.h (renamed from modules/gltf/gltf_animation.h)5
-rw-r--r--modules/gltf/structures/gltf_buffer_view.cpp (renamed from modules/gltf/gltf_buffer_view.cpp)2
-rw-r--r--modules/gltf/structures/gltf_buffer_view.h (renamed from modules/gltf/gltf_buffer_view.h)3
-rw-r--r--modules/gltf/structures/gltf_camera.cpp (renamed from modules/gltf/gltf_camera.cpp)0
-rw-r--r--modules/gltf/structures/gltf_camera.h (renamed from modules/gltf/gltf_camera.h)1
-rw-r--r--modules/gltf/structures/gltf_mesh.cpp (renamed from modules/gltf/gltf_mesh.cpp)0
-rw-r--r--modules/gltf/structures/gltf_mesh.h (renamed from modules/gltf/gltf_mesh.h)0
-rw-r--r--modules/gltf/structures/gltf_node.cpp (renamed from modules/gltf/gltf_node.cpp)0
-rw-r--r--modules/gltf/structures/gltf_node.h (renamed from modules/gltf/gltf_node.h)3
-rw-r--r--modules/gltf/structures/gltf_skeleton.cpp (renamed from modules/gltf/gltf_skeleton.cpp)13
-rw-r--r--modules/gltf/structures/gltf_skeleton.h (renamed from modules/gltf/gltf_skeleton.h)11
-rw-r--r--modules/gltf/structures/gltf_skin.cpp (renamed from modules/gltf/gltf_skin.cpp)23
-rw-r--r--modules/gltf/structures/gltf_skin.h (renamed from modules/gltf/gltf_skin.h)7
-rw-r--r--modules/gltf/structures/gltf_texture.cpp (renamed from modules/gltf/gltf_texture.cpp)0
-rw-r--r--modules/gltf/structures/gltf_texture.h (renamed from modules/gltf/gltf_texture.h)2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml17
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp17
-rw-r--r--modules/gridmap/grid_map.cpp111
-rw-r--r--modules/gridmap/grid_map.h31
-rw-r--r--modules/gridmap/register_types.cpp15
-rw-r--r--modules/gridmap/register_types.h6
-rw-r--r--modules/hdr/image_loader_hdr.h2
-rw-r--r--modules/hdr/register_types.cpp12
-rw-r--r--modules/hdr/register_types.h6
-rw-r--r--modules/jpg/SCsub1
-rw-r--r--modules/jpg/image_loader_jpegd.cpp56
-rw-r--r--modules/jpg/image_loader_jpegd.h6
-rw-r--r--modules/jpg/register_types.cpp12
-rw-r--r--modules/jpg/register_types.h6
-rw-r--r--modules/jsonrpc/jsonrpc.h8
-rw-r--r--modules/jsonrpc/register_types.cpp11
-rw-r--r--modules/jsonrpc/register_types.h6
-rw-r--r--modules/lightmapper_rd/SCsub4
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp69
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h41
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl3
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl86
-rw-r--r--modules/lightmapper_rd/register_types.cpp11
-rw-r--r--modules/lightmapper_rd/register_types.h8
-rw-r--r--modules/mbedtls/dtls_server_mbedtls.h6
-rw-r--r--modules/mbedtls/register_types.cpp12
-rw-r--r--modules/mbedtls/register_types.h6
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.h6
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.cpp4
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h7
-rw-r--r--modules/meshoptimizer/register_types.cpp12
-rw-r--r--modules/meshoptimizer/register_types.h8
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp75
-rw-r--r--modules/minimp3/audio_stream_mp3.h25
-rw-r--r--modules/minimp3/doc_classes/AudioStreamMP3.xml31
-rw-r--r--modules/minimp3/register_types.cpp11
-rw-r--r--modules/minimp3/register_types.h6
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp50
-rw-r--r--modules/minimp3/resource_importer_mp3.h10
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp18
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h2
-rw-r--r--modules/mobile_vr/register_types.cpp12
-rw-r--r--modules/mobile_vr/register_types.h6
-rw-r--r--modules/mono/SCsub2
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py2
-rw-r--r--modules/mono/build_scripts/mono_configure.py16
-rw-r--r--modules/mono/class_db_api_json.cpp41
-rw-r--r--modules/mono/config.py4
-rw-r--r--modules/mono/csharp_script.cpp137
-rw-r--r--modules/mono/csharp_script.h47
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs9
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs7
-rw-r--r--modules/mono/editor/bindings_generator.cpp203
-rw-r--r--modules/mono/editor/bindings_generator.h26
-rw-r--r--modules/mono/editor/code_completion.cpp6
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp16
-rw-r--r--modules/mono/editor/editor_internal_calls.h6
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs16
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs15
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs43
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs47
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs32
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs32
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj2
-rw-r--r--modules/mono/glue/base_object_glue.cpp6
-rw-r--r--modules/mono/glue/gd_glue.cpp22
-rw-r--r--modules/mono/glue/glue_header.h5
-rw-r--r--modules/mono/glue/nodepath_glue.cpp5
-rw-r--r--modules/mono/godotsharp_dirs.cpp4
-rw-r--r--modules/mono/managed_callable.cpp5
-rw-r--r--modules/mono/managed_callable.h2
-rw-r--r--modules/mono/mono_gc_handle.h6
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp33
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp22
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h11
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp39
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp23
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h83
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp10
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h7
-rw-r--r--modules/mono/mono_gd/support/ios_support.h4
-rw-r--r--modules/mono/mono_gd/support/ios_support.mm4
-rw-r--r--modules/mono/register_types.cpp12
-rw-r--r--modules/mono/register_types.h6
-rw-r--r--modules/mono/signal_awaiter_utils.cpp4
-rw-r--r--modules/mono/utils/macos_utils.cpp (renamed from modules/mono/utils/osx_utils.cpp)12
-rw-r--r--modules/mono/utils/macos_utils.h (renamed from modules/mono/utils/osx_utils.h)12
-rw-r--r--modules/mono/utils/macros.h6
-rw-r--r--modules/mono/utils/path_utils.cpp12
-rw-r--r--modules/mono/utils/path_utils.h6
-rw-r--r--modules/mono/utils/string_utils.cpp4
-rw-r--r--modules/mono/utils/string_utils.h6
-rw-r--r--modules/msdfgen/register_types.cpp12
-rw-r--r--modules/msdfgen/register_types.h6
-rw-r--r--modules/multiplayer/SCsub14
-rw-r--r--modules/multiplayer/config.py19
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml66
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml70
-rw-r--r--modules/multiplayer/doc_classes/SceneMultiplayer.xml55
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml67
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.cpp645
-rw-r--r--modules/multiplayer/editor/replication_editor_plugin.h148
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp302
-rw-r--r--modules/multiplayer/multiplayer_spawner.h118
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp304
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.h96
-rw-r--r--modules/multiplayer/register_types.cpp60
-rw-r--r--modules/multiplayer/register_types.h39
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp265
-rw-r--r--modules/multiplayer/scene_cache_interface.h82
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp332
-rw-r--r--modules/multiplayer/scene_multiplayer.h136
-rw-r--r--modules/multiplayer/scene_replication_config.cpp205
-rw-r--r--modules/multiplayer/scene_replication_config.h91
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp527
-rw-r--r--modules/multiplayer/scene_replication_interface.h86
-rw-r--r--modules/multiplayer/scene_replication_state.cpp267
-rw-r--r--modules/multiplayer/scene_replication_state.h135
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp521
-rw-r--r--modules/multiplayer/scene_rpc_interface.h102
-rw-r--r--modules/navigation/godot_navigation_server.cpp123
-rw-r--r--modules/navigation/godot_navigation_server.h25
-rw-r--r--modules/navigation/nav_map.cpp113
-rw-r--r--modules/navigation/nav_map.h33
-rw-r--r--modules/navigation/nav_region.cpp8
-rw-r--r--modules/navigation/nav_region.h18
-rw-r--r--modules/navigation/nav_utils.h20
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp93
-rw-r--r--modules/navigation/register_types.cpp22
-rw-r--r--modules/navigation/register_types.h6
-rw-r--r--modules/navigation/rvo_agent.cpp3
-rw-r--r--modules/noise/editor/noise_editor_plugin.cpp2
-rw-r--r--modules/noise/fastnoise_lite.cpp53
-rw-r--r--modules/noise/fastnoise_lite.h3
-rw-r--r--modules/noise/noise.cpp4
-rw-r--r--modules/noise/noise_texture.cpp4
-rw-r--r--modules/noise/register_types.cpp19
-rw-r--r--modules/noise/register_types.h6
-rw-r--r--modules/ogg/config.py4
-rw-r--r--modules/ogg/doc_classes/OggPacketSequence.xml (renamed from modules/ogg/doc_classes/OGGPacketSequence.xml)8
-rw-r--r--modules/ogg/doc_classes/OggPacketSequencePlayback.xml (renamed from modules/ogg/doc_classes/OGGPacketSequencePlayback.xml)2
-rw-r--r--modules/ogg/ogg_packet_sequence.cpp50
-rw-r--r--modules/ogg/ogg_packet_sequence.h28
-rw-r--r--modules/ogg/register_types.cpp16
-rw-r--r--modules/ogg/register_types.h6
-rw-r--r--modules/openxr/SCsub8
-rw-r--r--modules/openxr/action_map/openxr_action.h2
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp65
-rw-r--r--modules/openxr/action_map/openxr_action_map.h6
-rw-r--r--modules/openxr/action_map/openxr_action_set.h2
-rw-r--r--modules/openxr/action_map/openxr_defs.cpp158
-rw-r--r--modules/openxr/action_map/openxr_defs.h23
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile.cpp2
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRIPBinding.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml4
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.h2
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h2
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.cpp9
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.h2
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.cpp4
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.h2
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp4
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.h2
-rw-r--r--modules/openxr/extensions/openxr_android_extension.h2
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h12
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp67
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.h52
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.cpp13
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.h14
-rw-r--r--modules/openxr/openxr_api.cpp54
-rw-r--r--modules/openxr/openxr_api.h26
-rw-r--r--modules/openxr/openxr_interface.cpp27
-rw-r--r--modules/openxr/openxr_interface.h4
-rw-r--r--modules/openxr/openxr_util.h2
-rw-r--r--modules/openxr/register_types.cpp62
-rw-r--r--modules/openxr/register_types.h7
-rw-r--r--modules/raycast/lightmap_raycaster.cpp10
-rw-r--r--modules/raycast/lightmap_raycaster.h8
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp72
-rw-r--r--modules/raycast/raycast_occlusion_cull.h35
-rw-r--r--modules/raycast/register_types.cpp12
-rw-r--r--modules/raycast/register_types.h6
-rw-r--r--modules/raycast/static_raycaster.cpp10
-rw-r--r--modules/raycast/static_raycaster.h6
-rw-r--r--modules/regex/regex.cpp12
-rw-r--r--modules/regex/regex.h4
-rw-r--r--modules/regex/register_types.cpp11
-rw-r--r--modules/regex/register_types.h6
-rw-r--r--modules/regex/tests/test_regex.h10
-rw-r--r--modules/register_module_types.h14
-rw-r--r--modules/squish/register_types.cpp12
-rw-r--r--modules/squish/register_types.h6
-rw-r--r--modules/svg/image_loader_svg.cpp6
-rw-r--r--modules/svg/register_types.cpp12
-rw-r--r--modules/svg/register_types.h6
-rw-r--r--modules/text_server_adv/SCsub26
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct46
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py2
-rw-r--r--modules/text_server_adv/gdextension_build/text_server_adv.gdextension4
-rw-r--r--modules/text_server_adv/register_types.cpp19
-rw-r--r--modules/text_server_adv/register_types.h12
-rw-r--r--modules/text_server_adv/script_iterator.h2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp1116
-rw-r--r--modules/text_server_adv/text_server_adv.h103
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct20
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py2
-rw-r--r--modules/text_server_fb/gdextension_build/text_server_fb.gdextension4
-rw-r--r--modules/text_server_fb/register_types.cpp19
-rw-r--r--modules/text_server_fb/register_types.h12
-rw-r--r--modules/text_server_fb/text_server_fb.cpp490
-rw-r--r--modules/text_server_fb/text_server_fb.h83
-rw-r--r--modules/tga/register_types.cpp12
-rw-r--r--modules/tga/register_types.h6
-rw-r--r--modules/theora/register_types.cpp12
-rw-r--r--modules/theora/register_types.h6
-rw-r--r--modules/theora/video_stream_theora.cpp6
-rw-r--r--modules/theora/video_stream_theora.h8
-rw-r--r--modules/tinyexr/image_loader_tinyexr.h2
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp33
-rw-r--r--modules/tinyexr/image_saver_tinyexr.h1
-rw-r--r--modules/tinyexr/register_types.cpp13
-rw-r--r--modules/tinyexr/register_types.h6
-rw-r--r--modules/upnp/register_types.cpp11
-rw-r--r--modules/upnp/register_types.h6
-rw-r--r--modules/upnp/upnp.h6
-rw-r--r--modules/upnp/upnp_device.h6
-rw-r--r--modules/vhacd/register_types.cpp12
-rw-r--r--modules/vhacd/register_types.h6
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp272
-rw-r--r--modules/visual_script/editor/visual_script_editor.h32
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp45
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h20
-rw-r--r--modules/visual_script/register_types.cpp171
-rw-r--r--modules/visual_script/register_types.h6
-rw-r--r--modules/visual_script/visual_script.cpp364
-rw-r--r--modules/visual_script/visual_script.h44
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp6
-rw-r--r--modules/visual_script/visual_script_expression.cpp4
-rw-r--r--modules/visual_script/visual_script_expression.h6
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp36
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp38
-rw-r--r--modules/visual_script/visual_script_nodes.cpp95
-rw-r--r--modules/visual_script/visual_script_nodes.h7
-rw-r--r--modules/visual_script/visual_script_yield_nodes.cpp8
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp218
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h66
-rw-r--r--modules/vorbis/config.py4
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOggVorbis.xml (renamed from modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml)12
-rw-r--r--modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml (renamed from modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml)2
-rw-r--r--modules/vorbis/register_types.cpp18
-rw-r--r--modules/vorbis/register_types.h6
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp98
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h17
-rw-r--r--modules/webp/image_loader_webp.cpp181
-rw-r--r--modules/webp/image_loader_webp.h6
-rw-r--r--modules/webp/register_types.cpp22
-rw-r--r--modules/webp/register_types.h6
-rw-r--r--modules/webp/resource_saver_webp.cpp90
-rw-r--r--modules/webp/resource_saver_webp.h49
-rw-r--r--modules/webp/webp_common.cpp190
-rw-r--r--modules/webp/webp_common.h45
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml2
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml7
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml5
-rw-r--r--modules/webrtc/register_types.cpp12
-rw-r--r--modules/webrtc/register_types.h6
-rw-r--r--modules/webrtc/webrtc_data_channel.h3
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp48
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h10
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection.h1
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h2
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp2
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h2
-rw-r--r--modules/websocket/emws_client.h6
-rw-r--r--modules/websocket/emws_peer.h6
-rw-r--r--modules/websocket/emws_server.h6
-rw-r--r--modules/websocket/register_types.cpp34
-rw-r--r--modules/websocket/register_types.h6
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h4
-rw-r--r--modules/websocket/websocket_peer.h7
-rw-r--r--modules/websocket/websocket_server.h6
-rw-r--r--modules/websocket/wsl_client.cpp28
-rw-r--r--modules/websocket/wsl_client.h11
-rw-r--r--modules/websocket/wsl_peer.h6
-rw-r--r--modules/websocket/wsl_server.cpp2
-rw-r--r--modules/websocket/wsl_server.h6
-rw-r--r--modules/webxr/godot_webxr.h2
-rw-r--r--modules/webxr/register_types.cpp12
-rw-r--r--modules/webxr/register_types.h6
-rw-r--r--modules/webxr/webxr_interface_js.cpp22
-rw-r--r--modules/webxr/webxr_interface_js.h2
-rw-r--r--modules/xatlas_unwrap/register_types.cpp11
-rw-r--r--modules/xatlas_unwrap/register_types.h6
518 files changed, 13296 insertions, 4681 deletions
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index 18554177d9..e80d453df7 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -266,7 +266,11 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
return basis_universal_unpacker_ptr(r, size);
}
-void register_basis_universal_types() {
+void initialize_basis_universal_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
using namespace basisu;
using namespace basist;
@@ -277,7 +281,11 @@ void register_basis_universal_types() {
Image::basis_universal_unpacker_ptr = basis_universal_unpacker_ptr;
}
-void unregister_basis_universal_types() {
+void uninitialize_basis_universal_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
Image::basis_universal_packer = nullptr;
#endif
diff --git a/modules/basis_universal/register_types.h b/modules/basis_universal/register_types.h
index 7275c2ebb7..68d5dd64f3 100644
--- a/modules/basis_universal/register_types.h
+++ b/modules/basis_universal/register_types.h
@@ -31,7 +31,9 @@
#ifndef BASIS_UNIVERSAL_REGISTER_TYPES_H
#define BASIS_UNIVERSAL_REGISTER_TYPES_H
-void register_basis_universal_types();
-void unregister_basis_universal_types();
+#include "modules/register_module_types.h"
+
+void initialize_basis_universal_module(ModuleInitializationLevel p_level);
+void uninitialize_basis_universal_module(ModuleInitializationLevel p_level);
#endif // BASIS_UNIVERSAL_REGISTER_TYPES_H
diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp
index 13e44099e5..7c4a2085b2 100644
--- a/modules/bmp/register_types.cpp
+++ b/modules/bmp/register_types.cpp
@@ -34,11 +34,19 @@
static ImageLoaderBMP *image_loader_bmp = nullptr;
-void register_bmp_types() {
+void initialize_bmp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
image_loader_bmp = memnew(ImageLoaderBMP);
ImageLoader::add_image_format_loader(image_loader_bmp);
}
-void unregister_bmp_types() {
+void uninitialize_bmp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_bmp);
}
diff --git a/modules/bmp/register_types.h b/modules/bmp/register_types.h
index 1e53f4c2f7..45c8499c58 100644
--- a/modules/bmp/register_types.h
+++ b/modules/bmp/register_types.h
@@ -31,7 +31,9 @@
#ifndef BMP_REGISTER_TYPES_H
#define BMP_REGISTER_TYPES_H
-void register_bmp_types();
-void unregister_bmp_types();
+#include "modules/register_module_types.h"
+
+void initialize_bmp_module(ModuleInitializationLevel p_level);
+void uninitialize_bmp_module(ModuleInitializationLevel p_level);
#endif // BMP_REGISTER_TYPES_H
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index de97724d09..9a6147d433 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -9,6 +9,6 @@ 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")
-elif env["platform"] == "osx":
+elif env["platform"] == "macos":
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
- env_camera.add_source_files(env.modules_sources, "camera_osx.mm")
+ env_camera.add_source_files(env.modules_sources, "camera_macos.mm")
diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_macos.h
index b0db844599..903eda51bf 100644
--- a/modules/camera/camera_osx.h
+++ b/modules/camera/camera_macos.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* camera_osx.h */
+/* camera_macos.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,19 +28,19 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CAMERAOSX_H
-#define CAMERAOSX_H
+#ifndef CAMERA_MACOS_H
+#define CAMERA_MACOS_H
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
#include "servers/camera_server.h"
-class CameraOSX : public CameraServer {
+class CameraMacOS : public CameraServer {
public:
- CameraOSX();
+ CameraMacOS();
void update_feeds();
};
-#endif /* CAMERAOSX_H */
+#endif // CAMERA_MACOS_H
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_macos.mm
index d199c31b2f..0b9696a3e9 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_macos.mm
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* camera_osx.mm */
+/* camera_macos.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -31,7 +31,7 @@
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
// If you fix something here, make sure you fix it there as well!
-#include "camera_osx.h"
+#include "camera_macos.h"
#include "servers/camera/camera_feed.h"
#import <AVFoundation/AVFoundation.h>
@@ -191,9 +191,9 @@
@end
//////////////////////////////////////////////////////////////////////////
-// CameraFeedOSX - Subclass for camera feeds in OSX
+// CameraFeedMacOS - Subclass for camera feeds in macOS
-class CameraFeedOSX : public CameraFeed {
+class CameraFeedMacOS : public CameraFeed {
private:
AVCaptureDevice *device;
MyCaptureSession *capture_session;
@@ -201,7 +201,7 @@ private:
public:
AVCaptureDevice *get_device() const;
- CameraFeedOSX();
+ CameraFeedMacOS();
void set_device(AVCaptureDevice *p_device);
@@ -209,16 +209,16 @@ public:
void deactivate_feed();
};
-AVCaptureDevice *CameraFeedOSX::get_device() const {
+AVCaptureDevice *CameraFeedMacOS::get_device() const {
return device;
};
-CameraFeedOSX::CameraFeedOSX() {
+CameraFeedMacOS::CameraFeedMacOS() {
device = nullptr;
capture_session = nullptr;
};
-void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
+void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) {
device = p_device;
// get some info
@@ -232,7 +232,7 @@ void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
};
};
-bool CameraFeedOSX::activate_feed() {
+bool CameraFeedMacOS::activate_feed() {
if (capture_session) {
// Already recording!
} else {
@@ -258,7 +258,7 @@ bool CameraFeedOSX::activate_feed() {
return true;
};
-void CameraFeedOSX::deactivate_feed() {
+void CameraFeedMacOS::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
@@ -271,7 +271,7 @@ void CameraFeedOSX::deactivate_feed() {
// when devices are connected/disconnected
@interface MyDeviceNotifications : NSObject {
- CameraOSX *camera_server;
+ CameraMacOS *camera_server;
}
@end
@@ -282,7 +282,7 @@ void CameraFeedOSX::deactivate_feed() {
camera_server->update_feeds();
}
-- (id)initForServer:(CameraOSX *)p_server {
+- (id)initForServer:(CameraMacOS *)p_server {
if (self = [super init]) {
camera_server = p_server;
@@ -303,9 +303,9 @@ void CameraFeedOSX::deactivate_feed() {
MyDeviceNotifications *device_notifications = nil;
//////////////////////////////////////////////////////////////////////////
-// CameraOSX - Subclass for our camera server on OSX
+// CameraMacOS - Subclass for our camera server on macOS
-void CameraOSX::update_feeds() {
+void CameraMacOS::update_feeds() {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500
AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternalUnknown, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
NSArray *devices = session.devices;
@@ -315,7 +315,7 @@ void CameraOSX::update_feeds() {
// remove devices that are gone..
for (int i = feeds.size() - 1; i >= 0; i--) {
- Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+ Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
if (![devices containsObject:feed->get_device()]) {
// remove it from our array, this will also destroy it ;)
@@ -326,14 +326,14 @@ void CameraOSX::update_feeds() {
for (AVCaptureDevice *device in devices) {
bool found = false;
for (int i = 0; i < feeds.size() && !found; i++) {
- Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+ Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i];
if (feed->get_device() == device) {
found = true;
};
};
if (!found) {
- Ref<CameraFeedOSX> newfeed;
+ Ref<CameraFeedMacOS> newfeed;
newfeed.instantiate();
newfeed->set_device(device);
@@ -346,7 +346,7 @@ void CameraOSX::update_feeds() {
};
};
-CameraOSX::CameraOSX() {
+CameraMacOS::CameraMacOS() {
// Find available cameras we have at this time
update_feeds();
diff --git a/modules/camera/camera_win.h b/modules/camera/camera_win.h
index 9563326acb..ebfc117190 100644
--- a/modules/camera/camera_win.h
+++ b/modules/camera/camera_win.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CAMERAWIN_H
-#define CAMERAWIN_H
+#ifndef CAMERA_WIN_H
+#define CAMERA_WIN_H
#include "servers/camera/camera_feed.h"
#include "servers/camera_server.h"
@@ -43,4 +43,4 @@ public:
~CameraWindows() {}
};
-#endif /* CAMERAWIN_H */
+#endif // CAMERA_WIN_H
diff --git a/modules/camera/config.py b/modules/camera/config.py
index 8a22751aa7..d2b2542dd9 100644
--- a/modules/camera/config.py
+++ b/modules/camera/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return platform == "osx" or platform == "windows"
+ return platform == "macos" or platform == "windows"
def configure(env):
diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp
index b0b1276436..40e2224d6b 100644
--- a/modules/camera/register_types.cpp
+++ b/modules/camera/register_types.cpp
@@ -33,18 +33,25 @@
#if defined(WINDOWS_ENABLED)
#include "camera_win.h"
#endif
-#if defined(OSX_ENABLED)
-#include "camera_osx.h"
+#if defined(MACOS_ENABLED)
+#include "camera_macos.h"
#endif
-void register_camera_types() {
+void initialize_camera_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#if defined(WINDOWS_ENABLED)
CameraServer::make_default<CameraWindows>();
#endif
-#if defined(OSX_ENABLED)
- CameraServer::make_default<CameraOSX>();
+#if defined(MACOS_ENABLED)
+ CameraServer::make_default<CameraMacOS>();
#endif
}
-void unregister_camera_types() {
+void uninitialize_camera_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h
index 5ee7aec886..4ac4426588 100644
--- a/modules/camera/register_types.h
+++ b/modules/camera/register_types.h
@@ -31,7 +31,9 @@
#ifndef CAMERA_REGISTER_TYPES_H
#define CAMERA_REGISTER_TYPES_H
-void register_camera_types();
-void unregister_camera_types();
+#include "modules/register_module_types.h"
+
+void initialize_camera_module(ModuleInitializationLevel p_level);
+void uninitialize_camera_module(ModuleInitializationLevel p_level);
#endif // CAMERA_REGISTER_TYPES_H
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 82dc4a4175..93533e1690 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -194,7 +194,7 @@ void CSGBrush::_regen_face_aabbs() {
}
}
-void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_invert_faces) {
+void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_flip_faces) {
faces.clear();
int vc = p_vertices.size();
@@ -208,10 +208,10 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
const bool *rs = p_smooth.ptr();
int mc = p_materials.size();
const Ref<Material> *rm = p_materials.ptr();
- int ic = p_invert_faces.size();
- const bool *ri = p_invert_faces.ptr();
+ int ic = p_flip_faces.size();
+ const bool *ri = p_flip_faces.ptr();
- Map<Ref<Material>, int> material_map;
+ HashMap<Ref<Material>, int> material_map;
faces.resize(p_vertices.size() / 3);
@@ -242,10 +242,10 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
if (mc == vc / 3) {
Ref<Material> mat = rm[i];
if (mat.is_valid()) {
- const Map<Ref<Material>, int>::Element *E = material_map.find(mat);
+ HashMap<Ref<Material>, int>::ConstIterator E = material_map.find(mat);
if (E) {
- f.material = E->get();
+ f.material = E->value;
} else {
f.material = material_map.size();
material_map[mat] = f.material;
@@ -1336,9 +1336,9 @@ CSGBrushOperation::Build2DFaces::Build2DFaces(const CSGBrush &p_brush, int p_fac
plane = Plane(points_3D[0], points_3D[1], points_3D[2]);
to_3D.origin = points_3D[0];
- to_3D.basis.set_axis(2, plane.normal);
- to_3D.basis.set_axis(0, (points_3D[1] - points_3D[2]).normalized());
- to_3D.basis.set_axis(1, to_3D.basis.get_axis(0).cross(to_3D.basis.get_axis(2)).normalized());
+ to_3D.basis.set_column(2, plane.normal);
+ to_3D.basis.set_column(0, (points_3D[1] - points_3D[2]).normalized());
+ to_3D.basis.set_column(1, to_3D.basis.get_column(0).cross(to_3D.basis.get_column(2)).normalized());
to_2D = to_3D.affine_inverse();
Face2D face;
@@ -1387,13 +1387,13 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
}
// Ensure B has points either side of or in the plane of A.
- int in_plane_count = 0, over_count = 0, under_count = 0;
+ int over_count = 0, under_count = 0;
Plane plane_a(vertices_a[0], vertices_a[1], vertices_a[2]);
ERR_FAIL_COND_MSG(plane_a.normal == Vector3(), "Couldn't form plane from Brush A face.");
for (int i = 0; i < 3; i++) {
if (plane_a.has_point(vertices_b[i])) {
- in_plane_count++;
+ // In plane.
} else if (plane_a.is_point_over(vertices_b[i])) {
over_count++;
} else {
@@ -1406,7 +1406,6 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
}
// Ensure A has points either side of or in the plane of B.
- in_plane_count = 0;
over_count = 0;
under_count = 0;
Plane plane_b(vertices_b[0], vertices_b[1], vertices_b[2]);
@@ -1414,7 +1413,7 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
for (int i = 0; i < 3; i++) {
if (plane_b.has_point(vertices_a[i])) {
- in_plane_count++;
+ // In plane.
} else if (plane_b.is_point_over(vertices_a[i])) {
over_count++;
} else {
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 9ff7b13a44..738e3d68ea 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -38,8 +38,8 @@
#include "core/math/vector3.h"
#include "core/object/ref_counted.h"
#include "core/templates/list.h"
-#include "core/templates/map.h"
#include "core/templates/oa_hash_map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "scene/resources/material.h"
@@ -130,17 +130,17 @@ struct CSGBrushOperation {
struct VertexKeyHash {
static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) {
- uint32_t h = hash_djb2_one_32(p_vk.x);
- h = hash_djb2_one_32(p_vk.y, h);
- h = hash_djb2_one_32(p_vk.z, h);
+ uint32_t h = hash_murmur3_one_32(p_vk.x);
+ h = hash_murmur3_one_32(p_vk.y, h);
+ h = hash_murmur3_one_32(p_vk.z, h);
return h;
}
};
Vector<Vector3> points;
Vector<Face> faces;
- Map<Ref<Material>, int> materials;
- Map<Vector3, int> vertex_map;
+ HashMap<Ref<Material>, int> materials;
+ HashMap<Vector3, int> vertex_map;
OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
float vertex_snap = 0.0;
@@ -184,8 +184,8 @@ struct CSGBrushOperation {
};
struct Build2DFaceCollection {
- Map<int, Build2DFaces> build2DFacesA;
- Map<int, Build2DFaces> build2DFacesB;
+ HashMap<int, Build2DFaces> build2DFacesA;
+ HashMap<int, Build2DFaces> build2DFacesB;
};
void update_faces(const CSGBrush &p_brush_a, const int p_face_idx_a, const CSGBrush &p_brush_b, const int p_face_idx_b, Build2DFaceCollection &p_collection, float p_vertex_snap);
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index be9bf9538f..56be4e65f0 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -638,7 +638,7 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
ADD_GROUP("Collision", "collision_");
@@ -682,7 +682,7 @@ CSGBrush *CSGPrimitive3D::_create_brush_from_arrays(const Vector<Vector3> &p_ver
int ic = invert.size();
bool *w = invert.ptrw();
for (int i = 0; i < ic; i++) {
- w[i] = invert_faces;
+ w[i] = flip_faces;
}
}
brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
@@ -691,28 +691,28 @@ CSGBrush *CSGPrimitive3D::_create_brush_from_arrays(const Vector<Vector3> &p_ver
}
void CSGPrimitive3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive3D::set_invert_faces);
- ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive3D::is_inverting_faces);
+ ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &CSGPrimitive3D::set_flip_faces);
+ ClassDB::bind_method(D_METHOD("get_flip_faces"), &CSGPrimitive3D::get_flip_faces);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
}
-void CSGPrimitive3D::set_invert_faces(bool p_invert) {
- if (invert_faces == p_invert) {
+void CSGPrimitive3D::set_flip_faces(bool p_invert) {
+ if (flip_faces == p_invert) {
return;
}
- invert_faces = p_invert;
+ flip_faces = p_invert;
_make_dirty();
}
-bool CSGPrimitive3D::is_inverting_faces() {
- return invert_faces;
+bool CSGPrimitive3D::get_flip_faces() {
+ return flip_faces;
}
CSGPrimitive3D::CSGPrimitive3D() {
- invert_faces = false;
+ flip_faces = false;
}
/////////////////////
@@ -921,7 +921,7 @@ CSGBrush *CSGSphere3D::_build_brush() {
int face_count = rings * radial_segments * 2 - radial_segments * 2;
- bool invert_val = is_inverting_faces();
+ bool invert_val = get_flip_faces();
Ref<Material> material = get_material();
Vector<Vector3> faces;
@@ -1052,7 +1052,7 @@ void CSGSphere3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere3D::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1110,7 +1110,7 @@ Ref<Material> CSGSphere3D::get_material() const {
CSGSphere3D::CSGSphere3D() {
// defaults
- radius = 1.0;
+ radius = 0.5;
radial_segments = 12;
rings = 6;
smooth_faces = true;
@@ -1125,7 +1125,7 @@ CSGBrush *CSGBox3D::_build_brush() {
int face_count = 12; //it's a cube..
- bool invert_val = is_inverting_faces();
+ bool invert_val = get_flip_faces();
Ref<Material> material = get_material();
Vector<Vector3> faces;
@@ -1225,7 +1225,7 @@ void CSGBox3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
@@ -1258,7 +1258,7 @@ CSGBrush *CSGCylinder3D::_build_brush() {
int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
- bool invert_val = is_inverting_faces();
+ bool invert_val = get_flip_faces();
Ref<Material> material = get_material();
Vector<Vector3> faces;
@@ -1405,8 +1405,8 @@ void CSGCylinder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1503,7 +1503,7 @@ CSGBrush *CSGTorus3D::_build_brush() {
int face_count = ring_sides * sides * 2;
- bool invert_val = is_inverting_faces();
+ bool invert_val = get_flip_faces();
Ref<Material> material = get_material();
Vector<Vector3> faces;
@@ -1630,8 +1630,8 @@ void CSGTorus3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_outer_radius", "get_outer_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1858,7 +1858,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
Transform3D facing = Transform3D().looking_at(direction, current_up);
- current_xform = base_xform.translated(current_point) * facing;
+ current_xform = base_xform.translated_local(current_point) * facing;
}
// Create the mesh.
@@ -1881,7 +1881,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
smoothw[face] = false;
materialsw[face] = material;
- invertw[face] = invert_faces;
+ invertw[face] = flip_faces;
face++;
}
}
@@ -1897,7 +1897,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
switch (mode) {
case MODE_DEPTH: {
- current_xform.translate(Vector3(0, 0, -depth));
+ current_xform.translate_local(Vector3(0, 0, -depth));
} break;
case MODE_SPIN: {
current_xform.rotate(Vector3(0, 1, 0), spin_step);
@@ -1945,7 +1945,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
Transform3D facing = Transform3D().looking_at(direction, current_up);
- current_xform = base_xform.translated(current_point) * facing;
+ current_xform = base_xform.translated_local(current_point) * facing;
} break;
}
@@ -1986,7 +1986,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
uvsw[face * 3 + 2] = u[2];
smoothw[face] = smooth_faces;
- invertw[face] = invert_faces;
+ invertw[face] = flip_faces;
materialsw[face] = material;
face++;
@@ -2001,7 +2001,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
uvsw[face * 3 + 2] = u[0];
smoothw[face] = smooth_faces;
- invertw[face] = invert_faces;
+ invertw[face] = flip_faces;
materialsw[face] = material;
face++;
@@ -2026,7 +2026,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
smoothw[face] = false;
materialsw[face] = material;
- invertw[face] = invert_faces;
+ invertw[face] = flip_faces;
face++;
}
}
@@ -2135,7 +2135,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp,suffix:m"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node");
@@ -2145,7 +2145,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater"), "set_path_u_distance", "get_path_u_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater,suffix:m"), "set_path_u_distance", "get_path_u_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index b5d5f97cf3..0b49dc4609 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -74,9 +74,9 @@ private:
struct Vector3Hasher {
_ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
- uint32_t h = hash_djb2_one_float(p_vec3.x);
- h = hash_djb2_one_float(p_vec3.y, h);
- h = hash_djb2_one_float(p_vec3.z, h);
+ uint32_t h = hash_murmur3_one_float(p_vec3.x);
+ h = hash_murmur3_one_float(p_vec3.y, h);
+ h = hash_murmur3_one_float(p_vec3.z, h);
return h;
}
};
@@ -171,13 +171,13 @@ class CSGPrimitive3D : public CSGShape3D {
GDCLASS(CSGPrimitive3D, CSGShape3D);
protected:
- bool invert_faces;
+ bool flip_faces;
CSGBrush *_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials);
static void _bind_methods();
public:
- void set_invert_faces(bool p_invert);
- bool is_inverting_faces();
+ void set_flip_faces(bool p_invert);
+ bool get_flip_faces();
CSGPrimitive3D();
};
diff --git a/modules/csg/doc_classes/CSGPrimitive3D.xml b/modules/csg/doc_classes/CSGPrimitive3D.xml
index 39f4fa320d..6ea413c991 100644
--- a/modules/csg/doc_classes/CSGPrimitive3D.xml
+++ b/modules/csg/doc_classes/CSGPrimitive3D.xml
@@ -11,8 +11,8 @@
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
- <member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces" default="false">
- Invert the faces of the mesh.
+ <member name="flip_faces" type="bool" setter="set_flip_faces" getter="get_flip_faces" default="false">
+ If set, the order of the vertices in each triangle are reversed resulting in the backside of the mesh being drawn.
</member>
</members>
</class>
diff --git a/modules/csg/doc_classes/CSGSphere3D.xml b/modules/csg/doc_classes/CSGSphere3D.xml
index 227f620a4e..d2f985b3a2 100644
--- a/modules/csg/doc_classes/CSGSphere3D.xml
+++ b/modules/csg/doc_classes/CSGSphere3D.xml
@@ -17,7 +17,7 @@
<member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" default="12">
Number of vertical slices for the sphere.
</member>
- <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
Radius of the sphere.
</member>
<member name="rings" type="int" setter="set_rings" getter="get_rings" default="6">
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 4d972e46c6..6442ff71fc 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -349,9 +349,11 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (cs->is_root_shape()) {
Array csg_meshes = cs->get_meshes();
- Ref<Mesh> csg_mesh = csg_meshes[1];
- if (csg_mesh.is_valid()) {
- p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
+ if (csg_meshes.size() == 2) {
+ Ref<Mesh> csg_mesh = csg_meshes[1];
+ if (csg_mesh.is_valid()) {
+ p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
+ }
}
}
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
index 72ed027dc9..9b5888dafe 100644
--- a/modules/csg/register_types.cpp
+++ b/modules/csg/register_types.cpp
@@ -38,23 +38,29 @@
#include "editor/csg_gizmos.h"
#endif
-void register_csg_types() {
- GDREGISTER_ABSTRACT_CLASS(CSGShape3D);
- GDREGISTER_ABSTRACT_CLASS(CSGPrimitive3D);
- GDREGISTER_CLASS(CSGMesh3D);
- GDREGISTER_CLASS(CSGSphere3D);
- GDREGISTER_CLASS(CSGBox3D);
- GDREGISTER_CLASS(CSGCylinder3D);
- GDREGISTER_CLASS(CSGTorus3D);
- GDREGISTER_CLASS(CSGPolygon3D);
- GDREGISTER_CLASS(CSGCombiner3D);
-
+void initialize_csg_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_ABSTRACT_CLASS(CSGShape3D);
+ GDREGISTER_ABSTRACT_CLASS(CSGPrimitive3D);
+ GDREGISTER_CLASS(CSGMesh3D);
+ GDREGISTER_CLASS(CSGSphere3D);
+ GDREGISTER_CLASS(CSGBox3D);
+ GDREGISTER_CLASS(CSGCylinder3D);
+ GDREGISTER_CLASS(CSGTorus3D);
+ GDREGISTER_CLASS(CSGPolygon3D);
+ GDREGISTER_CLASS(CSGCombiner3D);
+ }
#ifdef TOOLS_ENABLED
- EditorPlugins::add_by_type<EditorPluginCSG>();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<EditorPluginCSG>();
+ }
#endif
}
-void unregister_csg_types() {
+void uninitialize_csg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
#endif // _3D_DISABLED
diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h
index 59d84dd52a..ec65adde9c 100644
--- a/modules/csg/register_types.h
+++ b/modules/csg/register_types.h
@@ -31,7 +31,9 @@
#ifndef CSG_REGISTER_TYPES_H
#define CSG_REGISTER_TYPES_H
-void register_csg_types();
-void unregister_csg_types();
+#include "modules/register_module_types.h"
+
+void initialize_csg_module(ModuleInitializationLevel p_level);
+void uninitialize_csg_module(ModuleInitializationLevel p_level);
#endif // CSG_REGISTER_TYPES_H
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index d18340a2c8..3322ff0a1b 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -46,7 +46,7 @@ struct CVTTCompressionJobParams {
};
struct CVTTCompressionRowTask {
- const uint8_t *in_mm_bytes;
+ const uint8_t *in_mm_bytes = nullptr;
uint8_t *out_mm_bytes = nullptr;
int y_start = 0;
int width = 0;
@@ -55,7 +55,7 @@ struct CVTTCompressionRowTask {
struct CVTTCompressionJobQueue {
CVTTCompressionJobParams job_params;
- const CVTTCompressionRowTask *job_tasks;
+ const CVTTCompressionRowTask *job_tasks = nullptr;
uint32_t num_tasks = 0;
SafeNumeric<uint32_t> current_task;
};
@@ -129,14 +129,6 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
}
}
-static void _digest_job_queue(void *p_job_queue) {
- CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue);
-
- for (uint32_t next_task = job_queue->current_task.increment(); next_task <= job_queue->num_tasks; next_task = job_queue->current_task.increment()) {
- _digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]);
- }
-}
-
void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels) {
if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
return; //do not compress, already compressed
@@ -202,7 +194,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
job_queue.job_params.bytes_per_pixel = is_hdr ? 6 : 4;
cvtt::Kernels::ConfigureBC7EncodingPlanFromQuality(job_queue.job_params.bc7_plan, 5);
- int num_job_threads = 0;
// Amdahl's law (Wikipedia)
// If a program needs 20 hours to complete using a single thread, but a one-hour portion of the program cannot be parallelized,
// therefore only the remaining 19 hours (p = 0.95) of execution time can be parallelized, then regardless of how many threads are devoted
@@ -229,11 +220,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
row_task.in_mm_bytes = in_bytes;
row_task.out_mm_bytes = out_bytes;
- if (num_job_threads > 0) {
- tasks.push_back(row_task);
- } else {
- _digest_row_task(job_queue.job_params, row_task);
- }
+ _digest_row_task(job_queue.job_params, row_task);
out_bytes += 16 * (bw / 4);
}
@@ -243,29 +230,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
h = MAX(h / 2, 1);
}
- if (num_job_threads > 0) {
- Vector<Thread *> threads;
- threads.resize(num_job_threads);
-
- Thread **threads_wb = threads.ptrw();
-
- const CVTTCompressionRowTask *tasks_rb = tasks.ptr();
-
- job_queue.job_tasks = &tasks_rb[0];
- job_queue.current_task.set(0);
- job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
-
- for (int i = 0; i < num_job_threads; i++) {
- threads_wb[i] = memnew(Thread);
- threads_wb[i]->start(_digest_job_queue, &job_queue);
- }
- _digest_job_queue(&job_queue);
-
- for (int i = 0; i < num_job_threads; i++) {
- threads_wb[i]->wait_to_finish();
- memdelete(threads_wb[i]);
- }
- }
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp
index 13903f700b..ff22c0f53e 100644
--- a/modules/cvtt/register_types.cpp
+++ b/modules/cvtt/register_types.cpp
@@ -34,11 +34,19 @@
#include "image_compress_cvtt.h"
-void register_cvtt_types() {
+void initialize_cvtt_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
Image::set_compress_bptc_func(image_compress_cvtt);
Image::_image_decompress_bptc = image_decompress_cvtt;
}
-void unregister_cvtt_types() {}
+void uninitialize_cvtt_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
#endif
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
index 9cbca75c7b..38a375eb44 100644
--- a/modules/cvtt/register_types.h
+++ b/modules/cvtt/register_types.h
@@ -33,8 +33,10 @@
#ifdef TOOLS_ENABLED
-void register_cvtt_types();
-void unregister_cvtt_types();
+#include "modules/register_module_types.h"
+
+void initialize_cvtt_module(ModuleInitializationLevel p_level);
+void uninitialize_cvtt_module(ModuleInitializationLevel p_level);
#endif // TOOLS_ENABLED
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
index 15a93050ee..e819c92dd3 100644
--- a/modules/dds/register_types.cpp
+++ b/modules/dds/register_types.cpp
@@ -34,12 +34,20 @@
static Ref<ResourceFormatDDS> resource_loader_dds;
-void register_dds_types() {
+void initialize_dds_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
}
-void unregister_dds_types() {
+void uninitialize_dds_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
ResourceLoader::remove_resource_format_loader(resource_loader_dds);
resource_loader_dds.unref();
}
diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h
index d676346e02..3cd154d576 100644
--- a/modules/dds/register_types.h
+++ b/modules/dds/register_types.h
@@ -31,7 +31,9 @@
#ifndef DDS_REGISTER_TYPES_H
#define DDS_REGISTER_TYPES_H
-void register_dds_types();
-void unregister_dds_types();
+#include "modules/register_module_types.h"
+
+void initialize_dds_module(ModuleInitializationLevel p_level);
+void uninitialize_dds_module(ModuleInitializationLevel p_level);
#endif // DDS_REGISTER_TYPES_H
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 58e5e31f46..eb5614338b 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -68,7 +68,7 @@ enum DDSFormat {
};
struct DDSFormatInfo {
- const char *name;
+ const char *name = nullptr;
bool compressed = false;
bool palette = false;
uint32_t divisor = 0;
@@ -94,7 +94,7 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
};
-RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
@@ -102,7 +102,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
if (f.is_null()) {
- return RES();
+ return Ref<Resource>();
}
Ref<FileAccess> fref(f);
@@ -110,7 +110,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
*r_error = ERR_FILE_CORRUPT;
}
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open DDS texture file '" + p_path + "'.");
+ ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open DDS texture file '" + p_path + "'.");
uint32_t magic = f->get_32();
uint32_t hsize = f->get_32();
@@ -131,7 +131,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
// We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing,
// but non-mandatory when reading (as some writers don't set them)...
if (magic != DDS_MAGIC || hsize != 124) {
- ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
+ ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported DDS texture file '" + p_path + "'.");
}
/* uint32_t format_size = */ f->get_32();
@@ -204,7 +204,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
dds_format = DDS_BGR565;
} else {
printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask);
- ERR_FAIL_V_MSG(RES(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'.");
+ ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'.");
}
if (!(flags & DDSD_MIPMAPCOUNT)) {
@@ -221,8 +221,8 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
//compressed bc
uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size;
- ERR_FAIL_COND_V(size != pitch, RES());
- ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), RES());
+ ERR_FAIL_COND_V(size != pitch, Ref<Resource>());
+ ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref<Resource>());
for (uint32_t i = 1; i < mipmaps; i++) {
w = MAX(1u, w >> 1);
@@ -238,11 +238,11 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
} else if (info.palette) {
//indexed
- ERR_FAIL_COND_V(!(flags & DDSD_PITCH), RES());
- ERR_FAIL_COND_V(format_rgb_bits != 8, RES());
+ ERR_FAIL_COND_V(!(flags & DDSD_PITCH), Ref<Resource>());
+ ERR_FAIL_COND_V(format_rgb_bits != 8, Ref<Resource>());
uint32_t size = pitch * height;
- ERR_FAIL_COND_V(size != width * height * info.block_size, RES());
+ ERR_FAIL_COND_V(size != width * height * info.block_size, Ref<Resource>());
uint8_t palette[256 * 4];
f->get_buffer(palette, 256 * 4);
@@ -409,9 +409,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
}
Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data));
-
- Ref<ImageTexture> texture = memnew(ImageTexture);
- texture->create_from_image(img);
+ Ref<ImageTexture> texture = ImageTexture::create_from_image(img);
if (r_error) {
*r_error = OK;
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
index 25ded4e168..701f8f4a13 100644
--- a/modules/dds/texture_loader_dds.h
+++ b/modules/dds/texture_loader_dds.h
@@ -36,7 +36,7 @@
class ResourceFormatDDS : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
diff --git a/modules/denoise/config.py b/modules/denoise/config.py
index 3aa840acb0..521115dae5 100644
--- a/modules/denoise/config.py
+++ b/modules/denoise/config.py
@@ -4,7 +4,7 @@ def can_build(env, platform):
# It's also only relevant for tools build and desktop platforms,
# as doing lightmap generation and denoising on Android or HTML5
# would be a bit far-fetched.
- desktop_platforms = ["linuxbsd", "osx", "windows"]
+ desktop_platforms = ["linuxbsd", "macos", "windows"]
supported_arch = env["bits"] == "64"
if env["arch"] == "arm64":
supported_arch = False
diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp
index 07393d0f5c..891a03c657 100644
--- a/modules/denoise/register_types.cpp
+++ b/modules/denoise/register_types.cpp
@@ -32,9 +32,16 @@
#include "core/config/engine.h"
#include "lightmap_denoiser.h"
-void register_denoise_types() {
+void initialize_denoise_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
LightmapDenoiserOIDN::make_default_denoiser();
}
-void unregister_denoise_types() {
+void uninitialize_denoise_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h
index 6ce386dc5d..13eba88d17 100644
--- a/modules/denoise/register_types.h
+++ b/modules/denoise/register_types.h
@@ -31,7 +31,9 @@
#ifndef DENOISE_REGISTER_TYPES_H
#define DENOISE_REGISTER_TYPES_H
-void register_denoise_types();
-void unregister_denoise_types();
+#include "modules/register_module_types.h"
+
+void initialize_denoise_module(ModuleInitializationLevel p_level);
+void uninitialize_denoise_module(ModuleInitializationLevel p_level);
#endif // DENOISE_REGISTER_TYPES_H
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index cd94cc9425..dfdd08c9f4 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -441,15 +441,15 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
channel = SYSCH_MAX + transfer_channel - 1;
} else {
switch (get_transfer_mode()) {
- case Multiplayer::TRANSFER_MODE_UNRELIABLE: {
+ case TRANSFER_MODE_UNRELIABLE: {
packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
channel = SYSCH_UNRELIABLE;
} break;
- case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ case TRANSFER_MODE_UNRELIABLE_ORDERED: {
packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
channel = SYSCH_UNRELIABLE;
} break;
- case Multiplayer::TRANSFER_MODE_RELIABLE: {
+ case TRANSFER_MODE_RELIABLE: {
packet_flags = ENET_PACKET_FLAG_RELIABLE;
channel = SYSCH_RELIABLE;
} break;
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index e7b61169fb..3152068d46 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NETWORKED_MULTIPLAYER_ENET_H
-#define NETWORKED_MULTIPLAYER_ENET_H
+#ifndef ENET_MULTIPLAYER_PEER_H
+#define ENET_MULTIPLAYER_PEER_H
#include "core/crypto/crypto.h"
-#include "core/multiplayer/multiplayer_peer.h"
+#include "scene/main/multiplayer_peer.h"
#include "enet_connection.h"
#include <enet/enet.h>
@@ -70,8 +70,8 @@ private:
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
- Map<int, Ref<ENetConnection>> hosts;
- Map<int, Ref<ENetPacketPeer>> peers;
+ HashMap<int, Ref<ENetConnection>> hosts;
+ HashMap<int, Ref<ENetPacketPeer>> peers;
struct Packet {
ENetPacket *packet = nullptr;
@@ -135,4 +135,4 @@ public:
~ENetMultiplayerPeer();
};
-#endif // NETWORKED_MULTIPLAYER_ENET_H
+#endif // ENET_MULTIPLAYER_PEER_H
diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp
index ebc5d95348..14f3374e24 100644
--- a/modules/enet/register_types.cpp
+++ b/modules/enet/register_types.cpp
@@ -36,7 +36,11 @@
static bool enet_ok = false;
-void register_enet_types() {
+void initialize_enet_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (enet_initialize() != 0) {
ERR_PRINT("ENet initialization failure");
} else {
@@ -48,7 +52,11 @@ void register_enet_types() {
GDREGISTER_CLASS(ENetConnection);
}
-void unregister_enet_types() {
+void uninitialize_enet_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (enet_ok) {
enet_deinitialize();
}
diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h
index a94ecccc61..b4f491287d 100644
--- a/modules/enet/register_types.h
+++ b/modules/enet/register_types.h
@@ -31,7 +31,9 @@
#ifndef ENET_REGISTER_TYPES_H
#define ENET_REGISTER_TYPES_H
-void register_enet_types();
-void unregister_enet_types();
+#include "modules/register_module_types.h"
+
+void initialize_enet_module(ModuleInitializationLevel p_level);
+void uninitialize_enet_module(ModuleInitializationLevel p_level);
#endif // ENET_REGISTER_TYPES_H
diff --git a/modules/etcpak/register_types.cpp b/modules/etcpak/register_types.cpp
index e835004406..eaad1e7b01 100644
--- a/modules/etcpak/register_types.cpp
+++ b/modules/etcpak/register_types.cpp
@@ -32,11 +32,18 @@
#include "image_compress_etcpak.h"
-void register_etcpak_types() {
+void initialize_etcpak_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
Image::_image_compress_etc1_func = _compress_etc1;
Image::_image_compress_etc2_func = _compress_etc2;
Image::_image_compress_bc_func = _compress_bc;
}
-void unregister_etcpak_types() {
+void uninitialize_etcpak_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/etcpak/register_types.h b/modules/etcpak/register_types.h
index 30ce974d08..2048a62737 100644
--- a/modules/etcpak/register_types.h
+++ b/modules/etcpak/register_types.h
@@ -31,7 +31,11 @@
#ifndef ETCPAK_REGISTER_TYPES_H
#define ETCPAK_REGISTER_TYPES_H
-void register_etcpak_types();
-void unregister_etcpak_types();
+#include "modules/register_module_types.h"
+
+#include "modules/register_module_types.h"
+
+void initialize_etcpak_module(ModuleInitializationLevel p_level);
+void uninitialize_etcpak_module(ModuleInitializationLevel p_level);
#endif // ETCPAK_REGISTER_TYPES_H
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index d23c4b637c..4b2ea6faa5 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -49,6 +49,7 @@ if env["builtin_freetype"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -87,7 +88,7 @@ if env["builtin_freetype"]:
# Also needed in main env for scene/
env.Prepend(CPPPATH=[thirdparty_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG"])
+ env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", "FT_CONFIG_OPTION_SYSTEM_ZLIB"])
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
diff --git a/modules/freetype/register_types.cpp b/modules/freetype/register_types.cpp
index 28fd1a57c9..a5a60c0368 100644
--- a/modules/freetype/register_types.cpp
+++ b/modules/freetype/register_types.cpp
@@ -30,6 +30,14 @@
#include "register_types.h"
-void register_freetype_types() {}
+void initialize_freetype_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
-void unregister_freetype_types() {}
+void uninitialize_freetype_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h
index c4eb241636..3399c0b3bc 100644
--- a/modules/freetype/register_types.h
+++ b/modules/freetype/register_types.h
@@ -31,7 +31,9 @@
#ifndef FREETYPE_REGISTER_TYPES_H
#define FREETYPE_REGISTER_TYPES_H
-void register_freetype_types();
-void unregister_freetype_types();
+#include "modules/register_module_types.h"
+
+void initialize_freetype_module(ModuleInitializationLevel p_level);
+void uninitialize_freetype_module(ModuleInitializationLevel p_level);
#endif // FREETYPE_REGISTER_TYPES_H
diff --git a/modules/freetype/uwpdef.h b/modules/freetype/uwpdef.h
index 05aaae61b5..f85f293171 100644
--- a/modules/freetype/uwpdef.h
+++ b/modules/freetype/uwpdef.h
@@ -28,6 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef UWPDEF_H
+#define UWPDEF_H
+
// "generic" is a reserved keyword in C++/CX code
// this avoids the errors in the variable name from Freetype code
#define generic freetype_generic
+
+#endif // UWPDEF_H
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index d0926d317b..10cf783e73 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -144,6 +144,7 @@
[/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.
+ [b]Note:[/b] You have to import the files into the engine first to load them using [method load]. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data].
</description>
</method>
<method name="preload">
@@ -184,27 +185,24 @@
<method name="range" qualifiers="vararg">
<return type="Array" />
<description>
- Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
- Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run.
- [code]range()[/code] converts all arguments to [int] before processing.
+ Returns an array with the given range. [method range] can be called in three ways:
+ [code]range(n: int)[/code]: Starts from 0, increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The argument [code]n[/code] is [b]exclusive[/b].
+ [code]range(b: int, n: int)[/code]: Starts from [code]b[/code], increases by steps of 1, and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively.
+ [code]range(b: int, n: int, s: int)[/code]: Starts from [code]b[/code], increases/decreases by steps of [code]s[/code], and stops [i]before[/i] [code]n[/code]. The arguments [code]b[/code] and [code]n[/code] are [b]inclusive[/b] and [b]exclusive[/b], respectively. The argument [code]s[/code] [b]can[/b] be negative, but not [code]0[/code]. If [code]s[/code] is [code]0[/code], an error message is printed.
+ [method range] converts all arguments to [int] before processing.
+ [b]Note:[/b] Returns an empty array if no value meets the value constraint (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
+ Examples:
[codeblock]
- 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]
+ print(range(4)) # Prints [0, 1, 2, 3]
+ print(range(2, 5)) # Prints [2, 3, 4]
+ print(range(0, 6, 2)) # Prints [0, 2, 4]
+ print(range(4, 1, -1)) # Prints [4, 3, 2]
[/codeblock]
To iterate over an [Array] backwards, use:
[codeblock]
var array = [3, 6, 9]
- var i := array.size() - 1
- while i &gt;= 0:
- print(array[i])
- i -= 1
+ for i in range(array.size(), 0, -1):
+ print(array[i - 1])
[/codeblock]
Output:
[codeblock]
@@ -260,4 +258,162 @@
[b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead.
</constant>
</constants>
+ <annotations>
+ <annotation name="@export">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_category">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_color_no_alpha">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_dir">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_enum" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="names" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_exp_easing" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="hints" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_file" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="names" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_navigation">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_physics">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_render">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_navigation">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_physics">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_render">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_global_dir">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_global_file" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_group">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="prefix" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_multiline">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_node_path" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="type" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_placeholder">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_range" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="min" type="float" />
+ <argument index="1" name="max" type="float" />
+ <argument index="2" name="step" type="float" default="1.0" />
+ <argument index="3" name="extra_hints" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_subgroup">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="prefix" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@icon">
+ <return type="void" />
+ <argument index="0" name="icon_path" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@onready">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@rpc" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="mode" type="String" default="&quot;&quot;" />
+ <argument index="1" name="sync" type="String" default="&quot;&quot;" />
+ <argument index="2" name="transfer_mode" type="String" default="&quot;&quot;" />
+ <argument index="3" name="transfer_channel" type="int" default="0" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@tool">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@warning_ignore" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="warning" type="String" />
+ <description>
+ </description>
+ </annotation>
+ </annotations>
</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index e3f0ddfc35..4372bb33ba 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -55,7 +55,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
+ bool in_node_ref = false;
bool in_annotation = false;
+ bool in_string_name = false;
bool is_hex_notation = false;
bool is_bin_notation = false;
bool expect_type = false;
@@ -165,6 +167,12 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
region_color = node_path_color;
}
+ if (in_node_ref && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ region_color = node_ref_color;
+ }
+ if (in_string_name && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
+ region_color = string_name_color;
+ }
prev_color = region_color;
highlighter_info["color"] = region_color;
@@ -387,24 +395,42 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_member_variable = false;
}
- if (!in_node_path && in_region == -1 && str[j] == '$') {
+ if (!in_node_path && in_region == -1 && (str[j] == '^')) {
in_node_path = true;
- } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) {
+ } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) {
in_node_path = false;
}
+ if (!in_node_ref && in_region == -1 && (str[j] == '$' || str[j] == '%')) {
+ in_node_ref = true;
+ } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) {
+ in_node_ref = false;
+ }
+
if (!in_annotation && in_region == -1 && str[j] == '@') {
in_annotation = true;
} else if (in_region != -1 || is_a_symbol) {
in_annotation = false;
}
+ if (!in_string_name && in_region == -1 && str[j] == '&') {
+ in_string_name = true;
+ } else if (in_region != -1 || is_a_symbol) {
+ in_string_name = false;
+ }
+
if (in_node_path) {
next_type = NODE_PATH;
color = node_path_color;
+ } else if (in_node_ref) {
+ next_type = NODE_REF;
+ color = node_ref_color;
} else if (in_annotation) {
next_type = ANNOTATION;
color = annotation_color;
+ } else if (in_string_name) {
+ next_type = STRING_NAME;
+ color = string_name_color;
} else if (in_keyword) {
next_type = KEYWORD;
color = keyword_color;
@@ -510,9 +536,8 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
/* Autoloads. */
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.value();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
keywords[info.name] = usertype_color;
}
@@ -593,17 +618,23 @@ void GDScriptSyntaxHighlighter::_update_cache() {
if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
- node_path_color = Color(0.39, 0.76, 0.35);
+ node_path_color = Color(0.72, 0.77, 0.49);
+ node_ref_color = Color(0.39, 0.76, 0.35);
annotation_color = Color(1.0, 0.7, 0.45);
+ string_name_color = Color(1.0, 0.66, 0.72);
} else {
function_definition_color = Color(0.0, 0.65, 0.73);
- node_path_color = Color(0.32, 0.55, 0.29);
+ node_path_color = Color(0.62, 0.67, 0.39);
+ node_ref_color = Color(0.32, 0.55, 0.29);
annotation_color = Color(0.8, 0.5, 0.25);
+ string_name_color = Color(0.9, 0.56, 0.62);
}
EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_reference_color", node_ref_color);
EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/string_name_color", string_name_color);
if (text_edit_color_theme == "Default" || godot_2_theme) {
EditorSettings::get_singleton()->set_initial_value(
"text_editor/theme/highlighting/gdscript/function_definition_color",
@@ -614,14 +645,24 @@ void GDScriptSyntaxHighlighter::_update_cache() {
node_path_color,
true);
EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/gdscript/node_reference_color",
+ node_ref_color,
+ true);
+ EditorSettings::get_singleton()->set_initial_value(
"text_editor/theme/highlighting/gdscript/annotation_color",
annotation_color,
true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/gdscript/string_name_color",
+ string_name_color,
+ true);
}
function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color");
node_path_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_path_color");
+ node_ref_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_reference_color");
annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color");
+ string_name_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/string_name_color");
type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 1ae0d72896..7987582f07 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -45,7 +45,7 @@ private:
bool line_only = false;
};
Vector<ColorRegion> color_regions;
- Map<int, int> color_region_cache;
+ HashMap<int, int> color_region_cache;
HashMap<StringName, Color> keywords;
HashMap<StringName, Color> member_keywords;
@@ -54,7 +54,9 @@ private:
NONE,
REGION,
NODE_PATH,
+ NODE_REF,
ANNOTATION,
+ STRING_NAME,
SYMBOL,
NUMBER,
FUNCTION,
@@ -74,7 +76,9 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
+ Color node_ref_color;
Color annotation_color;
+ Color string_name_color;
Color type_color;
void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index a8f4483cf4..9b540b16f2 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
// Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
Error err;
- RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
+ Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
if (err) {
ERR_PRINT("Failed to load " + p_path);
return err;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index e7b40aa367..969a50f48c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "editor/editor_translation_parser.h"
#include "modules/gdscript/gdscript_parser.h"
@@ -44,9 +44,9 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
// 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;
+ HashSet<StringName> assignment_patterns;
+ HashSet<StringName> first_arg_patterns;
+ HashSet<StringName> second_arg_patterns;
// FileDialog patterns.
StringName fd_add_filter = "add_filter";
StringName fd_set_filter = "set_filters";
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
new file mode 100644
index 0000000000..556afe994b
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
@@ -0,0 +1,9 @@
+# meta-description: Basic import script template
+@tool
+extends EditorScenePostImport
+
+
+# Called by the editor when a scene has this script set as the import script in the import tab.
+func _post_import(scene: Node) -> Object:
+ # Modify the contents of the scene upon import.
+ return scene # Return the modified root node when you're done.
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
new file mode 100644
index 0000000000..875afb4fc0
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
@@ -0,0 +1,7 @@
+# meta-description: Basic import script template (no comments)
+@tool
+extends EditorScenePostImport
+
+
+func _post_import(scene: Node) -> Object:
+ return scene
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 55c7ace938..d752bef14f 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -51,7 +51,7 @@
#endif
#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
+#include "editor/editor_paths.h"
#endif
///////////////////////////
@@ -62,7 +62,7 @@ GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const {
bool ok;
- int v = ClassDB::get_integer_constant(name, p_name, &ok);
+ int64_t v = ClassDB::get_integer_constant(name, p_name, &ok);
if (ok) {
r_ret = v;
@@ -82,7 +82,7 @@ Variant GDScriptNativeClass::_new() {
RefCounted *rc = Object::cast_to<RefCounted>(o);
if (rc) {
- return REF(rc);
+ return Ref<RefCounted>(rc);
} else {
return o;
}
@@ -128,6 +128,7 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
return;
}
}
+ ERR_FAIL_NULL(p_script->implicit_initializer);
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
}
@@ -195,7 +196,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
}
r_error.error = Callable::CallError::CALL_OK;
- REF ref;
+ Ref<RefCounted> ref;
Object *owner = nullptr;
GDScript *_baseptr = this;
@@ -213,7 +214,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
- ref = REF(r);
+ ref = Ref<RefCounted>(r);
}
GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
@@ -277,6 +278,11 @@ void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_
GDScriptFunction *func = E.value;
MethodInfo mi;
mi.name = E.key;
+
+ if (func->is_static()) {
+ mi.flags |= METHOD_FLAG_STATIC;
+ }
+
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arginfo = func->get_argument_type(i);
#ifdef TOOLS_ENABLED
@@ -340,14 +346,14 @@ bool GDScript::has_method(const StringName &p_method) const {
}
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
- const Map<StringName, GDScriptFunction *>::Element *E = member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
return MethodInfo();
}
- GDScriptFunction *func = E->get();
+ GDScriptFunction *func = E->value;
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E->key;
for (int i = 0; i < func->get_argument_count(); i++) {
mi.arguments.push_back(func->get_argument_type(i));
}
@@ -359,9 +365,9 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
- const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property);
+ HashMap<StringName, Variant>::ConstIterator E = member_default_values_cache.find(p_property);
if (E) {
- r_value = E->get();
+ r_value = E->value;
return true;
}
@@ -427,7 +433,7 @@ void GDScript::set_source_code(const String &p_code) {
}
#ifdef TOOLS_ENABLED
-void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) {
+void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
if (base_cache.is_valid()) {
base_cache->_update_exports_values(values, propnames);
}
@@ -535,6 +541,9 @@ void GDScript::_update_doc() {
List<PropertyInfo> props;
_get_script_property_list(&props, false);
for (int i = 0; i < props.size(); i++) {
+ if (props[i].usage & PROPERTY_USAGE_CATEGORY || props[i].usage & PROPERTY_USAGE_GROUP || props[i].usage & PROPERTY_USAGE_SUBGROUP) {
+ continue;
+ }
ScriptMemberInfo scr_member_info;
scr_member_info.propinfo = props[i];
scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
@@ -759,13 +768,13 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
if ((changed || p_instance_to_update) && placeholders.size()) { //hm :(
// update placeholders if any
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
if (changed) {
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ for (PlaceHolderScriptInstance *E : placeholders) {
+ E->update(propnames, values);
}
} else {
p_instance_to_update->update(propnames, values);
@@ -788,10 +797,10 @@ void GDScript::update_exports() {
return;
}
- Set<ObjectID> copy = inheriters_cache; //might get modified
+ HashSet<ObjectID> copy = inheriters_cache; //might get modified
- for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) {
- Object *id = ObjectDB::get_instance(E->get());
+ for (const ObjectID &E : copy) {
+ Object *id = ObjectDB::get_instance(E);
GDScript *s = Object::cast_to<GDScript>(id);
if (!s) {
continue;
@@ -839,7 +848,7 @@ Error GDScript::reload(bool p_keep_state) {
// Loading a template, don't parse.
#ifdef TOOLS_ENABLED
- if (EditorSettings::get_singleton() && basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) {
+ if (EditorPaths::get_singleton() && basedir.begins_with(EditorPaths::get_singleton()->get_project_script_templates_dir())) {
return OK;
}
#endif
@@ -866,7 +875,7 @@ Error GDScript::reload(bool p_keep_state) {
}
// TODO: Show all error messages.
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_PARSE_ERROR);
+ return ERR_PARSE_ERROR;
}
GDScriptAnalyzer analyzer(&parser);
@@ -882,7 +891,7 @@ Error GDScript::reload(bool p_keep_state) {
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
e = e->next();
}
- ERR_FAIL_V(ERR_PARSE_ERROR);
+ return ERR_PARSE_ERROR;
}
bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
@@ -900,7 +909,7 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
_err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
- ERR_FAIL_V(ERR_COMPILATION_FAILED);
+ return ERR_COMPILATION_FAILED;
} else {
return err;
}
@@ -929,7 +938,7 @@ ScriptLanguage *GDScript::get_language() const {
return GDScriptLanguage::get_singleton();
}
-void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
+void GDScript::get_constants(HashMap<StringName, Variant> *p_constants) {
if (p_constants) {
for (const KeyValue<StringName, Variant> &E : constants) {
(*p_constants)[E.key] = E.value;
@@ -937,26 +946,26 @@ void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
}
}
-void GDScript::get_members(Set<StringName> *p_members) {
+void GDScript::get_members(HashSet<StringName> *p_members) {
if (p_members) {
- for (Set<StringName>::Element *E = members.front(); E; E = E->next()) {
- p_members->insert(E->get());
+ for (const StringName &E : members) {
+ p_members->insert(E);
}
}
}
-const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const {
- return rpc_functions;
+const Variant GDScript::get_rpc_config() const {
+ return rpc_config;
}
Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
- Map<StringName, GDScriptFunction *>::Element *E = top->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method);
if (E) {
- ERR_FAIL_COND_V_MSG(!E->get()->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script.");
+ ERR_FAIL_COND_V_MSG(!E->value->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script.");
- return E->get()->call(nullptr, p_args, p_argcount, r_error);
+ return E->value->call(nullptr, p_args, p_argcount, r_error);
}
top = top->_base;
}
@@ -971,17 +980,17 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
const GDScript *top = this;
while (top) {
{
- const Map<StringName, Variant>::Element *E = top->constants.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
}
{
- const Map<StringName, Ref<GDScript>>::Element *E = subclasses.find(p_name);
+ HashMap<StringName, Ref<GDScript>>::ConstIterator E = subclasses.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
}
@@ -1049,7 +1058,7 @@ Error GDScript::load_source_code(const String &p_path) {
w[len] = 0;
String s;
- if (s.parse_utf8((const char *)w)) {
+ if (s.parse_utf8((const char *)w) != OK) {
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
@@ -1061,7 +1070,7 @@ Error GDScript::load_source_code(const String &p_path) {
return OK;
}
-const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const {
+const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const {
return member_functions;
}
@@ -1203,38 +1212,32 @@ void GDScript::_save_orphaned_subclasses() {
void GDScript::_init_rpc_methods_properties() {
// Copy the base rpc methods so we don't mask their IDs.
- rpc_functions.clear();
+ rpc_config.clear();
if (base.is_valid()) {
- rpc_functions = base->rpc_functions;
+ rpc_config = base->rpc_config.duplicate();
}
GDScript *cscript = this;
- Map<StringName, Ref<GDScript>>::Element *sub_E = subclasses.front();
+ HashMap<StringName, Ref<GDScript>>::Iterator sub_E = subclasses.begin();
while (cscript) {
// RPC Methods
for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
- Multiplayer::RPCConfig config = E.value->get_rpc_config();
- if (config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
- config.name = E.value->get_name();
- if (rpc_functions.find(config) == -1) {
- rpc_functions.push_back(config);
- }
+ Variant config = E.value->get_rpc_config();
+ if (config.get_type() != Variant::NIL) {
+ rpc_config[E.value->get_name()] = config;
}
}
if (cscript != this) {
- sub_E = sub_E->next();
+ ++sub_E;
}
if (sub_E) {
- cscript = sub_E->get().ptr();
+ cscript = sub_E->value.ptr();
} else {
cscript = nullptr;
}
}
-
- // Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
GDScript::~GDScript() {
@@ -1253,6 +1256,14 @@ GDScript::~GDScript() {
memdelete(E.value);
}
+ if (implicit_initializer) {
+ memdelete(implicit_initializer);
+ }
+
+ if (implicit_ready) {
+ memdelete(implicit_ready);
+ }
+
if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
GDScriptCache::remove_script(get_path());
}
@@ -1282,9 +1293,9 @@ GDScript::~GDScript() {
bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
//member
{
- const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name);
+ HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
- const GDScript::MemberInfo *member = &E->get();
+ const GDScript::MemberInfo *member = &E->value;
if (member->setter) {
const Variant *val = &p_value;
Callable::CallError err;
@@ -1325,13 +1336,13 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
GDScript *sptr = script.ptr();
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
if (E) {
Variant name = p_name;
const Variant *args[2] = { &name, &p_value };
Callable::CallError err;
- Variant ret = E->get()->call(this, (const Variant **)args, 2, err);
+ Variant ret = E->value->call(this, (const Variant **)args, 2, err);
if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) {
return true;
}
@@ -1346,16 +1357,16 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
const GDScript *sptr = script.ptr();
while (sptr) {
{
- const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name);
+ HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name);
if (E) {
- if (E->get().getter) {
+ if (E->value.getter) {
Callable::CallError err;
- r_ret = const_cast<GDScriptInstance *>(this)->callp(E->get().getter, nullptr, 0, err);
+ r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err);
if (err.error == Callable::CallError::CALL_OK) {
return true;
}
}
- r_ret = members[E->get().index];
+ r_ret = members[E->value.index];
return true; //index found
}
}
@@ -1363,9 +1374,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
{
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, Variant>::Element *E = sl->constants.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = sl->constants.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true; //index found
}
sl = sl->_base;
@@ -1376,9 +1387,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
// Signals.
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, Vector<StringName>>::Element *E = sl->_signals.find(p_name);
+ HashMap<StringName, Vector<StringName>>::ConstIterator E = sl->_signals.find(p_name);
if (E) {
- r_ret = Signal(this->owner, E->key());
+ r_ret = Signal(this->owner, E->key);
return true; //index found
}
sl = sl->_base;
@@ -1389,14 +1400,12 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
// Methods.
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sl->member_functions.find(p_name);
if (E) {
- Multiplayer::RPCConfig config;
- config.name = p_name;
- if (sptr->rpc_functions.find(config) != -1) {
- r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key())));
+ if (sptr->rpc_config.has(p_name)) {
+ r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key)));
} else {
- r_ret = Callable(this->owner, E->key());
+ r_ret = Callable(this->owner, E->key);
}
return true; //index found
}
@@ -1405,13 +1414,13 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
{
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
if (E) {
Variant name = p_name;
const Variant *args[1] = { &name };
Callable::CallError err;
- Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
+ Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
r_ret = ret;
return true;
@@ -1449,10 +1458,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
List<PropertyInfo> props;
while (sptr) {
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
if (E) {
Callable::CallError err;
- Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
+ Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
if (err.error == Callable::CallError::CALL_OK) {
ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries.");
@@ -1475,6 +1484,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
if (d.has("usage")) {
pinfo.usage = d["usage"];
}
+ if (d.has("class_name")) {
+ pinfo.class_name = d["class_name"];
+ }
props.push_back(pinfo);
}
@@ -1512,7 +1524,6 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
MethodInfo mi;
mi.name = E.key;
- mi.flags |= METHOD_FLAG_FROM_SCRIPT;
for (int i = 0; i < E.value->get_argument_count(); i++) {
mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
}
@@ -1525,7 +1536,7 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
bool GDScriptInstance::has_method(const StringName &p_method) const {
const GDScript *sptr = script.ptr();
while (sptr) {
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method);
if (E) {
return true;
}
@@ -1537,10 +1548,22 @@ bool GDScriptInstance::has_method(const StringName &p_method) const {
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
+ if (unlikely(p_method == SNAME("_ready"))) {
+ // Call implicit ready first, including for the super classes.
+ while (sptr) {
+ if (sptr->implicit_ready) {
+ sptr->implicit_ready->call(this, nullptr, 0, r_error);
+ }
+ sptr = sptr->_base;
+ }
+
+ // Reset this back for the regular call.
+ sptr = script.ptr();
+ }
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method);
if (E) {
- return E->get()->call(this, p_args, p_argcount, r_error);
+ return E->value->call(this, p_args, p_argcount, r_error);
}
sptr = sptr->_base;
}
@@ -1555,10 +1578,10 @@ void GDScriptInstance::notification(int p_notification) {
GDScript *sptr = script.ptr();
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
if (E) {
Callable::CallError err;
- E->get()->call(this, args, 1, err);
+ E->value->call(this, args, 1, err);
if (err.error != Callable::CallError::CALL_OK) {
//print error about notification call
}
@@ -1598,15 +1621,13 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
-const Vector<Multiplayer::RPCConfig> GDScriptInstance::get_rpc_methods() const {
- return script->get_rpc_methods();
+const Variant GDScriptInstance::get_rpc_config() const {
+ return script->get_rpc_config();
}
void GDScriptInstance::reload_members() {
#ifdef DEBUG_ENABLED
- members.resize(script->member_indices.size()); //resize
-
Vector<Variant> new_members;
new_members.resize(script->member_indices.size());
@@ -1618,6 +1639,8 @@ void GDScriptInstance::reload_members() {
}
}
+ members.resize(new_members.size()); //resize
+
//apply
members = new_members;
@@ -1882,7 +1905,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//when someone asks you why dynamically typed languages are easier to write....
- Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> to_reload;
+ HashMap<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> to_reload;
//as scripts are going to be reloaded, must proceed without locking here
@@ -1895,11 +1918,11 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
continue;
}
- to_reload.insert(script, Map<ObjectID, List<Pair<StringName, Variant>>>());
+ to_reload.insert(script, HashMap<ObjectID, List<Pair<StringName, Variant>>>());
if (!p_soft_reload) {
//save state and remove script from instances
- Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
+ HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
while (script->instances.front()) {
Object *obj = script->instances.front()->get();
@@ -1916,7 +1939,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#ifdef TOOLS_ENABLED
while (script->placeholders.size()) {
- Object *obj = script->placeholders.front()->get()->get_owner();
+ Object *obj = (*script->placeholders.begin())->get_owner();
//save instance info
if (obj->get_script_instance()) {
@@ -1926,7 +1949,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
obj->set_script(Variant());
} else {
// no instance found. Let's remove it so we don't loop forever
- script->placeholders.erase(script->placeholders.front()->get());
+ script->placeholders.erase(*script->placeholders.begin());
}
}
@@ -1938,7 +1961,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
}
- for (KeyValue<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
+ for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
Ref<GDScript> scr = E.key;
scr->reload(p_soft_reload);
@@ -2232,9 +2255,13 @@ GDScriptLanguage::GDScriptLanguage() {
GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
- String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
- bool default_enabled = !warning.begins_with("unsafe_");
- GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled);
+ GDScriptWarning::Code code = (GDScriptWarning::Code)i;
+ Variant default_enabled = GDScriptWarning::get_default_value(code);
+ String path = GDScriptWarning::get_settings_path_from_code(code);
+ GLOBAL_DEF(path, default_enabled);
+
+ PropertyInfo property_info = GDScriptWarning::get_property_info(code);
+ ProjectSettings::get_singleton()->set_custom_property_info(path, property_info);
}
#endif // DEBUG_ENABLED
}
@@ -2277,13 +2304,13 @@ void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const
}
Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_name) {
- Map<String, ObjectID>::Element *orphan_subclass_element = orphan_subclasses.find(p_qualified_name);
+ HashMap<String, ObjectID>::Iterator orphan_subclass_element = orphan_subclasses.find(p_qualified_name);
if (!orphan_subclass_element) {
return Ref<GDScript>();
}
- ObjectID orphan_subclass = orphan_subclass_element->get();
+ ObjectID orphan_subclass = orphan_subclass_element->value;
Object *obj = ObjectDB::get_instance(orphan_subclass);
- orphan_subclasses.erase(orphan_subclass_element);
+ orphan_subclasses.remove(orphan_subclass_element);
if (!obj) {
return Ref<GDScript>();
}
@@ -2292,7 +2319,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2353,7 +2380,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
}
}
-Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<GDScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
@@ -2372,18 +2399,18 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou
}
if (ScriptServer::is_reload_scripts_on_save_enabled()) {
- GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, false);
+ GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true);
}
return OK;
}
-void ResourceFormatSaverGDScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
if (Object::cast_to<GDScript>(*p_resource)) {
p_extensions->push_back("gd");
}
}
-bool ResourceFormatSaverGDScript::recognize(const RES &p_resource) const {
+bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<GDScript>(*p_resource) != nullptr;
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index caca7d8ca5..5123cccddd 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -37,6 +37,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
+#include "core/templates/rb_set.h"
#include "gdscript_function.h"
class GDScriptNativeClass : public RefCounted {
@@ -80,48 +81,49 @@ class GDScript : public Script {
GDScript *_base = nullptr; //fast pointer access
GDScript *_owner = nullptr; //for subclasses
- Set<StringName> members; //members are just indices to the instantiated script.
- Map<StringName, Variant> constants;
- Map<StringName, GDScriptFunction *> member_functions;
- Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
- Map<StringName, Ref<GDScript>> subclasses;
- Map<StringName, Vector<StringName>> _signals;
- Vector<Multiplayer::RPCConfig> rpc_functions;
+ HashSet<StringName> members; //members are just indices to the instantiated script.
+ HashMap<StringName, Variant> constants;
+ HashMap<StringName, GDScriptFunction *> member_functions;
+ HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
+ HashMap<StringName, Ref<GDScript>> subclasses;
+ HashMap<StringName, Vector<StringName>> _signals;
+ Dictionary rpc_config;
#ifdef TOOLS_ENABLED
- Map<StringName, int> member_lines;
- Map<StringName, Variant> member_default_values;
+ HashMap<StringName, int> member_lines;
+ HashMap<StringName, Variant> member_default_values;
List<PropertyInfo> members_cache;
- Map<StringName, Variant> member_default_values_cache;
+ HashMap<StringName, Variant> member_default_values_cache;
Ref<GDScript> base_cache;
- Set<ObjectID> inheriters_cache;
+ HashSet<ObjectID> inheriters_cache;
bool source_changed_cache = false;
bool placeholder_fallback_enabled = false;
- void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
DocData::ClassDoc doc;
Vector<DocData::ClassDoc> docs;
String doc_brief_description;
String doc_description;
Vector<DocData::TutorialDoc> doc_tutorials;
- Map<String, String> doc_functions;
- Map<String, String> doc_variables;
- Map<String, String> doc_constants;
- Map<String, String> doc_signals;
- Map<String, DocData::EnumDoc> doc_enums;
+ HashMap<String, String> doc_functions;
+ HashMap<String, String> doc_variables;
+ HashMap<String, String> doc_constants;
+ HashMap<String, String> doc_signals;
+ HashMap<String, DocData::EnumDoc> doc_enums;
void _clear_doc();
void _update_doc();
void _add_doc(const DocData::ClassDoc &p_inner_class);
#endif
- Map<StringName, PropertyInfo> member_info;
+ HashMap<StringName, PropertyInfo> member_info;
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
+ GDScriptFunction *implicit_ready = nullptr;
int subclass_count = 0;
- Set<Object *> instances;
+ RBSet<Object *> instances;
//exported members
String source;
String path;
@@ -139,14 +141,14 @@ class GDScript : public Script {
String _get_debug_path() const;
#ifdef TOOLS_ENABLED
- Set<PlaceHolderScriptInstance *> placeholders;
+ HashSet<PlaceHolderScriptInstance *> placeholders;
//void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
#ifdef DEBUG_ENABLED
- Map<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state;
+ HashMap<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state;
#endif
@@ -176,14 +178,14 @@ public:
bool inherits_script(const Ref<Script> &p_script) const override;
- const Map<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
- const Map<StringName, Variant> &get_constants() const { return constants; }
- const Set<StringName> &get_members() const { return members; }
+ const HashMap<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
+ const HashMap<StringName, Variant> &get_constants() const { return constants; }
+ const HashSet<StringName> &get_members() const { return members; }
const GDScriptDataType &get_member_type(const StringName &p_member) const {
CRASH_COND(!member_indices.has(p_member));
return member_indices[p_member].data_type;
}
- const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
+ const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
@@ -193,8 +195,8 @@ public:
bool is_tool() const override { return tool; }
Ref<GDScript> get_base() const;
- const Map<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; }
- const Map<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only
+ const HashMap<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; }
+ const HashMap<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -245,10 +247,10 @@ public:
return -1;
}
- virtual void get_constants(Map<StringName, Variant> *p_constants) override;
- virtual void get_members(Set<StringName> *p_members) override;
+ virtual void get_constants(HashMap<StringName, Variant> *p_constants) override;
+ virtual void get_members(HashSet<StringName> *p_members) override;
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
+ virtual const Variant get_rpc_config() const override;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@@ -270,7 +272,7 @@ class GDScriptInstance : public ScriptInstance {
Object *owner = nullptr;
Ref<GDScript> script;
#ifdef DEBUG_ENABLED
- Map<StringName, int> member_indices_cache; //used only for hot script reloading
+ HashMap<StringName, int> member_indices_cache; //used only for hot script reloading
#endif
Vector<Variant> members;
bool base_ref_counted;
@@ -302,7 +304,7 @@ public:
void reload_members();
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
+ virtual const Variant get_rpc_config() const;
GDScriptInstance();
~GDScriptInstance();
@@ -315,8 +317,8 @@ class GDScriptLanguage : public ScriptLanguage {
Variant *_global_array = nullptr;
Vector<Variant> global_array;
- Map<StringName, int> globals;
- Map<StringName, Variant> named_globals;
+ HashMap<StringName, int> globals;
+ HashMap<StringName, Variant> named_globals;
struct CallLevel {
Variant *stack = nullptr;
@@ -348,7 +350,7 @@ class GDScriptLanguage : public ScriptLanguage {
bool profiling;
uint64_t script_frame_time;
- Map<String, ObjectID> orphan_subclasses;
+ HashMap<String, ObjectID> orphan_subclasses;
public:
int calls;
@@ -367,7 +369,7 @@ public:
if (_debug_call_stack_pos >= _debug_max_call_stack) {
//stack overflow
- _debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
+ _debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -427,8 +429,8 @@ public:
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
- _FORCE_INLINE_ const Map<StringName, int> &get_global_map() const { return globals; }
- _FORCE_INLINE_ const Map<StringName, Variant> &get_named_globals_map() const { return named_globals; }
+ _FORCE_INLINE_ const HashMap<StringName, int> &get_global_map() const { return globals; }
+ _FORCE_INLINE_ const HashMap<StringName, Variant> &get_named_globals_map() const { return named_globals; }
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
@@ -449,7 +451,7 @@ public:
virtual bool is_using_templates() override;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override;
virtual Script *create_script() const override;
virtual bool has_named_classes() const override;
virtual bool supports_builtin_mode() const override;
@@ -487,6 +489,7 @@ public:
virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override;
virtual void profiling_stop() override;
@@ -512,7 +515,7 @@ public:
class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
@@ -521,9 +524,9 @@ public:
class ResourceFormatSaverGDScript : public ResourceFormatSaver {
public:
- virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
- virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
- virtual bool recognize(const RES &p_resource) const;
+ virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
};
#endif // GDSCRIPT_H
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d346264933..7ed440929c 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -655,43 +655,43 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
}
- } else {
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ }
- if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
- }
- } 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);
-#endif
- }
- if (member.variable->initializer->get_datatype().is_variant()) {
- // TODO: Warn unsafe assign.
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = specified_type;
+
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
+ } 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);
+#endif
}
- } else if (member.variable->infer_datatype) {
- if (member.variable->initializer == nullptr) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
- } else if (!datatype.is_set() || datatype.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.is_variant()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
- } 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);
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
+ mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } 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;
@@ -860,6 +860,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
break;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ // No-op, but needed to silence warnings.
+ break;
case GDScriptParser::ClassNode::Member::UNDEFINED:
ERR_PRINT("Trying to resolve undefined member.");
break;
@@ -898,7 +901,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -947,7 +950,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -1160,8 +1163,16 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
}
} else {
- GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
- p_function->set_datatype(return_type);
+ if (p_function->return_type != nullptr) {
+ p_function->set_datatype(resolve_datatype(p_function->return_type));
+ } else {
+ // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here.
+ // It's not "undetected" to not mix up with unknown functions.
+ GDScriptParser::DataType return_type;
+ return_type.type_source = GDScriptParser::DataType::INFERRED;
+ return_type.kind = GDScriptParser::DataType::VARIANT;
+ p_function->set_datatype(return_type);
+ }
#ifdef TOOLS_ENABLED
// Check if the function signature matches the parent. If not it's an error since it breaks polymorphism.
@@ -1231,7 +1242,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
GDScriptParser::DataType return_type = p_function->body->get_datatype();
- if (p_function->get_datatype().has_no_type() && return_type.is_set()) {
+ if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) {
// Use the suite inferred type if return isn't explicitly set.
return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
@@ -1279,7 +1290,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : stmt->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -1514,10 +1525,22 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
+ GDScriptParser::DataType explicit_type;
+ if (p_constant->datatype_specifier != nullptr) {
+ explicit_type = resolve_datatype(p_constant->datatype_specifier);
+ explicit_type.is_meta_type = false;
+ }
+
if (p_constant->initializer != nullptr) {
reduce_expression(p_constant->initializer);
if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer);
+ const_fold_array(array);
+
+ // Can only infer typed array if it has elements.
+ if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) {
+ update_array_literal_element_type(explicit_type, array);
+ }
} else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
}
@@ -1536,8 +1559,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
}
if (p_constant->datatype_specifier != nullptr) {
- GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
- explicit_type.is_meta_type = false;
if (!is_type_compatible(explicit_type, type)) {
push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
#ifdef DEBUG_ENABLED
@@ -1639,8 +1660,8 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
is_shadowing(p_match_pattern->bind, "pattern bind");
- if (p_match_pattern->bind->usages == 0) {
- parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name);
+ if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
+ parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
#endif
break;
@@ -2057,7 +2078,8 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
p_await->set_datatype(awaiting_type);
#ifdef DEBUG_ENABLED
- if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) {
+ awaiting_type = p_await->to_await->get_datatype();
+ if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) {
parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT);
}
#endif
@@ -2174,7 +2196,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
- Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
+ HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
for (int i = 0; i < p_call->arguments.size(); i++) {
reduce_expression(p_call->arguments[i]);
if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
@@ -2259,6 +2281,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
p_call->is_constant = true;
@@ -2361,6 +2384,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2403,6 +2427,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2881,7 +2906,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
bool valid = false;
- int int_constant = ClassDB::get_integer_constant(native, name, &valid);
+ int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid);
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
@@ -3304,8 +3329,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::VECTOR2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
case Variant::TRANSFORM2D:
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
index_type.builtin_type != Variant::STRING;
break;
@@ -3368,6 +3396,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_INT64_ARRAY:
case Variant::VECTOR2I:
case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
result_type.builtin_type = Variant::INT;
break;
// Return float.
@@ -3375,6 +3404,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::VECTOR2:
case Variant::VECTOR3:
+ case Variant::VECTOR4:
case Variant::QUATERNION:
result_type.builtin_type = Variant::FLOAT;
break;
@@ -3405,6 +3435,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
break;
// Depends on the index.
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
@@ -4218,13 +4249,11 @@ Error GDScriptAnalyzer::resolve_program() {
resolve_class_interface(parser->head);
resolve_class_body(parser->head);
- List<String> parser_keys;
- depended_parsers.get_key_list(&parser_keys);
- for (const String &E : parser_keys) {
- if (depended_parsers[E].is_null()) {
+ for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) {
+ if (K.value.is_null()) {
return ERR_PARSE_ERROR;
}
- depended_parsers[E]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ K.value->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 519e1975c4..3966b81b6e 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -33,7 +33,7 @@
#include "core/object/object.h"
#include "core/object/ref_counted.h"
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index e72069bcd5..fa158591fd 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -84,11 +84,14 @@ uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type
case Variant::VECTOR3:
case Variant::VECTOR3I:
case Variant::TRANSFORM2D:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
case Variant::PLANE:
case Variant::QUATERNION:
case Variant::AABB:
case Variant::BASIS:
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
case Variant::COLOR:
case Variant::STRING_NAME:
case Variant::NODE_PATH:
@@ -155,7 +158,7 @@ void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.reverse();
}
-void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
+void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) {
function = memnew(GDScriptFunction);
debug_stack = EngineDebugger::is_active();
@@ -196,10 +199,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
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;
+ for (const KeyValue<Variant, int> &K : constant_map) {
+ function->constants.write[K.value] = K.key;
}
} else {
function->_constants_ptr = nullptr;
@@ -455,6 +456,12 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
case Variant::TRANSFORM2D:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM2D, 1);
break;
+ case Variant::VECTOR4:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3, 1);
+ break;
+ case Variant::VECTOR4I:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I, 1);
+ break;
case Variant::PLANE:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE, 1);
break;
@@ -470,6 +477,9 @@ void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Varia
case Variant::TRANSFORM3D:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D, 1);
break;
+ case Variant::PROJECTION:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PROJECTION, 1);
+ break;
case Variant::COLOR:
append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1);
break;
@@ -1338,6 +1348,18 @@ void GDScriptByteCodeGenerator::write_endif() {
if_jmp_addrs.pop_back();
}
+void GDScriptByteCodeGenerator::write_jump_if_shared(const Address &p_value) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_SHARED, 1);
+ append(p_value);
+ if_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+}
+
void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
@@ -1586,7 +1608,7 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
// Typed array.
const GDScriptDataType &element_type = function->return_type.get_container_element_type();
- Variant script = function->return_type.script_type;
+ Variant script = element_type.script_type;
int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY, 2);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 0503f66161..7dd51845df 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_BYTE_CODEGEN
-#define GDSCRIPT_BYTE_CODEGEN
+#ifndef GDSCRIPT_BYTE_CODEGEN_H
+#define GDSCRIPT_BYTE_CODEGEN_H
#include "gdscript_codegen.h"
@@ -53,19 +53,19 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
bool debug_stack = false;
Vector<int> opcodes;
- List<Map<StringName, int>> stack_id_stack;
- Map<StringName, int> stack_identifiers;
+ List<RBMap<StringName, int>> stack_id_stack;
+ RBMap<StringName, int> stack_identifiers;
List<int> stack_identifiers_counts;
- Map<StringName, int> local_constants;
+ RBMap<StringName, int> local_constants;
Vector<StackSlot> locals;
Vector<StackSlot> temporaries;
List<int> used_temporaries;
- Map<Variant::Type, List<int>> temporaries_pool;
+ RBMap<Variant::Type, List<int>> temporaries_pool;
List<GDScriptFunction::StackDebug> stack_debug;
- List<Map<StringName, int>> block_identifier_stack;
- Map<StringName, int> block_identifiers;
+ List<RBMap<StringName, int>> block_identifier_stack;
+ RBMap<StringName, int> block_identifiers;
int max_locals = 0;
int current_line = 0;
@@ -77,23 +77,23 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
#endif
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
- Map<StringName, int> name_map;
+ RBMap<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
#endif
- Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
- Map<Variant::ValidatedSetter, int> setters_map;
- Map<Variant::ValidatedGetter, int> getters_map;
- Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
- Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
- Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
- Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
- Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
- Map<Variant::ValidatedConstructor, int> constructors_map;
- Map<Variant::ValidatedUtilityFunction, int> utilities_map;
- Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
- Map<MethodBind *, int> method_bind_map;
- Map<GDScriptFunction *, int> lambdas_map;
+ RBMap<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+ RBMap<Variant::ValidatedSetter, int> setters_map;
+ RBMap<Variant::ValidatedGetter, int> getters_map;
+ RBMap<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+ RBMap<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+ RBMap<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+ RBMap<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+ RBMap<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+ RBMap<Variant::ValidatedConstructor, int> constructors_map;
+ RBMap<Variant::ValidatedUtilityFunction, int> utilities_map;
+ RBMap<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
+ RBMap<MethodBind *, int> method_bind_map;
+ RBMap<GDScriptFunction *, int> lambdas_map;
// Lists since these can be nested.
List<int> if_jmp_addrs;
@@ -135,7 +135,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
stack_identifiers_counts.push_back(locals.size());
stack_id_stack.push_back(stack_identifiers);
if (debug_stack) {
- Map<StringName, int> block_ids(block_identifiers);
+ RBMap<StringName, int> block_ids(block_identifiers);
block_identifier_stack.push_back(block_ids);
block_identifiers.clear();
}
@@ -419,7 +419,7 @@ public:
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, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) override;
virtual GDScriptFunction *write_end() override;
#ifdef DEBUG_ENABLED
@@ -479,6 +479,8 @@ public:
virtual void write_if(const Address &p_condition) override;
virtual void write_else() override;
virtual void write_endif() override;
+ virtual void write_jump_if_shared(const Address &p_value) override;
+ virtual void write_end_jump_if_shared() override;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override;
virtual void write_for() override;
@@ -500,4 +502,4 @@ public:
virtual ~GDScriptByteCodeGenerator();
};
-#endif // GDSCRIPT_BYTE_CODEGEN
+#endif // GDSCRIPT_BYTE_CODEGEN_H
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 8c198345c2..48d5fbc569 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -157,7 +157,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
source_file.write[len] = 0;
String source;
- if (source.parse_utf8((const char *)source_file.ptr())) {
+ if (source.parse_utf8((const char *)source_file.ptr()) != OK) {
ERR_FAIL_V_MSG("", "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
return source;
@@ -223,13 +223,13 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
singleton->full_gdscript_cache[p_owner] = script.ptr();
singleton->shallow_gdscript_cache.erase(p_owner);
- Set<String> depends = singleton->dependencies[p_owner];
+ HashSet<String> depends = singleton->dependencies[p_owner];
Error err = OK;
- for (const Set<String>::Element *E = depends.front(); E != nullptr; E = E->next()) {
+ for (const String &E : depends) {
Error this_err = OK;
// No need to save the script. We assume it's already referenced in the owner.
- get_full_script(E->get(), this_err);
+ get_full_script(E, this_err);
if (this_err != OK) {
err = this_err;
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 3ce976ee14..b971bdd984 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -34,7 +34,7 @@
#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/templates/hash_map.h"
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript.h"
class GDScriptAnalyzer;
@@ -74,7 +74,7 @@ class GDScriptCache {
HashMap<String, GDScriptParserRef *> parser_map;
HashMap<String, GDScript *> shallow_gdscript_cache;
HashMap<String, GDScript *> full_gdscript_cache;
- HashMap<String, Set<String>> dependencies;
+ HashMap<String, HashSet<String>> dependencies;
friend class GDScript;
friend class GDScriptParserRef;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 326b66a295..5972481c3a 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -28,10 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_CODEGEN
-#define GDSCRIPT_CODEGEN
+#ifndef GDSCRIPT_CODEGEN_H
+#define GDSCRIPT_CODEGEN_H
-#include "core/multiplayer/multiplayer.h"
#include "core/string/string_name.h"
#include "core/variant/variant.h"
#include "gdscript_function.h"
@@ -80,7 +79,7 @@ public:
virtual void start_block() = 0;
virtual void end_block() = 0;
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) = 0;
virtual GDScriptFunction *write_end() = 0;
#ifdef DEBUG_ENABLED
@@ -140,6 +139,8 @@ public:
virtual void write_if(const Address &p_condition) = 0;
virtual void write_else() = 0;
virtual void write_endif() = 0;
+ virtual void write_jump_if_shared(const Address &p_value) = 0;
+ virtual void write_end_jump_if_shared() = 0;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0;
virtual void write_for() = 0;
@@ -161,4 +162,4 @@ public:
virtual ~GDScriptCodeGenerator() {}
};
-#endif // GDSCRIPT_CODEGEN
+#endif // GDSCRIPT_CODEGEN_H
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 225c2d0d45..00e8223b9a 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.locals.has(p_name)) {
+ if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) {
return false; //shadowed
}
@@ -108,11 +108,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
- // Locate class by constructing the path to it and following that path
+ // Locate class by constructing the path to it and following that path.
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path);
- const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name);
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.builtin_type = p_datatype.builtin_type;
+
+ String class_name = class_type->fqcn.split("::")[0];
+ const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_name == main_script->path);
+ const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_name == main_script->name);
if (is_inner_by_path || is_inner_by_name) {
// Local class.
List<StringName> names;
@@ -131,16 +135,41 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
script = script->subclasses[names.back()->get()];
names.pop_back();
}
- result.kind = GDScriptDataType::GDSCRIPT;
result.script_type = script.ptr();
result.native_type = script->get_instance_base_type();
- result.builtin_type = p_datatype.builtin_type;
} else {
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ // Inner class.
+ PackedStringArray classes = class_type->fqcn.split("::");
+ if (!classes.is_empty()) {
+ for (GDScript *script : parsed_classes) {
+ // Checking of inheritance structure of inner class to find a correct script link.
+ if (script->name == classes[classes.size() - 1]) {
+ PackedStringArray classes2 = script->fully_qualified_name.split("::");
+ bool valid = true;
+ if (classes.size() != classes2.size()) {
+ valid = false;
+ } else {
+ for (int i = 0; i < classes.size(); i++) {
+ if (classes[i] != classes2[i]) {
+ valid = false;
+ break;
+ }
+ }
+ }
+ if (!valid) {
+ continue;
+ }
+ result.script_type_ref = Ref<GDScript>(script);
+ break;
+ }
+ }
+ }
+ if (result.script_type_ref.is_null()) {
+ result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ }
+
result.script_type = result.script_type_ref.ptr();
result.native_type = p_datatype.native_type;
- result.builtin_type = p_datatype.builtin_type;
}
}
} break;
@@ -283,7 +312,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Class C++ integer constant.
if (nc) {
bool success = false;
- int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
return codegen.add_constant(constant);
}
@@ -336,7 +365,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
// If it's an autoload singleton, we postpone to load it at runtime.
// This is so one autoload doesn't try to load another before it's compiled.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype()));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
@@ -356,7 +385,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
class_node = class_node->outer;
}
- RES res;
+ Ref<Resource> res;
if (class_node->identifier && class_node->identifier->name == identifier) {
res = Ref<GDScript>(main_script);
@@ -638,20 +667,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::GET_NODE: {
const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);
- String node_name;
- if (get_node->string != nullptr) {
- node_name += String(get_node->string->value);
- } else {
- for (int i = 0; i < get_node->chain.size(); i++) {
- if (i > 0) {
- node_name += "/";
- }
- node_name += get_node->chain[i]->name;
- }
- }
-
Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(codegen.add_constant(NodePath(node_name)));
+ args.push_back(codegen.add_constant(NodePath(get_node->full_path)));
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
@@ -703,10 +720,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else if (subscript->is_attribute) {
if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
GDScriptParser::IdentifierNode *identifier = subscript->attribute;
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+ HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name);
#ifdef DEBUG_ENABLED
- if (MI && MI->get().getter == codegen.function_name) {
+ if (MI && MI->value.getter == codegen.function_name) {
String n = identifier->name;
_set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);
r_error = ERR_COMPILATION_FAILED;
@@ -714,11 +731,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
#endif
- if (MI && MI->get().getter == "") {
+ if (MI && MI->value.getter == "") {
// 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()));
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype()));
}
}
@@ -894,8 +911,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);
#ifdef DEBUG_ENABLED
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) {
+ HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(subscript->attribute->name);
+ if (MI && MI->value.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;
@@ -1039,13 +1056,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Set back the values into their bases.
for (const ChainInfo &info : set_chain) {
- if (!info.is_named) {
- gen->write_set(info.base, info.key, assigned);
- if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type || !is_shared) {
+ if (!known_type) {
+ // Jump shared values since they are already updated in-place.
+ gen->write_jump_if_shared(assigned);
+ }
+ if (!info.is_named) {
+ gen->write_set(info.base, info.key, assigned);
+ } else {
+ gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
}
- } else {
- gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (!info.is_named && info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -1053,19 +1082,35 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
assigned = info.base;
}
- // If this is a class member property, also assign to it.
- // This allow things like: position.x += 2.0
- if (assign_class_member_property != StringName()) {
- gen->write_set_member(assigned, assign_class_member_property);
- }
- // Same as above but for members
- if (is_member_property) {
- if (member_property_has_setter && !member_property_is_in_setter) {
- Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(assigned);
- gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
- } else {
- gen->write_assign(target_member_property, assigned);
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type || !is_shared) {
+ // If this is a class member property, also assign to it.
+ // This allow things like: position.x += 2.0
+ if (assign_class_member_property != StringName()) {
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ gen->write_set_member(assigned, assign_class_member_property);
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
+ } else if (is_member_property) {
+ // Same as above but for script members.
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ if (member_property_has_setter && !member_property_is_in_setter) {
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(assigned);
+ gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
+ } else {
+ gen->write_assign(target_member_property, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
}
}
@@ -1372,25 +1417,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
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++) {
@@ -1400,7 +1429,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// 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);
+ codegen.generator->write_and_left_operand(result_addr);
// Add index to constant map.
GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);
@@ -1414,19 +1443,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
- test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, result_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);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- return test_addr;
+ // 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_DICTIONARY: {
if (p_is_nested) {
@@ -1471,27 +1515,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
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++) {
@@ -1502,7 +1528,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// 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);
+ codegen.generator->write_and_left_operand(result_addr);
// Get the pattern key.
GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);
@@ -1513,11 +1539,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// 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);
+ codegen.generator->write_call(result_addr, 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);
+ codegen.generator->write_and_left_operand(result_addr);
// Get actual value from user dictionary.
codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);
@@ -1528,16 +1554,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
- test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, result_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);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
// Remove pattern key temporary.
if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1548,9 +1574,23 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- codegen.generator->pop_temporary();
- return test_addr;
+ // 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_REST:
// Do nothing.
@@ -1935,7 +1975,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
StringName func_name;
bool is_static = false;
- Multiplayer::RPCConfig rpc_config;
+ Variant rpc_config;
GDScriptDataType return_type;
return_type.has_type = true;
return_type.kind = GDScriptDataType::BUILTIN;
@@ -1978,18 +2018,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
// Parse initializer if applies.
bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;
- bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
- bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready");
+ bool is_initializer = p_func && !p_for_lambda && p_func->identifier->name == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_implicit_ready = !p_func && p_for_ready;
- if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) {
+ if (!p_for_lambda && (is_implicit_initializer || is_implicit_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 != is_for_ready) {
- // Only initialize in _ready.
+ if (field->onready != is_implicit_ready) {
+ // Only initialize in @implicit_ready.
continue;
}
@@ -2111,6 +2151,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
p_script->initializer = gd_function;
} else if (is_implicit_initializer) {
p_script->implicit_initializer = gd_function;
+ } else if (is_implicit_ready) {
+ p_script->implicit_ready = gd_function;
}
if (p_func) {
@@ -2128,7 +2170,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
#endif
}
- if (!p_for_lambda) {
+ if (!is_implicit_initializer && !is_implicit_ready && !p_for_lambda) {
p_script->member_functions[func_name] = gd_function;
}
@@ -2196,11 +2238,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
memdelete(E.value);
}
+ if (p_script->implicit_initializer) {
+ memdelete(p_script->implicit_initializer);
+ }
+ if (p_script->implicit_ready) {
+ memdelete(p_script->implicit_ready);
+ }
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
p_script->_signals.clear();
p_script->initializer = nullptr;
+ p_script->implicit_initializer = nullptr;
+ p_script->implicit_ready = nullptr;
p_script->tool = parser->is_tool();
p_script->name = p_class->identifier ? p_class->identifier->name : "";
@@ -2402,6 +2452,25 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
#endif
} break;
+
+ case GDScriptParser::ClassNode::Member::GROUP: {
+ const GDScriptParser::AnnotationNode *annotation = member.annotation;
+ StringName name = annotation->export_info.name;
+
+ // This is not a normal member, but we need this to keep indices in order.
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+
+ PropertyInfo prop_info;
+ prop_info.name = name;
+ prop_info.usage = annotation->export_info.usage;
+ prop_info.hint_string = annotation->export_info.hint_string;
+
+ p_script->member_info[name] = prop_info;
+ p_script->member_indices[name] = minfo;
+ p_script->members.insert(name);
+ } break;
+
default:
break; // Nothing to do here.
}
@@ -2444,15 +2513,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
- bool has_ready = false;
-
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type == member.FUNCTION) {
const GDScriptParser::FunctionNode *function = member.function;
- if (!has_ready && function->identifier->name == "_ready") {
- has_ready = true;
- }
Error err = OK;
_parse_function(err, p_script, p_class, function);
if (err) {
@@ -2486,8 +2550,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
}
- if (!has_ready && p_class->onready_used) {
- //create a _ready constructor
+ if (p_class->onready_used) {
+ // Create an implicit_ready constructor.
Error err = OK;
_parse_function(err, p_script, p_class, nullptr, true);
if (err) {
@@ -2500,8 +2564,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
//validate instances if keeping state
if (p_keep_state) {
- for (Set<Object *>::Element *E = p_script->instances.front(); E;) {
- Set<Object *>::Element *N = E->next();
+ for (RBSet<Object *>::Element *E = p_script->instances.front(); E;) {
+ RBSet<Object *>::Element *N = E->next();
ScriptInstance *si = E->get()->get_script_instance();
if (si->is_placeholder()) {
@@ -2563,7 +2627,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- Map<StringName, Ref<GDScript>> old_subclasses;
+ HashMap<StringName, Ref<GDScript>> old_subclasses;
if (p_keep_state) {
old_subclasses = p_script->subclasses;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 8d71437344..4841884e2d 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript.h"
#include "gdscript_codegen.h"
#include "gdscript_function.h"
@@ -39,8 +39,8 @@
class GDScriptCompiler {
const GDScriptParser *parser = nullptr;
- Set<GDScript *> parsed_classes;
- Set<GDScript *> parsing_classes;
+ HashSet<GDScript *> parsed_classes;
+ HashSet<GDScript *> parsing_classes;
GDScript *main_script = nullptr;
struct CodeGen {
@@ -49,9 +49,9 @@ class GDScriptCompiler {
const GDScriptParser::FunctionNode *function_node = nullptr;
StringName function_name;
GDScriptCodeGenerator *generator = nullptr;
- Map<StringName, GDScriptCodeGenerator::Address> parameters;
- Map<StringName, GDScriptCodeGenerator::Address> locals;
- List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack;
+ HashMap<StringName, GDScriptCodeGenerator::Address> parameters;
+ HashMap<StringName, GDScriptCodeGenerator::Address> locals;
+ List<HashMap<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);
@@ -101,7 +101,7 @@ class GDScriptCompiler {
}
void start_block() {
- Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
+ HashMap<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
locals_stack.push_back(old_locals);
generator->start_block();
}
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index dc114f2eff..b38c7c6699 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -639,10 +639,13 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_PTRCALL(VECTOR3);
DISASSEMBLE_PTRCALL(VECTOR3I);
DISASSEMBLE_PTRCALL(TRANSFORM2D);
+ DISASSEMBLE_PTRCALL(VECTOR4);
+ DISASSEMBLE_PTRCALL(VECTOR4I);
DISASSEMBLE_PTRCALL(PLANE);
DISASSEMBLE_PTRCALL(AABB);
DISASSEMBLE_PTRCALL(BASIS);
DISASSEMBLE_PTRCALL(TRANSFORM3D);
+ DISASSEMBLE_PTRCALL(PROJECTION);
DISASSEMBLE_PTRCALL(COLOR);
DISASSEMBLE_PTRCALL(STRING_NAME);
DISASSEMBLE_PTRCALL(NODE_PATH);
@@ -838,6 +841,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 1;
} break;
+ case OPCODE_JUMP_IF_SHARED: {
+ text += "jump-if-shared ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
case OPCODE_RETURN: {
text += "return ";
text += DADDR(1);
@@ -1005,11 +1016,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_TYPE_ADJUST(VECTOR3);
DISASSEMBLE_TYPE_ADJUST(VECTOR3I);
DISASSEMBLE_TYPE_ADJUST(TRANSFORM2D);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR4);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR4I);
DISASSEMBLE_TYPE_ADJUST(PLANE);
DISASSEMBLE_TYPE_ADJUST(QUATERNION);
DISASSEMBLE_TYPE_ADJUST(AABB);
DISASSEMBLE_TYPE_ADJUST(BASIS);
DISASSEMBLE_TYPE_ADJUST(TRANSFORM3D);
+ DISASSEMBLE_TYPE_ADJUST(PROJECTION);
DISASSEMBLE_TYPE_ADJUST(COLOR);
DISASSEMBLE_TYPE_ADJUST(STRING_NAME);
DISASSEMBLE_TYPE_ADJUST(NODE_PATH);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index b3f9914b7d..d943974ce4 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -98,7 +98,7 @@ Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(
return templates;
}
-static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) {
+static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, HashMap<int, String> &r_funcs) {
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {
const GDScriptParser::FunctionNode *function = p_class->members[i].function;
@@ -110,7 +110,7 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl
}
}
-bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const {
GDScriptParser parser;
GDScriptAnalyzer analyzer(&parser);
@@ -148,7 +148,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
return false;
} else {
const GDScriptParser::ClassNode *cl = parser.get_tree();
- Map<int, String> funcs;
+ HashMap<int, String> funcs;
get_function_names_recursively(cl, "", funcs);
@@ -159,7 +159,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
#ifdef DEBUG_ENABLED
if (r_safe_lines) {
- const Set<int> &unsafe_lines = parser.get_unsafe_lines();
+ const HashSet<int> &unsafe_lines = parser.get_unsafe_lines();
for (int i = 1; i <= parser.get_last_line_number(); i++) {
if (!unsafe_lines.has(i)) {
r_safe_lines->insert(i);
@@ -321,7 +321,7 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
Ref<GDScript> script = instance->get_script();
ERR_FAIL_COND(script.is_null());
- const Map<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
+ const HashMap<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {
p_members->push_back(E.key);
@@ -343,7 +343,7 @@ ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {
}
void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- const Map<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();
+ const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();
const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array();
List<Pair<String, Variant>> cinfo;
@@ -445,6 +445,16 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const
p_constants->push_back(nan);
}
+void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
+ GDScriptParser parser;
+ List<MethodInfo> annotations;
+ parser.get_annotation_list(&annotations);
+
+ for (const MethodInfo &E : annotations) {
+ p_annotations->push_back(E);
+ }
+}
+
String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {
#ifdef TOOLS_ENABLED
bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");
@@ -569,7 +579,7 @@ static int _get_enum_constant_location(StringName p_class, StringName p_enum_con
// END LOCATION METHODS
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
- if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
String enum_name = p_info.class_name;
if (!enum_name.contains(".")) {
return enum_name;
@@ -722,7 +732,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
return arghint;
}
-static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptLanguage::CodeCompletionOption> &r_list) {
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
for (int i = 0; i < p_dir->get_file_count(); i++) {
@@ -736,9 +746,9 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String
}
}
-static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (p_annotation->name == SNAME("@export_range")) {
- if (p_argument == 3 || p_argument == 4) {
+ if (p_argument == 3 || p_argument == 4 || p_argument == 5) {
// Slider hint.
ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider1.insert_text = slider1.display.quote(p_quote_style);
@@ -746,6 +756,9 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption slider2("or_lesser", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider2.insert_text = slider2.display.quote(p_quote_style);
r_result.insert(slider2.display, slider2);
+ ScriptLanguage::CodeCompletionOption slider3("no_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ slider3.insert_text = slider3.display.quote(p_quote_style);
+ r_result.insert(slider3.display, slider3);
}
} else if (p_annotation->name == SNAME("@export_exp_easing")) {
if (p_argument == 0 || p_argument == 1) {
@@ -777,7 +790,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
}
}
-static void _find_built_in_variants(Map<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {
+static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) {
ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
@@ -789,7 +802,7 @@ static void _find_built_in_variants(Map<String, ScriptLanguage::CodeCompletionOp
}
}
-static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
// Built-in Variant Types
_find_built_in_variants(r_result, true);
@@ -851,9 +864,10 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
}
// Autoload singletons
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {
continue;
}
@@ -862,7 +876,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
}
}
-static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
for (int i = 0; i < p_suite->locals.size(); i++) {
ScriptLanguage::CodeCompletionOption option;
if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {
@@ -878,9 +892,9 @@ 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, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &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, ScriptLanguage::CodeCompletionOption> &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, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
if (!p_parent_only) {
@@ -946,6 +960,8 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
}
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
break;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case GDScriptParser::ClassNode::Member::UNDEFINED:
break;
}
@@ -965,7 +981,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
_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, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
GDScriptParser::DataType base_type = p_base.type;
@@ -997,7 +1013,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
}
- Map<StringName, Variant> constants;
+ HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
for (const KeyValue<StringName, Variant> &E : constants) {
int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key);
@@ -1056,6 +1072,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
+ List<MethodInfo> signals;
+ ClassDB::get_signal_list(type, &signals);
+ for (const MethodInfo &E : signals) {
+ int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));
+ ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
+ r_result.insert(option.display, option);
+ }
+
if (!_static || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
@@ -1140,7 +1164,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
-static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
+static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &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);
@@ -1219,12 +1243,11 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
r_result.insert(option.display, option);
}
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- if (!E.value().is_singleton) {
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ if (!E.value.is_singleton) {
continue;
}
- ScriptLanguage::CodeCompletionOption option(E.key(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
+ ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
r_result.insert(option.display, option);
}
@@ -1281,7 +1304,7 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
return ci;
}
- if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
ci.enumeration = p_property.class_name;
}
@@ -1450,11 +1473,16 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) {
// Simple call, so base is 'self'.
if (p_context.current_class) {
- base.type.kind = GDScriptParser::DataType::CLASS;
- base.type.type_source = GDScriptParser::DataType::INFERRED;
- base.type.is_constant = true;
- base.type.class_type = p_context.current_class;
- base.value = p_context.base;
+ if (call->is_super) {
+ base.type = p_context.current_class->base_type;
+ base.value = p_context.base;
+ } else {
+ base.type.kind = GDScriptParser::DataType::CLASS;
+ base.type.type_source = GDScriptParser::DataType::INFERRED;
+ base.type.is_constant = true;
+ base.type.class_type = p_context.current_class;
+ base.value = p_context.base;
+ }
} else {
break;
}
@@ -1517,12 +1545,10 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
found = true;
} else {
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- String name = E.key();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ String name = E.key;
if (name == which) {
- String script = E.value().path;
+ String script = E.value.path;
if (!script.begins_with("res://")) {
script = "res://" + script;
@@ -1834,7 +1860,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
while (suite) {
for (int i = 0; i < suite->statements.size(); i++) {
- if (suite->statements[i]->start_line > p_context.current_line) {
+ if (suite->statements[i]->end_line >= p_context.current_line) {
break;
}
@@ -1882,7 +1908,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
suite = suite->parent_block;
}
- if (last_assigned_expression && last_assign_line != p_context.current_line) {
+ if (last_assigned_expression && last_assign_line < p_context.current_line) {
GDScriptParser::CompletionContext c = p_context;
c.current_line = last_assign_line;
r_type.assigned_expression = last_assigned_expression;
@@ -1983,8 +2009,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return false;
}
- // Check autoloads.
- if (ProjectSettings::get_singleton()->has_autoload(p_identifier)) {
+ // Check global variables (including autoloads).
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
return true;
}
@@ -2019,7 +2045,10 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
return true;
case GDScriptParser::ClassNode::Member::VARIABLE:
if (!is_static) {
- if (member.variable->initializer) {
+ if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
+ r_type.type = member.variable->get_datatype();
+ return true;
+ } else if (member.variable->initializer) {
const GDScriptParser::ExpressionNode *init = member.variable->initializer;
if (init->is_constant) {
r_type.value = init->reduced_value;
@@ -2041,9 +2070,6 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type = init->get_datatype();
return true;
}
- } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
- r_type.type = member.variable->get_datatype();
- return true;
}
}
// TODO: Check assignments in constructor.
@@ -2073,6 +2099,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.type.class_type = member.m_class;
return true;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ return false; // No-op, but silences warnings.
case GDScriptParser::ClassNode::Member::UNDEFINED:
return false; // Unreachable.
}
@@ -2083,7 +2111,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
case GDScriptParser::DataType::SCRIPT: {
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
- Map<StringName, Variant> constants;
+ HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
if (constants.has(p_identifier)) {
r_type = _type_from_variant(constants[p_identifier]);
@@ -2312,7 +2340,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
return false;
}
-static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (!p_enum_hint.contains(".")) {
// Global constant or in the current class.
StringName current_enum = p_enum_hint;
@@ -2349,7 +2377,7 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
}
}
-static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
Variant base = p_base.value;
GDScriptParser::DataType base_type = p_base.type;
@@ -2398,7 +2426,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (p_argidx < method_args) {
PropertyInfo arg_info = info.arguments[p_argidx];
- if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
_find_enumeration_candidates(p_context, arg_info.class_name, r_result);
}
}
@@ -2468,7 +2496,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
-static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
if (p_call->type == GDScriptParser::Node::PRELOAD) {
if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
@@ -2583,7 +2611,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
analyzer.analyze();
r_forced = false;
- Map<String, ScriptLanguage::CodeCompletionOption> options;
+ HashMap<String, ScriptLanguage::CodeCompletionOption> options;
GDScriptParser::CompletionContext completion_context = parser.get_completion_context();
completion_context.base = p_owner;
@@ -2882,10 +2910,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
// Get autoloads.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- String path = "/root/" + E.key();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ String path = "/root/" + E.key;
ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
options.insert(option.display, option);
}
@@ -3062,6 +3088,13 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
+ if (ClassDB::has_signal(class_name, p_symbol, true)) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;
+ r_result.class_name = base_type.native_type;
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+
StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true);
if (enum_name != StringName()) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;
@@ -3106,7 +3139,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
Variant v;
- REF v_ref;
+ Ref<RefCounted> v_ref;
if (base_type.builtin_type == Variant::OBJECT) {
v_ref.instantiate();
v = v_ref;
@@ -3144,7 +3177,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
- // Before parsing, try the usual stuff
+ // Before parsing, try the usual stuff.
if (ClassDB::class_exists(p_symbol)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;
r_result.class_name = p_symbol;
@@ -3160,7 +3193,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
- if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+ // Need special checks for assert and preload as they are technically
+ // keywords, so are not registered in GDScriptUtilityFunctions.
+ if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;
r_result.class_name = "@GDScript";
r_result.class_member = p_symbol;
@@ -3216,6 +3251,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
is_function = true;
[[fallthrough]];
}
+ case GDScriptParser::COMPLETION_ASSIGN:
case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER: {
GDScriptParser::DataType base_type;
@@ -3268,7 +3304,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
// Global.
- Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
+ HashMap<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
if (classes.has(p_symbol)) {
Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]];
if (value.get_type() == Variant::OBJECT) {
@@ -3359,6 +3395,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
} break;
+ case GDScriptParser::COMPLETION_ANNOTATION: {
+ const String annotation_symbol = "@" + p_symbol;
+ if (parser.annotation_exists(annotation_symbol)) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = annotation_symbol;
+ return OK;
+ }
+ } break;
default: {
}
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 3d708955ed..cd3b7d69c5 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -93,7 +93,7 @@ struct _GDFKCS {
void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {
int oc = 0;
- Map<StringName, _GDFKC> sdmap;
+ HashMap<StringName, _GDFKC> sdmap;
for (const StackDebug &sd : stack_debug) {
if (sd.line >= p_line) {
break;
@@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->exit_function();
}
+
+ _clear_stack();
#endif
}
@@ -279,7 +281,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
void GDScriptFunctionState::_clear_stack() {
if (state.stack_size) {
Variant *stack = (Variant *)state.stack.ptr();
- for (int i = 0; i < state.stack_size; i++) {
+ // The first 3 are special addresses and not copied to the state, so we skip them here.
+ for (int i = 3; i < state.stack_size; i++) {
stack[i].~Variant();
}
state.stack_size = 0;
@@ -300,8 +303,6 @@ GDScriptFunctionState::GDScriptFunctionState() :
}
GDScriptFunctionState::~GDScriptFunctionState() {
- _clear_stack();
-
{
MutexLock lock(GDScriptLanguage::singleton->lock);
scripts_list.remove_from_list();
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index ba0d51c5cc..e44038d6da 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -273,11 +273,14 @@ public:
OPCODE_CALL_PTRCALL_VECTOR3,
OPCODE_CALL_PTRCALL_VECTOR3I,
OPCODE_CALL_PTRCALL_TRANSFORM2D,
+ OPCODE_CALL_PTRCALL_VECTOR4,
+ OPCODE_CALL_PTRCALL_VECTOR4I,
OPCODE_CALL_PTRCALL_PLANE,
OPCODE_CALL_PTRCALL_QUATERNION,
OPCODE_CALL_PTRCALL_AABB,
OPCODE_CALL_PTRCALL_BASIS,
OPCODE_CALL_PTRCALL_TRANSFORM3D,
+ OPCODE_CALL_PTRCALL_PROJECTION,
OPCODE_CALL_PTRCALL_COLOR,
OPCODE_CALL_PTRCALL_STRING_NAME,
OPCODE_CALL_PTRCALL_NODE_PATH,
@@ -304,6 +307,7 @@ public:
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
OPCODE_JUMP_TO_DEF_ARGUMENT,
+ OPCODE_JUMP_IF_SHARED,
OPCODE_RETURN,
OPCODE_RETURN_TYPED_BUILTIN,
OPCODE_RETURN_TYPED_ARRAY,
@@ -362,11 +366,14 @@ public:
OPCODE_TYPE_ADJUST_VECTOR3,
OPCODE_TYPE_ADJUST_VECTOR3I,
OPCODE_TYPE_ADJUST_TRANSFORM2D,
+ OPCODE_TYPE_ADJUST_VECTOR4,
+ OPCODE_TYPE_ADJUST_VECTOR4I,
OPCODE_TYPE_ADJUST_PLANE,
OPCODE_TYPE_ADJUST_QUATERNION,
OPCODE_TYPE_ADJUST_AABB,
OPCODE_TYPE_ADJUST_BASIS,
OPCODE_TYPE_ADJUST_TRANSFORM3D,
+ OPCODE_TYPE_ADJUST_PROJECTION,
OPCODE_TYPE_ADJUST_COLOR,
OPCODE_TYPE_ADJUST_STRING_NAME,
OPCODE_TYPE_ADJUST_NODE_PATH,
@@ -470,7 +477,7 @@ private:
int _initial_line = 0;
bool _static = false;
- Multiplayer::RPCConfig rpc_config;
+ Variant rpc_config;
GDScript *_script = nullptr;
@@ -495,7 +502,7 @@ private:
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
- Map<int, Variant::Type> temporary_slots;
+ HashMap<int, Variant::Type> temporary_slots;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
@@ -592,7 +599,7 @@ public:
void disassemble(const Vector<String> &p_code_lines) const;
#endif
- _FORCE_INLINE_ Multiplayer::RPCConfig get_rpc_config() const { return rpc_config; }
+ _FORCE_INLINE_ const Variant get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index c43fa12c8c..a25bf9a306 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -91,7 +91,7 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -161,7 +161,7 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, G
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
@@ -169,5 +169,5 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 248176e32c..1954089983 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_LAMBDA_CALLABLE
-#define GDSCRIPT_LAMBDA_CALLABLE
+#ifndef GDSCRIPT_LAMBDA_CALLABLE_H
+#define GDSCRIPT_LAMBDA_CALLABLE_H
#include "core/object/ref_counted.h"
#include "core/templates/vector.h"
@@ -87,4 +87,4 @@ public:
virtual ~GDScriptLambdaSelfCallable() = default;
};
-#endif // GDSCRIPT_LAMBDA_CALLABLE
+#endif // GDSCRIPT_LAMBDA_CALLABLE_H
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 67f6b61f14..6f5397e1a3 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -35,6 +35,7 @@
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "gdscript.h"
+#include "scene/main/multiplayer_api.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
@@ -60,11 +61,14 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
builtin_types["Transform2D"] = Variant::TRANSFORM2D;
builtin_types["Vector3"] = Variant::VECTOR3;
builtin_types["Vector3i"] = Variant::VECTOR3I;
+ builtin_types["Vector4"] = Variant::VECTOR4;
+ builtin_types["Vector4i"] = Variant::VECTOR4I;
builtin_types["AABB"] = Variant::AABB;
builtin_types["Plane"] = Variant::PLANE;
builtin_types["Quaternion"] = Variant::QUATERNION;
builtin_types["Basis"] = Variant::BASIS;
builtin_types["Transform3D"] = Variant::TRANSFORM3D;
+ builtin_types["Projection"] = Variant::PROJECTION;
builtin_types["Color"] = Variant::COLOR;
builtin_types["RID"] = Variant::RID;
builtin_types["Object"] = Variant::OBJECT;
@@ -100,42 +104,49 @@ void GDScriptParser::cleanup() {
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
- List<StringName> keys;
- valid_annotations.get_key_list(&keys);
- for (const StringName &E : keys) {
- r_annotations->push_back(valid_annotations[E].info);
+ for (const KeyValue<StringName, AnnotationInfo> &E : valid_annotations) {
+ r_annotations->push_back(E.value.info);
}
}
+bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
+ return valid_annotations.has(p_annotation_name);
+}
+
GDScriptParser::GDScriptParser() {
// Register valid annotations.
// TODO: Should this be static?
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
- register_annotation(MethodInfo("@icon", { Variant::STRING, "icon_path" }), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_enum", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true);
- register_annotation(MethodInfo("@export_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, varray(), true);
+ register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_global_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 3);
- register_annotation(MethodInfo("@export_exp_easing", { Variant::STRING, "hint1" }, { Variant::STRING, "hint2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2);
+ register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
+ register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
- register_annotation(MethodInfo("@export_node_path", { Variant::STRING, "type" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true);
- register_annotation(MethodInfo("@export_flags", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true);
+ register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
+ register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@warning_ignore", { Variant::STRING, "warning" }), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
+ // Export grouping annotations.
+ register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
+ register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
+ register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
+ // Warning annotations.
+ register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
// Networking.
- register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
+ register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("", "", "", 0), true);
}
GDScriptParser::~GDScriptParser() {
@@ -155,6 +166,7 @@ void GDScriptParser::clear() {
for_completion = false;
errors.clear();
multiline_stack.clear();
+ nodes_in_progress.clear();
}
void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
@@ -205,7 +217,8 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
if (ignored_warnings.has(warn_name)) {
return;
}
- if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
+ int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
+ if (!warn_level) {
return;
}
@@ -217,6 +230,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.leftmost_column = p_source->leftmost_column;
warning.rightmost_column = p_source->rightmost_column;
+ if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
+ push_error(warning.get_message(), p_source);
+ return;
+ }
+
List<GDScriptWarning>::Element *before = nullptr;
for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
if (E->get().start_line > warning.start_line) {
@@ -400,6 +418,9 @@ GDScriptTokenizer::Token GDScriptParser::advance() {
push_error(current.literal);
current = tokenizer.scan();
}
+ for (Node *n : nodes_in_progress) {
+ update_extents(n);
+ }
return previous;
}
@@ -515,9 +536,13 @@ void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
current_class = head;
+ // If we happen to parse an annotation before extends or class_name keywords, track it.
+ // @tool is allowed, but others should fail.
+ AnnotationNode *premature_annotation = nullptr;
+
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @tool annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ // Check for @tool, script-level, or standalone annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
if (annotation->name == SNAME("@tool")) {
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
@@ -527,7 +552,14 @@ void GDScriptParser::parse_program() {
}
// @tool annotation has no specific target.
annotation->apply(this, nullptr);
+ } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
+ premature_annotation = annotation;
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after a standalone annotation.)");
+ }
+ annotation->apply(this, head);
} else {
+ premature_annotation = annotation;
annotation_stack.push_back(annotation);
}
}
@@ -537,8 +569,8 @@ void GDScriptParser::parse_program() {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (!annotation_stack.is_empty()) {
- push_error(R"("class_name" should be used before annotations.)");
+ if (premature_annotation != nullptr) {
+ push_error(R"("class_name" should be used before annotations (except @tool).)");
}
advance();
if (head->identifier != nullptr) {
@@ -548,8 +580,8 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (!annotation_stack.is_empty()) {
- push_error(R"("extends" should be used before annotations.)");
+ if (premature_annotation != nullptr) {
+ push_error(R"("extends" should be used before annotations (except @tool).)");
}
advance();
if (head->extends_used) {
@@ -570,12 +602,12 @@ void GDScriptParser::parse_program() {
}
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @icon annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ // Check for a script-level, or standalone annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == SNAME("@icon")) {
+ if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after "@icon" annotation.)");
+ push_error(R"(Expected newline after a standalone annotation.)");
}
annotation->apply(this, head);
} else {
@@ -585,6 +617,7 @@ void GDScriptParser::parse_program() {
}
parse_class_body(true);
+ complete_extents(head);
#ifdef TOOLS_ENABLED
for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
@@ -625,6 +658,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
current_class = previous_class;
+ complete_extents(n_class);
return n_class;
}
@@ -637,6 +671,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
}
parse_class_body(multiline);
+ complete_extents(n_class);
if (multiline) {
consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
@@ -803,9 +838,18 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
break;
case GDScriptTokenizer::Token::ANNOTATION: {
advance();
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::CLASS_LEVEL);
+
+ // Check for class-level annotations.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- annotation_stack.push_back(annotation);
+ if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after a standalone annotation.)");
+ }
+ annotation->apply(this, head);
+ } else {
+ annotation_stack.push_back(annotation);
+ }
}
break;
}
@@ -837,11 +881,13 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable() {
}
GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) {
+ VariableNode *variable = alloc_node<VariableNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
+ complete_extents(variable);
return nullptr;
}
- VariableNode *variable = alloc_node<VariableNode>();
variable->identifier = parse_identifier();
variable->export_info.name = variable->identifier->name;
@@ -849,10 +895,10 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
if (check(GDScriptTokenizer::Token::NEWLINE)) {
if (p_allow_property) {
advance();
-
return parse_property(variable, true);
} else {
push_error(R"(Expected type after ":")");
+ complete_extents(variable);
return nullptr;
}
} else if (check((GDScriptTokenizer::Token::EQUAL))) {
@@ -891,6 +937,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
}
}
+ complete_extents(variable);
end_statement("variable declaration");
return variable;
@@ -899,6 +946,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) {
if (p_need_indent) {
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) {
+ complete_extents(p_variable);
return nullptr;
}
}
@@ -908,6 +956,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
make_completion_context(COMPLETION_PROPERTY_DECLARATION, property);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) {
+ complete_extents(p_variable);
return nullptr;
}
@@ -964,6 +1013,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
}
function = parse_identifier();
}
+ complete_extents(p_variable);
if (p_variable->property == VariableNode::PROP_SETGET) {
end_statement("property declaration");
@@ -978,37 +1028,37 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
switch (p_variable->property) {
case VariableNode::PROP_INLINE: {
- consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
- if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
- p_variable->setter_parameter = parse_identifier();
- }
- consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
- consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
-
+ FunctionNode *function = alloc_node<FunctionNode>();
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_setter";
-
- FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier;
- FunctionNode *previous_function = current_function;
- current_function = function;
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
ParameterNode *parameter = alloc_node<ParameterNode>();
- parameter->identifier = p_variable->setter_parameter;
-
- if (parameter->identifier != nullptr) {
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
+ reset_extents(parameter, previous);
+ p_variable->setter_parameter = parse_identifier();
+ parameter->identifier = p_variable->setter_parameter;
function->parameters_indices[parameter->identifier->name] = 0;
function->parameters.push_back(parameter);
+ }
+ complete_extents(parameter);
+
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
+ consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+ if (p_variable->setter_parameter != nullptr) {
SuiteNode *body = alloc_node<SuiteNode>();
body->add_local(parameter, function);
-
function->body = parse_suite("setter declaration", body);
p_variable->setter = function;
}
-
current_function = previous_function;
+ complete_extents(function);
break;
}
case VariableNode::PROP_SETGET:
@@ -1026,12 +1076,13 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
switch (p_variable->property) {
case VariableNode::PROP_INLINE: {
+ FunctionNode *function = alloc_node<FunctionNode>();
+
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_getter";
-
- FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier;
FunctionNode *previous_function = current_function;
@@ -1039,9 +1090,10 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
SuiteNode *body = alloc_node<SuiteNode>();
function->body = parse_suite("getter declaration", body);
-
p_variable->getter = function;
+
current_function = previous_function;
+ complete_extents(function);
break;
}
case VariableNode::PROP_SETGET:
@@ -1057,11 +1109,12 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
}
GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
+ ConstantNode *constant = alloc_node<ConstantNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
return nullptr;
}
- ConstantNode *constant = alloc_node<ConstantNode>();
constant->identifier = parse_identifier();
if (match(GDScriptTokenizer::Token::COLON)) {
@@ -1080,12 +1133,15 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
if (constant->initializer == nullptr) {
push_error(R"(Expected initializer expression for constant.)");
+ complete_extents(constant);
return nullptr;
}
} else {
+ complete_extents(constant);
return nullptr;
}
+ complete_extents(constant);
end_statement("constant declaration");
return constant;
@@ -1115,15 +1171,18 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
parameter->default_value = parse_expression(false);
}
+ complete_extents(parameter);
return parameter;
}
GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
+ SignalNode *signal = alloc_node<SignalNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
+ complete_extents(signal);
return nullptr;
}
- SignalNode *signal = alloc_node<SignalNode>();
signal->identifier = parse_identifier();
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
@@ -1155,6 +1214,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
}
+ complete_extents(signal);
end_statement("signal declaration");
return signal;
@@ -1266,6 +1326,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
}
#endif // TOOLS_ENABLED
+ complete_extents(enum_node);
end_statement("enum");
return enum_node;
@@ -1317,19 +1378,22 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
}
GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+ FunctionNode *function = alloc_node<FunctionNode>();
+
bool _static = false;
if (previous.type == GDScriptTokenizer::Token::STATIC) {
// TODO: Improve message if user uses "static" with "var" or "const"
if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
+ complete_extents(function);
return nullptr;
}
_static = true;
}
- FunctionNode *function = alloc_node<FunctionNode>();
make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
+ complete_extents(function);
return nullptr;
}
@@ -1351,6 +1415,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
function->body = parse_suite("function declaration", body);
current_function = previous_function;
+ complete_extents(function);
return function;
}
@@ -1398,6 +1463,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
}
pop_completion_call();
}
+ complete_extents(annotation);
match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
@@ -1416,12 +1482,12 @@ void GDScriptParser::clear_unused_annotations() {
annotation_stack.clear();
}
-bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments, bool p_is_vararg) {
+bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments, bool p_is_vararg) {
ERR_FAIL_COND_V_MSG(valid_annotations.has(p_info.name), false, vformat(R"(Annotation "%s" already registered.)", p_info.name));
AnnotationInfo new_annotation;
new_annotation.info = p_info;
- new_annotation.info.default_arguments.resize(p_optional_arguments);
+ new_annotation.info.default_arguments = p_default_arguments;
if (p_is_vararg) {
new_annotation.info.flags |= METHOD_FLAG_VARARG;
}
@@ -1447,9 +1513,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
if (multiline) {
if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
current_suite = suite->parent_block;
+ complete_extents(suite);
return suite;
}
}
+ reset_extents(suite, current);
int error_count = 0;
@@ -1499,6 +1567,8 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
} while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
+ complete_extents(suite);
+
if (multiline) {
if (!lambda_ended) {
consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
@@ -1529,6 +1599,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::PASS:
advance();
result = alloc_node<PassNode>();
+ complete_extents(result);
end_statement(R"("pass")");
break;
case GDScriptTokenizer::Token::VAR:
@@ -1576,6 +1647,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
// If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
n_return->return_value = parse_expression(false);
}
+ complete_extents(n_return);
result = n_return;
current_suite->has_return = true;
@@ -1586,6 +1658,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::BREAKPOINT:
advance();
result = alloc_node<BreakpointNode>();
+ complete_extents(result);
end_statement(R"("breakpoint")");
break;
case GDScriptTokenizer::Token::ASSERT:
@@ -1611,10 +1684,12 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
lambda_ended = true;
has_ended_lambda = true;
} else {
+ advance();
push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
}
+ } else {
+ end_statement("expression");
}
- end_statement("expression");
lambda_ended = lambda_ended || has_ended_lambda;
result = expression;
@@ -1626,6 +1701,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case Node::AWAIT:
// Fine.
break;
+ case Node::LAMBDA:
+ // Standalone lambdas can't be used, so make this an error.
+ push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
+ break;
default:
push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
@@ -1673,6 +1752,7 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->condition = parse_expression(false);
if (assert->condition == nullptr) {
push_error("Expected expression to assert.");
+ complete_extents(assert);
return nullptr;
}
@@ -1681,12 +1761,14 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->message = parse_expression(false);
if (assert->message == nullptr) {
push_error(R"(Expected error message for assert after ",".)");
+ complete_extents(assert);
return nullptr;
}
}
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
+ complete_extents(assert);
end_statement(R"("assert")");
return assert;
@@ -1696,8 +1778,10 @@ GDScriptParser::BreakNode *GDScriptParser::parse_break() {
if (!can_break) {
push_error(R"(Cannot use "break" outside of a loop.)");
}
+ BreakNode *break_node = alloc_node<BreakNode>();
+ complete_extents(break_node);
end_statement(R"("break")");
- return alloc_node<BreakNode>();
+ return break_node;
}
GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
@@ -1705,9 +1789,10 @@ GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)");
}
current_suite->has_continue = true;
- end_statement(R"("continue")");
ContinueNode *cont = alloc_node<ContinueNode>();
cont->is_for_match = is_continue_match;
+ complete_extents(cont);
+ end_statement(R"("continue")");
return cont;
}
@@ -1740,11 +1825,16 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
+ const SuiteNode::Local &local = current_suite->get_local(n_for->variable->name);
+ if (local.type != SuiteNode::Local::UNDEFINED) {
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), n_for->variable->name), n_for->variable);
+ }
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite);
+ complete_extents(n_for);
// Reset break/continue state.
can_break = could_break;
@@ -1772,15 +1862,16 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
}
if (match(GDScriptTokenizer::Token::ELIF)) {
- IfNode *elif = parse_if("elif");
-
SuiteNode *else_block = alloc_node<SuiteNode>();
+ IfNode *elif = parse_if("elif");
else_block->statements.push_back(elif);
+ complete_extents(else_block);
n_if->false_block = else_block;
} else if (match(GDScriptTokenizer::Token::ELSE)) {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
n_if->false_block = parse_suite(R"("else" block)");
}
+ complete_extents(n_if);
if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) {
current_suite->has_return = true;
@@ -1804,13 +1895,13 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
+ complete_extents(match);
return match;
}
#ifdef DEBUG_ENABLED
bool all_have_return = true;
bool have_wildcard = false;
- bool wildcard_has_return = false;
bool have_wildcard_without_continue = false;
#endif
@@ -1822,15 +1913,12 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
}
#ifdef DEBUG_ENABLED
- if (have_wildcard_without_continue) {
+ if (have_wildcard_without_continue && !branch->patterns.is_empty()) {
push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN);
}
if (branch->has_wildcard) {
have_wildcard = true;
- if (branch->block->has_return) {
- wildcard_has_return = true;
- }
if (!branch->block->has_continue) {
have_wildcard_without_continue = true;
}
@@ -1841,11 +1929,12 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
#endif
match->branches.push_back(branch);
}
+ complete_extents(match);
consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
#ifdef DEBUG_ENABLED
- if (wildcard_has_return || (all_have_return && have_wildcard)) {
+ if (all_have_return && have_wildcard) {
current_suite->has_return = true;
}
#endif
@@ -1855,6 +1944,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
MatchBranchNode *branch = alloc_node<MatchBranchNode>();
+ reset_extents(branch, current);
bool has_bind = false;
@@ -1863,7 +1953,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
if (pattern == nullptr) {
continue;
}
- if (pattern->pattern_type == PatternNode::PT_BIND) {
+ if (pattern->binds.size() > 0) {
has_bind = true;
}
if (branch->patterns.size() > 0 && has_bind) {
@@ -1882,6 +1972,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
}
if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
+ complete_extents(branch);
return nullptr;
}
@@ -1894,16 +1985,15 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
- List<StringName> binds;
- branch->patterns[0]->binds.get_key_list(&binds);
-
- for (const StringName &E : binds) {
- SuiteNode::Local local(branch->patterns[0]->binds[E], current_function);
+ for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
+ SuiteNode::Local local(E.value, current_function);
+ local.type = SuiteNode::Local::PATTERN_BIND;
suite->add_local(local);
}
}
branch->block = parse_suite("match pattern block", suite);
+ complete_extents(branch);
// Restore continue state.
can_continue = could_continue;
@@ -1914,12 +2004,14 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
PatternNode *pattern = alloc_node<PatternNode>();
+ reset_extents(pattern, current);
switch (current.type) {
case GDScriptTokenizer::Token::VAR: {
// Bind.
advance();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
+ complete_extents(pattern);
return nullptr;
}
pattern->pattern_type = PatternNode::PT_BIND;
@@ -1930,12 +2022,14 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
if (p_root_pattern != nullptr) {
if (p_root_pattern->has_bind(pattern->bind->name)) {
push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name));
+ complete_extents(pattern);
return nullptr;
}
}
if (current_suite->has_local(pattern->bind->name)) {
push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
+ complete_extents(pattern);
return nullptr;
}
@@ -1988,6 +2082,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
} else {
PatternNode *sub_pattern = alloc_node<PatternNode>();
+ complete_extents(sub_pattern);
sub_pattern->pattern_type = PatternNode::PT_REST;
pattern->dictionary.push_back({ nullptr, sub_pattern });
pattern->rest_used = true;
@@ -2036,6 +2131,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
break;
}
}
+ complete_extents(pattern);
return pattern;
}
@@ -2069,6 +2165,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
+ complete_extents(n_while);
// Reset break/continue state.
can_break = could_break;
@@ -2107,7 +2204,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -2141,6 +2238,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
}
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = previous.get_identifier();
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
@@ -2192,6 +2290,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_
}
LiteralNode *literal = alloc_node<LiteralNode>();
+ complete_extents(literal);
literal->value = previous.literal;
return literal;
}
@@ -2201,6 +2300,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
push_error(R"(Cannot use "self" inside a static function.)");
}
SelfNode *self = alloc_node<SelfNode>();
+ complete_extents(self);
self->current_class = current_class;
return self;
}
@@ -2208,6 +2308,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token::Type op_type = previous.type;
LiteralNode *constant = alloc_node<LiteralNode>();
+ complete_extents(constant);
switch (op_type) {
case GDScriptTokenizer::Token::CONST_PI:
@@ -2268,30 +2369,38 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
}
break;
default:
+ complete_extents(operation);
return nullptr; // Unreachable.
}
+ complete_extents(operation);
return operation;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
- UnaryOpNode *operation = alloc_node<UnaryOpNode>();
operation->operation = UnaryOpNode::OP_LOGIC_NOT;
operation->variant_op = Variant::OP_NOT;
operation->operand = in_operation;
+ complete_extents(operation);
return operation;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>();
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1);
operation->left_operand = p_previous_operand;
operation->right_operand = parse_precedence(precedence, false);
+ complete_extents(operation);
if (operation->right_operand == nullptr) {
push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
@@ -2319,6 +2428,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
operation->operation = BinaryOpNode::OP_MODULO;
operation->variant_op = Variant::OP_MODULE;
break;
+ case GDScriptTokenizer::Token::STAR_STAR:
+ operation->operation = BinaryOpNode::OP_POWER;
+ operation->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::LESS_LESS:
operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_LEFT;
@@ -2390,8 +2503,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// Only one ternary operation exists, so no abstraction here.
TernaryOpNode *operation = alloc_node<TernaryOpNode>();
- operation->true_expr = p_previous_operand;
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
+ operation->true_expr = p_previous_operand;
operation->condition = parse_precedence(PREC_TERNARY, false);
if (operation->condition == nullptr) {
@@ -2406,6 +2521,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio
push_error(R"(Expected expression after "else".)");
}
+ complete_extents(operation);
return operation;
}
@@ -2458,6 +2574,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
AssignmentNode *assignment = alloc_node<AssignmentNode>();
+ reset_extents(assignment, p_previous_operand);
+ update_extents(assignment);
+
make_completion_context(COMPLETION_ASSIGN, assignment);
#ifdef DEBUG_ENABLED
bool has_operator = true;
@@ -2482,6 +2601,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
assignment->operation = AssignmentNode::OP_MULTIPLICATION;
assignment->variant_op = Variant::OP_MULTIPLY;
break;
+ case GDScriptTokenizer::Token::STAR_STAR_EQUAL:
+ assignment->operation = AssignmentNode::OP_POWER;
+ assignment->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::SLASH_EQUAL:
assignment->operation = AssignmentNode::OP_DIVISION;
assignment->variant_op = Variant::OP_DIVIDE;
@@ -2518,6 +2641,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
if (assignment->assigned_value == nullptr) {
push_error(R"(Expected an expression after "=".)");
}
+ complete_extents(assignment);
#ifdef DEBUG_ENABLED
if (source_variable != nullptr) {
@@ -2539,6 +2663,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr
push_error(R"(Expected signal or coroutine after "await".)");
}
await->to_await = element;
+ complete_extents(await);
if (current_function) { // Might be null in a getter or setter.
current_function->is_coroutine = true;
@@ -2567,6 +2692,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_pr
}
pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)");
+ complete_extents(array);
return array;
}
@@ -2658,6 +2784,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
}
pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)");
+ complete_extents(dictionary);
return dictionary;
}
@@ -2675,6 +2802,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p
GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *attribute = alloc_node<SubscriptNode>();
+ reset_extents(attribute, p_previous_operand);
+ update_extents(attribute);
if (for_completion) {
bool is_builtin = false;
@@ -2694,17 +2823,21 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
attribute->base = p_previous_operand;
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
+ complete_extents(attribute);
return attribute;
}
attribute->is_attribute = true;
attribute->attribute = parse_identifier();
+ complete_extents(attribute);
return attribute;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *subscript = alloc_node<SubscriptNode>();
+ reset_extents(subscript, p_previous_operand);
+ update_extents(subscript);
make_completion_context(COMPLETION_SUBSCRIPT, subscript);
@@ -2717,15 +2850,19 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
+ complete_extents(subscript);
return subscript;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) {
CastNode *cast = alloc_node<CastNode>();
+ reset_extents(cast, p_previous_operand);
+ update_extents(cast);
cast->operand = p_previous_operand;
cast->cast_type = parse_type();
+ complete_extents(cast);
if (cast->cast_type == nullptr) {
push_error(R"(Expected type specifier after "as".)");
@@ -2737,6 +2874,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) {
CallNode *call = alloc_node<CallNode>();
+ reset_extents(call, p_previous_operand);
if (previous.type == GDScriptTokenizer::Token::SUPER) {
// Super call.
@@ -2747,6 +2885,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
if (current_function == nullptr) {
push_error(R"(Cannot use implicit "super" call outside of a function.)");
pop_multiline();
+ complete_extents(call);
return nullptr;
}
if (current_function->identifier) {
@@ -2759,6 +2898,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
make_completion_context(COMPLETION_SUPER_METHOD, call, true);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
pop_multiline();
+ complete_extents(call);
return nullptr;
}
IdentifierNode *identifier = parse_identifier();
@@ -2815,56 +2955,108 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
+ complete_extents(call);
return call;
}
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 identifier after "$".)");
+ if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
+ return nullptr;
+ }
+
+ if (check(GDScriptTokenizer::Token::LITERAL)) {
+ if (current.literal.get_type() != Variant::STRING) {
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
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 (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 (!current.is_node_name()) {
- push_error(R"(Expect node path after "/".)");
+ }
+
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+
+ // Store the last item in the path so the parser knows what to expect.
+ // Allow allows more specific error messages.
+ enum PathState {
+ PATH_STATE_START,
+ PATH_STATE_SLASH,
+ PATH_STATE_PERCENT,
+ PATH_STATE_NODE_NAME,
+ } path_state = PATH_STATE_START;
+
+ if (previous.type == GDScriptTokenizer::Token::DOLLAR) {
+ // Detect initial slash, which will be handled in the loop if it matches.
+ match(GDScriptTokenizer::Token::SLASH);
+#ifdef DEBUG_ENABLED
+ } else {
+ get_node->use_dollar = false;
+#endif
+ }
+
+ int context_argument = 0;
+
+ do {
+ if (previous.type == GDScriptTokenizer::Token::PERCENT) {
+ if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) {
+ push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))");
+ complete_extents(get_node);
return nullptr;
}
- 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 if (match(GDScriptTokenizer::Token::SLASH)) {
- GetNodeNode *get_node = alloc_node<GetNodeNode>();
- IdentifierNode *identifier_root = alloc_node<IdentifierNode>();
- get_node->chain.push_back(identifier_root);
- int chain_position = 0;
- do {
- make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
- if (!current.is_node_name()) {
- push_error(R"(Expect node path after "/".)");
+ get_node->full_path += "%";
+
+ path_state = PATH_STATE_PERCENT;
+ } else if (previous.type == GDScriptTokenizer::Token::SLASH) {
+ if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) {
+ push_error(R"("/" is only valid at the beginning of the path or after a node name.)");
+ complete_extents(get_node);
+ return nullptr;
+ }
+
+ get_node->full_path += "/";
+
+ path_state = PATH_STATE_SLASH;
+ }
+
+ make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++);
+
+ if (match(GDScriptTokenizer::Token::LITERAL)) {
+ if (previous.literal.get_type() != Variant::STRING) {
+ String previous_token;
+ switch (path_state) {
+ case PATH_STATE_START:
+ previous_token = "$";
+ break;
+ case PATH_STATE_PERCENT:
+ previous_token = "%";
+ break;
+ case PATH_STATE_SLASH:
+ previous_token = "/";
+ break;
+ default:
+ break;
+ }
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token));
+ complete_extents(get_node);
return nullptr;
}
+
+ get_node->full_path += previous.literal.operator String();
+
+ path_state = PATH_STATE_NODE_NAME;
+ } else if (current.is_node_name()) {
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 identifier after "$".)");
- return nullptr;
- }
+ get_node->full_path += previous.get_identifier();
+
+ path_state = PATH_STATE_NODE_NAME;
+ } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
+ push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name()));
+ complete_extents(get_node);
+ return nullptr;
+ }
+ } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT));
+
+ complete_extents(get_node);
+ return get_node;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) {
@@ -2887,6 +3079,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*");
+ complete_extents(preload);
return preload;
}
@@ -2922,6 +3115,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
current_function = function;
SuiteNode *body = alloc_node<SuiteNode>();
+ body->parent_function = current_function;
+ body->parent_block = current_suite;
+
SuiteNode *previous_suite = current_suite;
current_suite = body;
@@ -2933,6 +3129,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
in_lambda = true;
function->body = parse_suite("lambda declaration", body, true);
+ complete_extents(function);
+ complete_extents(lambda);
pop_multiline();
@@ -2977,13 +3175,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
if (match(GDScriptTokenizer::Token::VOID)) {
if (p_allow_void) {
- TypeNode *void_type = alloc_node<TypeNode>();
+ complete_extents(type);
+ TypeNode *void_type = type;
return void_type;
} else {
push_error(R"("void" is only allowed for a function return type.)");
}
}
// Leave error message to the caller who knows the context.
+ complete_extents(type);
return nullptr;
}
@@ -2996,11 +3196,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
type->container_type = parse_type(false); // Don't allow void for array element type.
if (type->container_type == nullptr) {
push_error(R"(Expected type for collection after "[".)");
+ complete_extents(type);
type = nullptr;
} else if (type->container_type->container_type != nullptr) {
push_error("Nested typed collections are not supported.");
}
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
+ if (type != nullptr) {
+ complete_extents(type);
+ }
return type;
}
@@ -3013,6 +3217,7 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
}
}
+ complete_extents(type);
return type;
}
@@ -3049,7 +3254,7 @@ bool GDScriptParser::has_comment(int p_line) {
}
String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
ERR_FAIL_COND_V(!comments.has(p_line), String());
if (p_single_line) {
@@ -3101,7 +3306,7 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
}
void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
if (!comments.has(p_line)) {
return;
}
@@ -3134,24 +3339,23 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
String title, link; // For tutorials.
String doc_line = comments[line++].comment.trim_prefix("##");
- String striped_line = doc_line.strip_edges();
+ String stripped_line = doc_line.strip_edges();
// Set the read mode.
- if (striped_line.begins_with("@desc:") && p_desc.is_empty()) {
+ if (stripped_line.is_empty() && mode == BRIEF && !p_brief.is_empty()) {
mode = DESC;
- striped_line = striped_line.trim_prefix("@desc:");
- in_codeblock = _in_codeblock(doc_line, in_codeblock);
+ continue;
- } else if (striped_line.begins_with("@tutorial")) {
+ } else if (stripped_line.begins_with("@tutorial")) {
int begin_scan = String("@tutorial").length();
- if (begin_scan >= striped_line.length()) {
+ if (begin_scan >= stripped_line.length()) {
continue; // invalid syntax.
}
- if (striped_line[begin_scan] == ':') { // No title.
+ if (stripped_line[begin_scan] == ':') { // No title.
// Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
title = "";
- link = striped_line.trim_prefix("@tutorial:").strip_edges();
+ link = stripped_line.trim_prefix("@tutorial:").strip_edges();
} else {
/* Syntax:
@@ -3159,35 +3363,35 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
* ^ open ^ close ^ colon ^ url
*/
int open_bracket_pos = begin_scan, close_bracket_pos = 0;
- while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) {
+ while (open_bracket_pos < stripped_line.length() && (stripped_line[open_bracket_pos] == ' ' || stripped_line[open_bracket_pos] == '\t')) {
open_bracket_pos++;
}
- if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') {
+ if (open_bracket_pos == stripped_line.length() || stripped_line[open_bracket_pos++] != '(') {
continue; // invalid syntax.
}
close_bracket_pos = open_bracket_pos;
- while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') {
+ while (close_bracket_pos < stripped_line.length() && stripped_line[close_bracket_pos] != ')') {
close_bracket_pos++;
}
- if (close_bracket_pos == striped_line.length()) {
+ if (close_bracket_pos == stripped_line.length()) {
continue; // invalid syntax.
}
int colon_pos = close_bracket_pos + 1;
- while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) {
+ while (colon_pos < stripped_line.length() && (stripped_line[colon_pos] == ' ' || stripped_line[colon_pos] == '\t')) {
colon_pos++;
}
- if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') {
+ if (colon_pos == stripped_line.length() || stripped_line[colon_pos++] != ':') {
continue; // invalid syntax.
}
- title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
- link = striped_line.substr(colon_pos).strip_edges();
+ title = stripped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
+ link = stripped_line.substr(colon_pos).strip_edges();
}
mode = TUTORIALS;
in_codeblock = false;
- } else if (striped_line.is_empty()) {
+ } else if (stripped_line.is_empty()) {
continue;
} else {
// Tutorial docs are single line, we need a @tag after it.
@@ -3207,7 +3411,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
}
doc_line = doc_line.substr(i);
} else {
- doc_line = striped_line;
+ doc_line = stripped_line;
}
String line_join = (in_codeblock) ? "\n" : " ";
@@ -3222,7 +3426,16 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
p_tutorials.append(Pair<String, String>(title, link));
break;
case DONE:
- return;
+ break;
+ }
+ }
+ if (current_class->members.size() > 0) {
+ const ClassNode::Member &m = current_class->members[0];
+ int first_member_line = m.get_line();
+ if (first_member_line == line) {
+ p_brief = "";
+ p_desc = "";
+ p_tutorials.clear();
}
}
}
@@ -3264,13 +3477,15 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
- { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
+ { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
// Assignment
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
@@ -3545,8 +3760,12 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.native_type;
+ } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
+ variable->export_info.hint_string = export_type.native_type;
} else {
- push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
return false;
}
break;
@@ -3555,14 +3774,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint = PROPERTY_HINT_ENUM;
String enum_hint_string;
- for (OrderedHashMap<StringName, int>::Element E = export_type.enum_values.front(); E; E = E.next()) {
- enum_hint_string += E.key().operator String().capitalize().xml_escape();
- enum_hint_string += ":";
- enum_hint_string += String::num_int64(E.value()).xml_escape();
-
- if (E.next()) {
+ bool first = true;
+ for (const KeyValue<StringName, int> &E : export_type.enum_values) {
+ if (!first) {
enum_hint_string += ",";
+ } else {
+ first = false;
}
+ enum_hint_string += E.key.operator String().capitalize().xml_escape();
+ enum_hint_string += ":";
+ enum_hint_string += String::num_int64(E.value).xml_escape();
}
variable->export_info.hint_string = enum_hint_string;
@@ -3596,6 +3817,36 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
return true;
}
+template <PropertyUsageFlags t_usage>
+bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+ AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
+
+ annotation->export_info.name = annotation->resolved_arguments[0];
+
+ switch (t_usage) {
+ case PROPERTY_USAGE_CATEGORY: {
+ annotation->export_info.usage = t_usage;
+ } break;
+
+ case PROPERTY_USAGE_GROUP: {
+ annotation->export_info.usage = t_usage;
+ if (annotation->resolved_arguments.size() == 2) {
+ annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ }
+ } break;
+
+ case PROPERTY_USAGE_SUBGROUP: {
+ annotation->export_info.usage = t_usage;
+ if (annotation->resolved_arguments.size() == 2) {
+ annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ }
+ } break;
+ }
+
+ current_class->add_member_group(annotation);
+ return true;
+}
+
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
#ifdef DEBUG_ENABLED
bool has_error = false;
@@ -3617,16 +3868,21 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
#endif // DEBUG_ENABLED
}
-template <Multiplayer::RPCMode t_mode>
-bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) {
- ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name));
+bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_node) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to functions.)", p_annotation->name));
- Multiplayer::RPCConfig rpc_config;
- rpc_config.rpc_mode = t_mode;
+ FunctionNode *function = static_cast<FunctionNode *>(p_node);
+ if (function->rpc_config.get_type() != Variant::NIL) {
+ push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
+ return false;
+ }
+
+ Dictionary rpc_config;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
if (p_annotation->resolved_arguments.size()) {
int last = p_annotation->resolved_arguments.size() - 1;
if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) {
- rpc_config.channel = p_annotation->resolved_arguments[last].operator int();
+ rpc_config["channel"] = p_annotation->resolved_arguments[last].operator int();
last -= 1;
}
if (last > 3) {
@@ -3636,37 +3892,25 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
for (int i = last; i >= 0; i--) {
String mode = p_annotation->resolved_arguments[i].operator String();
if (mode == "any_peer") {
- rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY_PEER;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
} else if (mode == "authority") {
- rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
} else if (mode == "call_local") {
- rpc_config.call_local = true;
+ rpc_config["call_local"] = true;
} else if (mode == "call_remote") {
- rpc_config.call_local = false;
+ rpc_config["call_local"] = false;
} else if (mode == "reliable") {
- rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
+ rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
} else if (mode == "unreliable") {
- rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE;
+ rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
} else if (mode == "unreliable_ordered") {
- rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED;
+ rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
}
}
}
- switch (p_node->type) {
- case Node::FUNCTION: {
- FunctionNode *function = static_cast<FunctionNode *>(p_node);
- if (function->rpc_config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
- push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
- return false;
- }
- function->rpc_config = rpc_config;
- break;
- }
- default:
- return false; // Unreachable.
- }
+ function->rpc_config = rpc_config;
return true;
}
@@ -3793,6 +4037,46 @@ GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() co
return type;
}
+void GDScriptParser::complete_extents(Node *p_node) {
+ while (!nodes_in_progress.is_empty() && nodes_in_progress.back()->get() != p_node) {
+ ERR_PRINT("Parser bug: Mismatch in extents tracking stack.");
+ nodes_in_progress.pop_back();
+ }
+ if (nodes_in_progress.is_empty()) {
+ ERR_PRINT("Parser bug: Extents tracking stack is empty.");
+ } else {
+ nodes_in_progress.pop_back();
+ }
+}
+
+void GDScriptParser::update_extents(Node *p_node) {
+ p_node->end_line = previous.end_line;
+ p_node->end_column = previous.end_column;
+ p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column);
+ p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column);
+}
+
+void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) {
+ p_node->start_line = p_token.start_line;
+ p_node->end_line = p_token.end_line;
+ p_node->start_column = p_token.start_column;
+ p_node->end_column = p_token.end_column;
+ p_node->leftmost_column = p_token.leftmost_column;
+ p_node->rightmost_column = p_token.rightmost_column;
+}
+
+void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
+ if (p_from == nullptr) {
+ return;
+ }
+ p_node->start_line = p_from->start_line;
+ p_node->end_line = p_from->end_line;
+ p_node->start_column = p_from->start_column;
+ p_node->end_column = p_from->end_column;
+ p_node->leftmost_column = p_from->leftmost_column;
+ p_node->rightmost_column = p_from->rightmost_column;
+}
+
/*---------- PRETTY PRINT FOR DEBUG ----------*/
#ifdef DEBUG_ENABLED
@@ -3895,6 +4179,9 @@ void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment)
case AssignmentNode::OP_MODULO:
push_text("%");
break;
+ case AssignmentNode::OP_POWER:
+ push_text("**");
+ break;
case AssignmentNode::OP_BIT_SHIFT_LEFT:
push_text("<<");
break;
@@ -3943,6 +4230,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
case BinaryOpNode::OP_MODULO:
push_text(" % ");
break;
+ case BinaryOpNode::OP_POWER:
+ push_text(" ** ");
+ break;
case BinaryOpNode::OP_BIT_LEFT_SHIFT:
push_text(" << ");
break;
@@ -4073,6 +4363,8 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
break;
case ClassNode::Member::ENUM_VALUE:
break; // Nothing. Will be printed by enum.
+ case ClassNode::Member::GROUP:
+ break; // Nothing. Groups are only used by inspector.
case ClassNode::Member::UNDEFINED:
push_line("<unknown member>");
break;
@@ -4236,17 +4528,10 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const
}
void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
- push_text("$");
- if (p_get_node->string != nullptr) {
- print_literal(p_get_node->string);
- } else {
- for (int i = 0; i < p_get_node->chain.size(); i++) {
- if (i > 0) {
- push_text("/");
- }
- print_identifier(p_get_node->chain[i]);
- }
+ if (p_get_node->use_dollar) {
+ push_text("$");
}
+ push_text(p_get_node->full_path);
}
void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 10474db02f..d4efab173b 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -32,14 +32,13 @@
#define GDSCRIPT_PARSER_H
#include "core/io/resource.h"
-#include "core/multiplayer/multiplayer.h"
#include "core/object/ref_counted.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/rb_map.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "gdscript_cache.h"
@@ -132,7 +131,7 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- OrderedHashMap<StringName, int> enum_values; // For enums.
+ HashMap<StringName, int> enum_values; // For enums.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
@@ -325,6 +324,7 @@ public:
Vector<Variant> resolved_arguments;
AnnotationInfo *info = nullptr;
+ PropertyInfo export_info;
bool apply(GDScriptParser *p_this, Node *p_target) const;
bool applies_to(uint32_t p_target_kinds) const;
@@ -360,6 +360,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
+ OP_POWER,
OP_BIT_SHIFT_LEFT,
OP_BIT_SHIFT_RIGHT,
OP_BIT_AND,
@@ -393,6 +394,7 @@ public:
OP_MULTIPLICATION,
OP_DIVISION,
OP_MODULO,
+ OP_POWER,
OP_BIT_LEFT_SHIFT,
OP_BIT_RIGHT_SHIFT,
OP_BIT_AND,
@@ -498,6 +500,7 @@ public:
VARIABLE,
ENUM,
ENUM_VALUE, // For unnamed enums.
+ GROUP, // For member grouping.
};
Type type = UNDEFINED;
@@ -509,6 +512,7 @@ public:
SignalNode *signal;
VariableNode *variable;
EnumNode *m_enum;
+ AnnotationNode *annotation;
};
EnumNode::Value enum_value;
@@ -530,6 +534,8 @@ public:
return "enum";
case ENUM_VALUE:
return "enum value";
+ case GROUP:
+ return "group";
}
return "";
}
@@ -550,6 +556,8 @@ public:
return m_enum->start_line;
case SIGNAL:
return signal->start_line;
+ case GROUP:
+ return annotation->start_line;
case UNDEFINED:
ERR_FAIL_V_MSG(-1, "Reaching undefined member type.");
}
@@ -584,6 +592,9 @@ public:
// TODO: Add parameter info.
return type;
}
+ case GROUP: {
+ return DataType();
+ }
case UNDEFINED:
return DataType();
}
@@ -620,6 +631,10 @@ public:
type = ENUM_VALUE;
enum_value = p_enum_value;
}
+ Member(AnnotationNode *p_annotation) {
+ type = GROUP;
+ annotation = p_annotation;
+ }
};
IdentifierNode *identifier = nullptr;
@@ -666,6 +681,10 @@ public:
members_indices[p_enum_value.identifier->name] = members.size();
members.push_back(Member(p_enum_value));
}
+ void add_member_group(AnnotationNode *p_annotation_node) {
+ members_indices[p_annotation_node->export_info.name] = members.size();
+ members.push_back(Member(p_annotation_node));
+ }
ClassNode() {
type = CLASS;
@@ -730,7 +749,7 @@ public:
SuiteNode *body = nullptr;
bool is_static = false;
bool is_coroutine = false;
- Multiplayer::RPCConfig rpc_config;
+ Variant rpc_config;
MethodInfo info;
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
@@ -747,8 +766,10 @@ public:
};
struct GetNodeNode : public ExpressionNode {
- LiteralNode *string = nullptr;
- Vector<IdentifierNode *> chain;
+ String full_path;
+#ifdef DEBUG_ENABLED
+ bool use_dollar = true;
+#endif
GetNodeNode() {
type = GET_NODE;
@@ -800,7 +821,7 @@ public:
FunctionNode *function = nullptr;
FunctionNode *parent_function = nullptr;
Vector<IdentifierNode *> captures;
- Map<StringName, int> captures_indices;
+ HashMap<StringName, int> captures_indices;
bool use_self = false;
bool has_name() const {
@@ -1203,9 +1224,9 @@ private:
List<ParserError> errors;
#ifdef DEBUG_ENABLED
List<GDScriptWarning> warnings;
- Set<String> ignored_warnings;
- Set<uint32_t> ignored_warning_codes;
- Set<int> unsafe_lines;
+ HashSet<String> ignored_warnings;
+ HashSet<uint32_t> ignored_warning_codes;
+ HashSet<int> unsafe_lines;
#endif
GDScriptTokenizer tokenizer;
@@ -1234,6 +1255,7 @@ private:
SIGNAL = 1 << 4,
FUNCTION = 1 << 5,
STATEMENT = 1 << 6,
+ STANDALONE = 1 << 7,
CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
};
uint32_t target_kind = 0; // Flags.
@@ -1263,6 +1285,7 @@ private:
PREC_FACTOR,
PREC_SIGN,
PREC_BIT_NOT,
+ PREC_POWER,
PREC_TYPE_TEST,
PREC_AWAIT,
PREC_CALL,
@@ -1277,6 +1300,12 @@ private:
};
static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type);
+ List<Node *> nodes_in_progress;
+ void complete_extents(Node *p_node);
+ void update_extents(Node *p_node);
+ void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
+ void reset_extents(Node *p_node, Node *p_from);
+
template <class T>
T *alloc_node() {
T *node = memnew(T);
@@ -1284,13 +1313,8 @@ private:
node->next = list;
list = node;
- // TODO: Properly set positions for all nodes.
- node->start_line = previous.start_line;
- node->end_line = previous.end_line;
- node->start_column = previous.start_column;
- node->end_column = previous.end_column;
- node->leftmost_column = previous.leftmost_column;
- node->rightmost_column = previous.rightmost_column;
+ reset_extents(node, previous);
+ nodes_in_progress.push_back(node);
return node;
}
@@ -1335,7 +1359,7 @@ private:
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
- bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
+ bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
bool validate_annotation_arguments(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
@@ -1343,9 +1367,10 @@ private:
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ template <PropertyUsageFlags t_usage>
+ bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
- template <Multiplayer::RPCMode t_mode>
- bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ bool rpc_annotation(const AnnotationNode *p_annotation, Node *p_target);
// Statements.
Node *parse_statement();
VariableNode *parse_variable();
@@ -1408,6 +1433,7 @@ public:
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
void get_annotation_list(List<MethodInfo> *r_annotations) const;
+ bool annotation_exists(const String &p_annotation_name) const;
const List<ParserError> &get_errors() const { return errors; }
const List<String> get_dependencies() const {
@@ -1416,7 +1442,7 @@ public:
}
#ifdef DEBUG_ENABLED
const List<GDScriptWarning> &get_warnings() const { return warnings; }
- const Set<int> &get_unsafe_lines() const { return unsafe_lines; }
+ const HashSet<int> &get_unsafe_lines() const { return unsafe_lines; }
int get_last_line_number() const { return current.end_line; }
#endif
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index 07ef5aefcb..4e12419357 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -71,16 +71,16 @@ GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_m
object = p_object;
method = p_method;
h = method.hash();
- h = hash_djb2_one_64(object->get_instance_id(), h);
+ h = hash_murmur3_one_64(object->get_instance_id(), h);
node = Object::cast_to<Node>(object);
ERR_FAIL_COND_MSG(!node, "RPC can only be defined on class that extends Node.");
}
-void GDScriptRPCCallable::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
+Error GDScriptRPCCallable::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
if (unlikely(!node)) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- return;
+ return ERR_UNCONFIGURED;
}
r_call_error.error = Callable::CallError::CALL_OK;
- node->rpcp(p_peer_id, method, p_arguments, p_argcount);
+ return node->rpcp(p_peer_id, method, p_arguments, p_argcount);
}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
index 2c8734a74b..83b9c7e2df 100644
--- a/modules/gdscript/gdscript_rpc_callable.h
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_RPC_CALLABLE
-#define GDSCRIPT_RPC_CALLABLE
+#ifndef GDSCRIPT_RPC_CALLABLE_H
+#define GDSCRIPT_RPC_CALLABLE_H
#include "core/variant/callable.h"
#include "core/variant/variant.h"
@@ -52,10 +52,10 @@ public:
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
- void rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
+ Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
GDScriptRPCCallable(Object *p_object, const StringName &p_method);
virtual ~GDScriptRPCCallable() = default;
};
-#endif // GDSCRIPT_RPC_CALLABLE
+#endif // GDSCRIPT_RPC_CALLABLE_H
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 63fad0d9bb..6c17afe939 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -67,6 +67,7 @@ static const char *token_names[] = {
"+", // PLUS,
"-", // MINUS,
"*", // STAR,
+ "**", // STAR_STAR,
"/", // SLASH,
"%", // PERCENT,
// Assignment
@@ -74,6 +75,7 @@ static const char *token_names[] = {
"+=", // PLUS_EQUAL,
"-=", // MINUS_EQUAL,
"*=", // STAR_EQUAL,
+ "**=", // STAR_STAR_EQUAL,
"/=", // SLASH_EQUAL,
"%=", // PERCENT_EQUAL,
"<<=", // LESS_LESS_EQUAL,
@@ -1403,6 +1405,14 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '=') {
_advance();
return make_token(Token::STAR_EQUAL);
+ } else if (_peek() == '*') {
+ if (_peek(1) == '=') {
+ _advance();
+ _advance(); // Advance both '*' and '='
+ return make_token(Token::STAR_STAR_EQUAL);
+ }
+ _advance();
+ return make_token(Token::STAR_STAR);
} else {
return make_token(Token::STAR);
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index abd090e381..68b2c6eb1c 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,9 +31,9 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
+#include "core/templates/hash_map.h"
+#include "core/templates/hash_set.h"
#include "core/templates/list.h"
-#include "core/templates/map.h"
-#include "core/templates/set.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
@@ -78,6 +78,7 @@ public:
PLUS,
MINUS,
STAR,
+ STAR_STAR,
SLASH,
PERCENT,
// Assignment
@@ -85,6 +86,7 @@ public:
PLUS_EQUAL,
MINUS_EQUAL,
STAR_EQUAL,
+ STAR_STAR_EQUAL,
SLASH_EQUAL,
PERCENT_EQUAL,
LESS_LESS_EQUAL,
@@ -191,7 +193,7 @@ public:
new_line = p_new_line;
}
};
- const Map<int, CommentData> &get_comments() const {
+ const HashMap<int, CommentData> &get_comments() const {
return comments;
}
#endif // TOOLS_ENABLED
@@ -224,7 +226,7 @@ private:
int length = 0;
#ifdef TOOLS_ENABLED
- Map<int, CommentData> comments;
+ HashMap<int, CommentData> comments;
#endif // TOOLS_ENABLED
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
@@ -271,4 +273,4 @@ public:
GDScriptTokenizer();
};
-#endif
+#endif // GDSCRIPT_TOKENIZER_H
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 89d94d8635..4b97486cb3 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -115,6 +115,7 @@ struct GDScriptUtilityFunctionsDefinitions {
if (p_arg_count < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
+ r_error.expected = 1;
*r_ret = Variant();
return;
}
@@ -545,7 +546,7 @@ struct GDScriptUtilityFunctionsDefinitions {
};
struct GDScriptUtilityFunctionInfo {
- GDScriptUtilityFunctions::FunctionPtr function;
+ GDScriptUtilityFunctions::FunctionPtr function = nullptr;
MethodInfo info;
bool is_constant = false;
};
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index e28dd26c28..61e2c61abc 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -177,6 +177,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
err_text = "Invalid call. Nonexistent " + p_where + ".";
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ err_text = "Attempt to call " + p_where + " on a const instance.";
} else {
err_text = "Bug, call error: #" + itos(p_err.error);
}
@@ -197,11 +199,14 @@ void (*type_init_function_table[])(Variant *) = {
&VariantInitializer<Vector3>::init, // VECTOR3.
&VariantInitializer<Vector3i>::init, // VECTOR3I.
&VariantInitializer<Transform2D>::init, // TRANSFORM2D.
+ &VariantInitializer<Vector4>::init, // VECTOR4.
+ &VariantInitializer<Vector4i>::init, // VECTOR4I.
&VariantInitializer<Plane>::init, // PLANE.
&VariantInitializer<Quaternion>::init, // QUATERNION.
&VariantInitializer<AABB>::init, // AABB.
&VariantInitializer<Basis>::init, // BASIS.
&VariantInitializer<Transform3D>::init, // TRANSFORM3D.
+ &VariantInitializer<Projection>::init, // PROJECTION.
&VariantInitializer<Color>::init, // COLOR.
&VariantInitializer<StringName>::init, // STRING_NAME.
&VariantInitializer<NodePath>::init, // NODE_PATH.
@@ -280,11 +285,14 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_CALL_PTRCALL_VECTOR3, \
&&OPCODE_CALL_PTRCALL_VECTOR3I, \
&&OPCODE_CALL_PTRCALL_TRANSFORM2D, \
+ &&OPCODE_CALL_PTRCALL_VECTOR4, \
+ &&OPCODE_CALL_PTRCALL_VECTOR4I, \
&&OPCODE_CALL_PTRCALL_PLANE, \
&&OPCODE_CALL_PTRCALL_QUATERNION, \
&&OPCODE_CALL_PTRCALL_AABB, \
&&OPCODE_CALL_PTRCALL_BASIS, \
&&OPCODE_CALL_PTRCALL_TRANSFORM3D, \
+ &&OPCODE_CALL_PTRCALL_PROJECTION, \
&&OPCODE_CALL_PTRCALL_COLOR, \
&&OPCODE_CALL_PTRCALL_STRING_NAME, \
&&OPCODE_CALL_PTRCALL_NODE_PATH, \
@@ -311,6 +319,7 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
&&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_JUMP_IF_SHARED, \
&&OPCODE_RETURN, \
&&OPCODE_RETURN_TYPED_BUILTIN, \
&&OPCODE_RETURN_TYPED_ARRAY, \
@@ -369,11 +378,14 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_TYPE_ADJUST_VECTOR3, \
&&OPCODE_TYPE_ADJUST_VECTOR3I, \
&&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
+ &&OPCODE_TYPE_ADJUST_VECTOR4, \
+ &&OPCODE_TYPE_ADJUST_VECTOR4I, \
&&OPCODE_TYPE_ADJUST_PLANE, \
&&OPCODE_TYPE_ADJUST_QUATERNION, \
&&OPCODE_TYPE_ADJUST_AABB, \
&&OPCODE_TYPE_ADJUST_BASIS, \
&&OPCODE_TYPE_ADJUST_TRANSFORM3D, \
+ &&OPCODE_TYPE_ADJUST_PROJECTION, \
&&OPCODE_TYPE_ADJUST_COLOR, \
&&OPCODE_TYPE_ADJUST_STRING_NAME, \
&&OPCODE_TYPE_ADJUST_NODE_PATH, \
@@ -432,6 +444,8 @@ void (*type_init_function_table[])(Variant *) = {
#define OP_GET_VECTOR3 get_vector3
#define OP_GET_VECTOR3I get_vector3i
#define OP_GET_RECT2 get_rect2
+#define OP_GET_VECTOR4 get_vector4
+#define OP_GET_VECTOR4I get_vector4i
#define OP_GET_RECT2I get_rect2i
#define OP_GET_QUATERNION get_quaternion
#define OP_GET_COLOR get_color
@@ -453,6 +467,7 @@ void (*type_init_function_table[])(Variant *) = {
#define OP_GET_PACKED_COLOR_ARRAY get_color_array
#define OP_GET_TRANSFORM3D get_transform
#define OP_GET_TRANSFORM2D get_transform2d
+#define OP_GET_PROJECTION get_projection
#define OP_GET_PLANE get_plane
#define OP_GET_AABB get_aabb
#define OP_GET_BASIS get_basis
@@ -546,33 +561,32 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[i], Variant);
}
- memnew_placement(&stack[ADDR_STACK_NIL], Variant);
-
if (_instruction_args_size) {
instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
} else {
instruction_args = nullptr;
}
- if (p_instance) {
- memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
- script = p_instance->script.ptr();
- } else {
- memnew_placement(&stack[ADDR_STACK_SELF], Variant);
- script = _script;
+ for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
+ type_init_function_table[E.value](&stack[E.key]);
}
}
+
if (_ptrcall_args_size) {
call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
} else {
call_args_ptr = nullptr;
}
- memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
-
- for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
- type_init_function_table[E.value](&stack[E.key]);
+ if (p_instance) {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
+ script = p_instance->script.ptr();
+ } else {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant);
+ script = _script;
}
+ memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
+ memnew_placement(&stack[ADDR_STACK_NIL], Variant);
String err_text;
@@ -1030,11 +1044,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
#ifdef DEBUG_ENABLED
if (!valid) {
- if (src->has_method(*index)) {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
- } else {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
- }
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
OPCODE_BREAK;
}
*dst = ret;
@@ -1829,11 +1839,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_CALL_PTR(VECTOR3);
OPCODE_CALL_PTR(VECTOR3I);
OPCODE_CALL_PTR(TRANSFORM2D);
+ OPCODE_CALL_PTR(VECTOR4);
+ OPCODE_CALL_PTR(VECTOR4I);
OPCODE_CALL_PTR(PLANE);
OPCODE_CALL_PTR(QUATERNION);
OPCODE_CALL_PTR(AABB);
OPCODE_CALL_PTR(BASIS);
OPCODE_CALL_PTR(TRANSFORM3D);
+ OPCODE_CALL_PTR(PROJECTION);
OPCODE_CALL_PTR(COLOR);
OPCODE_CALL_PTR(STRING_NAME);
OPCODE_CALL_PTR(NODE_PATH);
@@ -1895,7 +1908,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
VariantInternal::initialize(ret, Variant::OBJECT);
Object **ret_opaque = VariantInternal::get_object(ret);
method->ptrcall(base_obj, argptrs, ret_opaque);
- VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too.
+ VariantInternal::update_object_id(ret);
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -2104,7 +2117,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
const GDScript *gds = _script;
- const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E;
while (gds->base.ptr()) {
gds = gds->base.ptr();
E = gds->member_functions.find(*methodname);
@@ -2116,7 +2129,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err;
if (E) {
- *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+ *dst = E->value->call(p_instance, (const Variant **)argptrs, argc, err);
} else if (gds->native.ptr()) {
if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
@@ -2171,8 +2184,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// Is this even possible to be null at this point?
if (obj) {
if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- static StringName completed = _scs_create("completed");
- result = Signal(obj, completed);
+ result = Signal(obj, "completed");
}
}
}
@@ -2193,8 +2205,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->function = this;
gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
+
+ // First 3 stack addresses are special, so we just skip them here.
+ for (int i = 3; i < _stack_size; i++) {
memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
}
gdfs->state.stack_size = _stack_size;
@@ -2221,7 +2234,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
retvalue = gdfs;
- Error err = sig.connect(callable_bind(Callable(gdfs.ptr(), "_signal_callback"), retvalue), Object::CONNECT_ONESHOT);
+ Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback").bind(retvalue), Object::CONNECT_ONESHOT);
if (err != OK) {
err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
OPCODE_BREAK;
@@ -2362,6 +2375,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_JUMP_IF_SHARED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(val, 0);
+
+ if (val->is_shared()) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_RETURN) {
CHECK_SPACE(2);
GET_INSTRUCTION_ARG(r, 0);
@@ -3267,6 +3295,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int globalname_idx = _code_ptr[ip + 2];
GD_ERR_BREAK(globalname_idx < 0 || globalname_idx >= _global_names_count);
const StringName *globalname = &_global_names_ptr[globalname_idx];
+ GD_ERR_BREAK(!GDScriptLanguage::get_singleton()->get_named_globals_map().has(*globalname));
GET_INSTRUCTION_ARG(dst, 0);
*dst = GDScriptLanguage::get_singleton()->get_named_globals_map()[*globalname];
@@ -3295,11 +3324,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_TYPE_ADJUST(VECTOR3, Vector3);
OPCODE_TYPE_ADJUST(VECTOR3I, Vector3i);
OPCODE_TYPE_ADJUST(TRANSFORM2D, Transform2D);
+ OPCODE_TYPE_ADJUST(VECTOR4, Vector4);
+ OPCODE_TYPE_ADJUST(VECTOR4I, Vector4i);
OPCODE_TYPE_ADJUST(PLANE, Plane);
OPCODE_TYPE_ADJUST(QUATERNION, Quaternion);
OPCODE_TYPE_ADJUST(AABB, AABB);
OPCODE_TYPE_ADJUST(BASIS, Basis);
OPCODE_TYPE_ADJUST(TRANSFORM3D, Transform3D);
+ OPCODE_TYPE_ADJUST(PROJECTION, Projection);
OPCODE_TYPE_ADJUST(COLOR, Color);
OPCODE_TYPE_ADJUST(STRING_NAME, StringName);
OPCODE_TYPE_ADJUST(NODE_PATH, NodePath);
@@ -3433,9 +3465,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
}
-#endif
// Get a default return type in case of failure
retvalue = _get_default_variant_for_data_type(return_type);
+#endif
OPCODE_OUT;
}
@@ -3451,26 +3483,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
}
- // Check if this is the last time the function is resuming from await
- // Will be true if never awaited as well
- // When it's the last resume it will postpone the exit from stack,
- // so the debugger knows which function triggered the resume of the next function (if any)
+ // Check if this is not the last time it was interrupted by `await` or if it's the first time executing.
+ // If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed.
+ // This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
if (!p_state || awaited) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->exit_function();
}
#endif
- if (_stack_size) {
- //free stack
- for (int i = 0; i < _stack_size; i++) {
- stack[i].~Variant();
- }
+ // Free stack, except reserved addresses.
+ for (int i = 3; i < _stack_size; i++) {
+ stack[i].~Variant();
}
-
#ifdef DEBUG_ENABLED
}
#endif
+ // Always free reserved addresses, since they are never copied.
+ for (int i = 0; i < 3; i++) {
+ stack[i].~Variant();
+ }
+
return retvalue;
}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index ad96e36640..1cae7bdfac 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -163,6 +163,18 @@ String GDScriptWarning::get_message() const {
#undef CHECK_SYMBOLS
}
+int GDScriptWarning::get_default_value(Code p_code) {
+ if (get_name_from_code(p_code).to_lower().begins_with("unsafe_")) {
+ return WarnLevel::IGNORE;
+ }
+ return WarnLevel::WARN;
+}
+
+PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
+ // Making this a separate function in case a warning needs different PropertyInfo in the future.
+ return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
+}
+
String GDScriptWarning::get_name() const {
return get_name_from_code(code);
}
@@ -210,6 +222,10 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
return names[(int)p_code];
}
+String GDScriptWarning::get_settings_path_from_code(Code p_code) {
+ return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower();
+}
+
GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
for (int i = 0; i < WARNING_MAX; i++) {
if (get_name_from_code((Code)i) == p_name) {
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 82efe3568f..a639e7b44e 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -28,16 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_WARNINGS
-#define GDSCRIPT_WARNINGS
+#ifndef GDSCRIPT_WARNING_H
+#define GDSCRIPT_WARNING_H
#ifdef DEBUG_ENABLED
+#include "core/object/object.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
class GDScriptWarning {
public:
+ enum WarnLevel {
+ IGNORE,
+ WARN,
+ ERROR
+ };
+
enum Code {
UNASSIGNED_VARIABLE, // Variable used but never assigned.
UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc).
@@ -81,10 +88,13 @@ public:
String get_name() const;
String get_message() const;
+ static int get_default_value(Code p_code);
+ static PropertyInfo get_property_info(Code p_code);
static String get_name_from_code(Code p_code);
+ static String get_settings_path_from_code(Code p_code);
static Code get_code_from_name(const String &p_name);
};
#endif // DEBUG_ENABLED
-#endif // GDSCRIPT_WARNINGS
+#endif // GDSCRIPT_WARNING_H
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 4c4e810370..46a9b33eb0 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -89,16 +89,16 @@ void ExtendGDScriptParser::update_symbols() {
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
- members.set(symbol.name, &symbol);
+ members.insert(symbol.name, &symbol);
// cache level one inner classes
if (symbol.kind == lsp::SymbolKind::Class) {
ClassMembers inner_class;
for (int j = 0; j < symbol.children.size(); j++) {
const lsp::DocumentSymbol &s = symbol.children[j];
- inner_class.set(s.name, &s);
+ inner_class.insert(s.name, &s);
}
- inner_classes.set(symbol.name, inner_class);
+ inner_classes.insert(symbol.name, inner_class);
}
}
}
@@ -212,12 +212,12 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
const Variant &default_value = m.constant->initializer->reduced_value;
String value_text;
if (default_value.get_type() == Variant::OBJECT) {
- RES res = default_value;
+ Ref<Resource> res = default_value;
if (res.is_valid() && !res->get_path().is_empty()) {
value_text = "preload(\"" + res->get_path() + "\")";
if (symbol.documentation.is_empty()) {
- if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
- symbol.documentation = S->get()->class_symbol.documentation;
+ if (HashMap<String, ExtendGDScriptParser *>::Iterator S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
+ symbol.documentation = S->value->class_symbol.documentation;
}
}
} else {
@@ -307,6 +307,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
parse_class_symbol(m.m_class, symbol);
r_symbol.children.push_back(symbol);
} break;
+ case ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case ClassNode::Member::UNDEFINED:
break; // Unreachable.
}
@@ -661,30 +663,22 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const
const Array &ExtendGDScriptParser::get_member_completions() {
if (member_completions.is_empty()) {
- const String *name = members.next(nullptr);
- while (name) {
- const lsp::DocumentSymbol *symbol = members.get(*name);
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &E : members) {
+ const lsp::DocumentSymbol *symbol = E.value;
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(path, *name);
+ item.data = JOIN_SYMBOLS(path, E.key);
member_completions.push_back(item.to_json());
-
- name = members.next(name);
}
- const String *_class = inner_classes.next(nullptr);
- while (_class) {
- const ClassMembers *inner_class = inner_classes.getptr(*_class);
- const String *member_name = inner_class->next(nullptr);
- while (member_name) {
- const lsp::DocumentSymbol *symbol = inner_class->get(*member_name);
+ for (const KeyValue<String, ClassMembers> &E : inner_classes) {
+ const ClassMembers *inner_class = &E.value;
+
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &F : *inner_class) {
+ const lsp::DocumentSymbol *symbol = F.value;
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(*_class, *member_name));
+ item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(E.key, F.key));
member_completions.push_back(item.to_json());
-
- member_name = inner_class->next(member_name);
}
-
- _class = inner_classes.next(_class);
}
}
@@ -696,9 +690,7 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
ERR_FAIL_NULL_V(p_func, func);
func["name"] = p_func->identifier->name;
func["return_type"] = p_func->get_datatype().to_string();
- func["rpc_mode"] = p_func->rpc_config.rpc_mode;
- func["rpc_transfer_mode"] = p_func->rpc_config.transfer_mode;
- func["rpc_transfer_channel"] = p_func->rpc_config.channel;
+ func["rpc_config"] = p_func->rpc_config;
Array parameters;
for (int i = 0; i < p_func->parameters.size(); i++) {
Dictionary arg;
@@ -823,6 +815,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
methods.append(dump_function_api(m.function));
}
} break;
+ case ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case ClassNode::Member::UNDEFINED:
break; // Unreachable.
}
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 99b0bf45d0..08bba4a2d4 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -33,7 +33,7 @@
#include "../gdscript_parser.h"
#include "core/variant/variant.h"
-#include "lsp.hpp"
+#include "godot_lsp.h"
#ifndef LINE_NUMBER_TO_INDEX
#define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1)
@@ -99,4 +99,4 @@ public:
Error parse(const String &p_code, const String &p_path);
};
-#endif
+#endif // GDSCRIPT_EXTEND_PARSER_H
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index cdddab319d..7460f8edff 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -126,7 +126,7 @@ Error GDScriptLanguageProtocol::on_client_connected() {
ERR_FAIL_COND_V_MSG(clients.size() >= LSP_MAX_CLIENTS, FAILED, "Max client limits reached");
Ref<LSPeer> peer = memnew(LSPeer);
peer->connection = tcp_peer;
- clients.set(next_client_id, peer);
+ clients.insert(next_client_id, peer);
next_client_id++;
EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
@@ -229,28 +229,33 @@ void GDScriptLanguageProtocol::poll() {
if (server->is_connection_available()) {
on_client_connected();
}
- const int *id = nullptr;
- while ((id = clients.next(id))) {
- Ref<LSPeer> peer = clients.get(*id);
+
+ HashMap<int, Ref<LSPeer>>::Iterator E = clients.begin();
+ while (E != clients.end()) {
+ Ref<LSPeer> peer = E->value;
StreamPeerTCP::Status status = peer->connection->get_status();
if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
} else {
if (peer->connection->get_available_bytes() > 0) {
- latest_client_id = *id;
+ latest_client_id = E->key;
Error err = peer->handle_data();
if (err != OK && err != ERR_BUSY) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
}
}
Error err = peer->send_data();
if (err != OK && err != ERR_BUSY) {
- on_client_disconnected(*id);
- id = nullptr;
+ on_client_disconnected(E->key);
+ E = clients.begin();
+ continue;
}
}
+ ++E;
}
}
@@ -259,9 +264,8 @@ Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
}
void GDScriptLanguageProtocol::stop() {
- const int *id = nullptr;
- while ((id = clients.next(id))) {
- Ref<LSPeer> peer = clients.get(*id);
+ for (const KeyValue<int, Ref<LSPeer>> &E : clients) {
+ Ref<LSPeer> peer = clients.get(E.key);
peer->connection->disconnect_from_host();
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 0fed8597f9..3c9cfe512f 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -36,7 +36,7 @@
#include "core/io/tcp_server.h"
#include "gdscript_text_document.h"
#include "gdscript_workspace.h"
-#include "lsp.hpp"
+#include "godot_lsp.h"
#include "modules/modules_enabled.gen.h" // For jsonrpc.
#ifdef MODULE_JSONRPC_ENABLED
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index c42bd58aeb..5ad9680ea0 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -109,23 +109,15 @@ void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *
void GDScriptTextDocument::initialize() {
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members;
+ for (const KeyValue<StringName, ClassMembers> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members) {
+ const ClassMembers &members = E.value;
- const StringName *class_ptr = native_members.next(nullptr);
- while (class_ptr) {
- const ClassMembers &members = native_members.get(*class_ptr);
-
- const String *name = members.next(nullptr);
- while (name) {
- const lsp::DocumentSymbol *symbol = members.get(*name);
+ for (const KeyValue<String, const lsp::DocumentSymbol *> &F : members) {
+ const lsp::DocumentSymbol *symbol = members.get(F.key);
lsp::CompletionItem item = symbol->make_completion_item();
- item.data = JOIN_SYMBOLS(String(*class_ptr), *name);
+ item.data = JOIN_SYMBOLS(String(E.key), F.key);
native_member_completions.push_back(item.to_json());
-
- name = members.next(name);
}
-
- class_ptr = native_members.next(class_ptr);
}
}
}
@@ -149,9 +141,9 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
String uri = params["uri"];
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri);
Array arr;
- if (const Map<String, ExtendGDScriptParser *>::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
+ if (HashMap<String, ExtendGDScriptParser *>::ConstIterator parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
Vector<lsp::DocumentedSymbolInformation> list;
- parser->get()->get_symbols().symbol_tree_as_list(uri, list);
+ parser->value->get_symbols().symbol_tree_as_list(uri, list);
for (int i = 0; i < list.size(); i++) {
arr.push_back(list[i].to_json());
}
@@ -177,6 +169,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
lsp::CompletionItem item;
item.label = option.display;
item.data = request_data;
+ item.insertText = option.insert_text;
switch (option.kind) {
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
@@ -275,8 +268,8 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
}
if (!symbol) {
- if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
- symbol = E->get()->get_member_symbol(member_name, inner_class_name);
+ if (HashMap<String, ExtendGDScriptParser *>::ConstIterator E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
+ symbol = E->value->get_member_symbol(member_name, inner_class_name);
}
}
}
@@ -286,12 +279,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
item.documentation = symbol->render();
}
- if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
- item.insertText = item.label + "(";
- if (symbol && symbol->children.is_empty()) {
- item.insertText += ")";
- }
- } else if (item.kind == lsp::CompletionItemKind::Event) {
+ if (item.kind == lsp::CompletionItemKind::Event) {
if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
item.insertText = item.label.quote(quote_style);
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 9732765f34..87bc08a34e 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -33,7 +33,7 @@
#include "core/io/file_access.h"
#include "core/object/ref_counted.h"
-#include "lsp.hpp"
+#include "godot_lsp.h"
class GDScriptTextDocument : public RefCounted {
GDCLASS(GDScriptTextDocument, RefCounted)
@@ -77,4 +77,4 @@ public:
GDScriptTextDocument();
};
-#endif
+#endif // GDSCRIPT_TEXT_DOCUMENT_H
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 229c322f26..959651c024 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -116,22 +116,22 @@ void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
}
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
- Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path);
- Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator parser = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator script = scripts.find(p_path);
if (parser && script) {
- if (script->get() && script->get() == parser->get()) {
- memdelete(script->get());
+ if (script->value && script->value == parser->value) {
+ memdelete(script->value);
} else {
- memdelete(script->get());
- memdelete(parser->get());
+ memdelete(script->value);
+ memdelete(parser->value);
}
parse_results.erase(p_path);
scripts.erase(p_path);
} else if (parser) {
- memdelete(parser->get());
+ memdelete(parser->value);
parse_results.erase(p_path);
} else if (script) {
- memdelete(script->get());
+ memdelete(script->value);
scripts.erase(p_path);
}
}
@@ -141,8 +141,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
StringName empty;
while (class_name != empty) {
- if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
- const lsp::DocumentSymbol &class_symbol = E->value();
+ if (HashMap<StringName, lsp::DocumentSymbol>::ConstIterator E = native_symbols.find(class_name)) {
+ const lsp::DocumentSymbol &class_symbol = E->value;
if (p_member.is_empty()) {
return &class_symbol;
@@ -162,9 +162,9 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
}
const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const {
- const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::ConstIterator S = scripts.find(p_path);
if (S) {
- return &(S->get()->get_symbols());
+ return &(S->value->get_symbols());
}
return nullptr;
}
@@ -209,10 +209,10 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
err = parse_script(path, content);
if (err != OK) {
- Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(path);
String err_msg = "Failed parse script " + path;
if (S) {
- err_msg += "\n" + S->get()->get_errors()[0].message;
+ err_msg += "\n" + S->value->get_errors()[0].message;
}
ERR_CONTINUE_MSG(err != OK, err_msg);
}
@@ -238,25 +238,25 @@ void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String>
}
ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String &p_path) {
- const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = scripts.find(p_path);
if (!S) {
parse_local_script(p_path);
S = scripts.find(p_path);
}
if (S) {
- return S->get();
+ return S->value;
}
return nullptr;
}
ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) {
- const Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(p_path);
if (!S) {
parse_local_script(p_path);
S = parse_results.find(p_path);
}
if (S) {
- return S->get();
+ return S->value;
}
return nullptr;
}
@@ -404,9 +404,9 @@ Error GDScriptWorkspace::initialize() {
const lsp::DocumentSymbol &class_symbol = E.value;
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
- members.set(symbol.name, &symbol);
+ members.insert(symbol.name, &symbol);
}
- native_members.set(E.key, members);
+ native_members.insert(E.key, members);
}
// cache member completions
@@ -424,8 +424,8 @@ Error GDScriptWorkspace::initialize() {
Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) {
ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
Error err = parser->parse(p_content, p_path);
- Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path);
- Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator last_parser = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator last_script = scripts.find(p_path);
if (err == OK) {
remove_cache_parser(p_path);
@@ -433,8 +433,8 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
scripts[p_path] = parser;
} else {
- if (last_parser && last_script && last_parser->get() != last_script->get()) {
- memdelete(last_parser->get());
+ if (last_parser && last_script && last_parser->value != last_script->value) {
+ memdelete(last_parser->value);
}
parse_results[p_path] = parser;
}
@@ -499,7 +499,9 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
String path = p_uri;
- path = path.replace(root_uri + "/", "res://");
+ path = path.replace("///", "//");
+ path = path.replace("%3A", ":");
+ path = path.replacen(root_uri + "/", "res://");
path = path.uri_decode();
return path;
}
@@ -513,9 +515,9 @@ String GDScriptWorkspace::get_file_uri(const String &p_path) const {
void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
Dictionary params;
Array errors;
- const Map<String, ExtendGDScriptParser *>::Element *ele = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::ConstIterator ele = parse_results.find(p_path);
if (ele) {
- const Vector<lsp::Diagnostic> &list = ele->get()->get_diagnostics();
+ const Vector<lsp::Diagnostic> &list = ele->value->get_diagnostics();
errors.resize(list.size());
for (int i = 0; i < list.size(); ++i) {
errors[i] = list[i].to_json();
@@ -560,7 +562,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
for (int i = 0; i < owners.size(); i++) {
NodePath owner_path = owners[i];
- RES owner_res = ResourceLoader::load(owner_path);
+ Ref<Resource> owner_res = ResourceLoader::load(owner_path);
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
owner_scene_node = owner_packed_scene->instantiate();
@@ -682,13 +684,11 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
- const StringName *class_ptr = native_members.next(nullptr);
- while (class_ptr) {
- const ClassMembers &members = native_members.get(*class_ptr);
+ for (const KeyValue<StringName, ClassMembers> &E : native_members) {
+ const ClassMembers &members = native_members.get(E.key);
if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
r_list.push_back(*symbol);
}
- class_ptr = native_members.next(class_ptr);
}
for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
@@ -698,23 +698,19 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
r_list.push_back(*symbol);
}
- const HashMap<String, ClassMembers> &inner_classes = script->get_inner_classes();
- const String *_class = inner_classes.next(nullptr);
- while (_class) {
- const ClassMembers *inner_class = inner_classes.getptr(*_class);
+ for (const KeyValue<String, ClassMembers> &F : script->get_inner_classes()) {
+ const ClassMembers *inner_class = &F.value;
if (const lsp::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) {
r_list.push_back(*symbol);
}
-
- _class = inner_classes.next(_class);
}
}
}
}
const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
- if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
- const lsp::DocumentSymbol &symbol = E->get();
+ if (HashMap<StringName, lsp::DocumentSymbol>::Iterator E = native_symbols.find(p_params.native_class)) {
+ const lsp::DocumentSymbol &symbol = E->value;
if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {
return &symbol;
}
@@ -790,7 +786,7 @@ GDScriptWorkspace::GDScriptWorkspace() {
}
GDScriptWorkspace::~GDScriptWorkspace() {
- Set<String> cached_parsers;
+ HashSet<String> cached_parsers;
for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) {
cached_parsers.insert(E.key);
@@ -800,7 +796,7 @@ GDScriptWorkspace::~GDScriptWorkspace() {
cached_parsers.insert(E.key);
}
- for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) {
- remove_cache_parser(E->get());
+ for (const String &E : cached_parsers) {
+ remove_cache_parser(E);
}
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 92e78f8992..88f3aaf957 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -35,7 +35,7 @@
#include "core/variant/variant.h"
#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
-#include "lsp.hpp"
+#include "godot_lsp.h"
class GDScriptWorkspace : public RefCounted {
GDCLASS(GDScriptWorkspace, RefCounted);
@@ -48,7 +48,7 @@ protected:
static void _bind_methods();
void remove_cache_parser(const String &p_path);
bool initialized = false;
- Map<StringName, lsp::DocumentSymbol> native_symbols;
+ HashMap<StringName, lsp::DocumentSymbol> native_symbols;
const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const;
const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const;
@@ -68,8 +68,8 @@ public:
String root;
String root_uri;
- Map<String, ExtendGDScriptParser *> scripts;
- Map<String, ExtendGDScriptParser *> parse_results;
+ HashMap<String, ExtendGDScriptParser *> scripts;
+ HashMap<String, ExtendGDScriptParser *> parse_results;
HashMap<StringName, ClassMembers> native_members;
public:
@@ -100,4 +100,4 @@ public:
~GDScriptWorkspace();
};
-#endif
+#endif // GDSCRIPT_WORKSPACE_H
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/godot_lsp.h
index a63f9df918..fbd40796c4 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/godot_lsp.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* lsp.hpp */
+/* godot_lsp.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -261,7 +261,7 @@ struct WorkspaceEdit {
/**
* Holds changes to existing resources.
*/
- Map<String, Vector<TextEdit>> changes;
+ HashMap<String, Vector<TextEdit>> changes;
_FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) {
if (changes.has(uri)) {
@@ -293,8 +293,8 @@ struct WorkspaceEdit {
}
_FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) {
- if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
- Vector<TextEdit> edit_list = E->value();
+ if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) {
+ Vector<TextEdit> edit_list = E->value;
for (int i = 0; i < edit_list.size(); ++i) {
TextEdit edit = edit_list[i];
if (edit.range.start.character == start_character) {
@@ -310,8 +310,8 @@ struct WorkspaceEdit {
new_edit.range.end.line = line;
new_edit.range.end.character = end_character;
- if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
- E->value().push_back(new_edit);
+ if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) {
+ E->value.push_back(new_edit);
} else {
Vector<TextEdit> edit_list;
edit_list.push_back(new_edit);
@@ -1004,8 +1004,8 @@ struct CompletionItem {
dict["label"] = label;
dict["kind"] = kind;
dict["data"] = data;
+ dict["insertText"] = insertText;
if (resolved) {
- dict["insertText"] = insertText;
dict["detail"] = detail;
dict["documentation"] = documentation.to_json();
dict["deprecated"] = deprecated;
@@ -1975,4 +1975,4 @@ static String marked_documentation(const String &p_bbcode) {
}
} // namespace lsp
-#endif
+#endif // GODOT_LSP_H
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index fcf122f567..059ca703ab 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -52,10 +52,10 @@ GDScriptCache *gdscript_cache = nullptr;
#ifdef TOOLS_ENABLED
-#include "editor/editor_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_translation_parser.h"
+#include "editor/export/editor_export.h"
#include "editor/gdscript_highlighter.h"
#include "editor/gdscript_translation_parser_plugin.h"
@@ -70,7 +70,7 @@ class EditorExportGDScript : public EditorExportPlugin {
GDCLASS(EditorExportGDScript, EditorExportPlugin);
public:
- virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) override {
+ virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override {
int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED;
String script_key;
@@ -111,54 +111,62 @@ static void _editor_init() {
#endif // TOOLS_ENABLED
-void register_gdscript_types() {
- GDREGISTER_CLASS(GDScript);
+void initialize_gdscript_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ GDREGISTER_CLASS(GDScript);
- script_language_gd = memnew(GDScriptLanguage);
- ScriptServer::register_language(script_language_gd);
+ script_language_gd = memnew(GDScriptLanguage);
+ ScriptServer::register_language(script_language_gd);
- resource_loader_gd.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_gd);
+ resource_loader_gd.instantiate();
+ ResourceLoader::add_resource_format_loader(resource_loader_gd);
- resource_saver_gd.instantiate();
- ResourceSaver::add_resource_format_saver(resource_saver_gd);
+ resource_saver_gd.instantiate();
+ ResourceSaver::add_resource_format_saver(resource_saver_gd);
- gdscript_cache = memnew(GDScriptCache);
+ gdscript_cache = memnew(GDScriptCache);
+
+ GDScriptUtilityFunctions::register_functions();
+ }
#ifdef TOOLS_ENABLED
- EditorNode::add_init_callback(_editor_init);
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ EditorNode::add_init_callback(_editor_init);
- gdscript_translation_parser_plugin.instantiate();
- EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
+ gdscript_translation_parser_plugin.instantiate();
+ EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
+ }
#endif // TOOLS_ENABLED
-
- GDScriptUtilityFunctions::register_functions();
}
-void unregister_gdscript_types() {
- ScriptServer::unregister_language(script_language_gd);
+void uninitialize_gdscript_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ ScriptServer::unregister_language(script_language_gd);
- if (gdscript_cache) {
- memdelete(gdscript_cache);
- }
+ if (gdscript_cache) {
+ memdelete(gdscript_cache);
+ }
- if (script_language_gd) {
- memdelete(script_language_gd);
- }
+ if (script_language_gd) {
+ memdelete(script_language_gd);
+ }
+
+ ResourceLoader::remove_resource_format_loader(resource_loader_gd);
+ resource_loader_gd.unref();
- ResourceLoader::remove_resource_format_loader(resource_loader_gd);
- resource_loader_gd.unref();
+ ResourceSaver::remove_resource_format_saver(resource_saver_gd);
+ resource_saver_gd.unref();
- ResourceSaver::remove_resource_format_saver(resource_saver_gd);
- resource_saver_gd.unref();
+ GDScriptParser::cleanup();
+ GDScriptUtilityFunctions::unregister_functions();
+ }
#ifdef TOOLS_ENABLED
- EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
- gdscript_translation_parser_plugin.unref();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorTranslationParser::get_singleton()->remove_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
+ gdscript_translation_parser_plugin.unref();
+ }
#endif // TOOLS_ENABLED
-
- GDScriptParser::cleanup();
- GDScriptUtilityFunctions::unregister_functions();
}
#ifdef TESTS_ENABLED
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
index baa7dcbbd1..a7e6b02dcf 100644
--- a/modules/gdscript/register_types.h
+++ b/modules/gdscript/register_types.h
@@ -31,7 +31,9 @@
#ifndef GDSCRIPT_REGISTER_TYPES_H
#define GDSCRIPT_REGISTER_TYPES_H
-void register_gdscript_types();
-void unregister_gdscript_types();
+#include "modules/register_module_types.h"
+
+void initialize_gdscript_module(ModuleInitializationLevel p_level);
+void uninitialize_gdscript_module(ModuleInitializationLevel p_level);
#endif // GDSCRIPT_REGISTER_TYPES_H
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index e78517a708..ff4832bde0 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -48,11 +48,11 @@
namespace GDScriptTests {
void init_autoloads() {
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
// First pass, add the constants so they exist before any script is loaded.
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (info.is_singleton) {
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@@ -62,15 +62,15 @@ void init_autoloads() {
}
// Second pass, load into global constants.
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.get();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
if (!info.is_singleton) {
// Skip non-singletons since we don't have a scene tree here anyway.
continue;
}
- RES res = ResourceLoader::load(info.path);
+ Ref<Resource> res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
Node *n = nullptr;
Ref<PackedScene> scn = res;
@@ -363,7 +363,7 @@ void GDScriptTest::disable_stdout() {
OS::get_singleton()->set_stderr_enabled(false);
}
-void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error) {
+void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich) {
TestResult *result = (TestResult *)p_this;
result->output += p_message + "\n";
}
@@ -543,8 +543,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
return result;
}
// Test running.
- const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
- if (test_function_element == nullptr) {
+ const HashMap<StringName, GDScriptFunction *>::ConstIterator test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
+ if (!test_function_element) {
enable_stdout();
result.status = GDTEST_LOAD_ERROR;
result.output = "";
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
index d6c6419e21..033d2fcad1 100644
--- a/modules/gdscript/tests/gdscript_test_runner.h
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_TEST_H
-#define GDSCRIPT_TEST_H
+#ifndef GDSCRIPT_TEST_RUNNER_H
+#define GDSCRIPT_TEST_RUNNER_H
#include "../gdscript.h"
#include "core/error/error_macros.h"
@@ -86,7 +86,7 @@ private:
TestResult execute_test_code(bool p_is_generating);
public:
- static void print_handler(void *p_this, const String &p_message, bool p_error);
+ static void print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich);
static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type);
TestResult run_test();
bool generate_output();
@@ -123,4 +123,4 @@ public:
} // namespace GDScriptTests
-#endif // GDSCRIPT_TEST_H
+#endif // GDSCRIPT_TEST_RUNNER_H
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd
new file mode 100644
index 0000000000..9a7c6a8250
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd
@@ -0,0 +1,12 @@
+# https://github.com/godotengine/godot/issues/54589
+# https://github.com/godotengine/godot/issues/56265
+
+extends Resource
+
+func test():
+ print("okay")
+ await self.changed
+ await unknown(self)
+
+func unknown(arg):
+ await arg.changed
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out
new file mode 100644
index 0000000000..2dc04a363e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+okay
diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd
new file mode 100644
index 0000000000..631e7be5ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.gd
@@ -0,0 +1,14 @@
+func test():
+ var instance := Parent.new()
+ instance.my_function({"a": 1})
+ instance = Child.new()
+ instance.my_function({"a": 1})
+ print("No failure")
+
+class Parent:
+ func my_function(_par1: Dictionary = {}) -> void:
+ pass
+
+class Child extends Parent:
+ func my_function(_par1: Dictionary = {}) -> void:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out
new file mode 100644
index 0000000000..67f0045867
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/function_match_parent_signature_with_default_dict_void.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+No failure
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd
new file mode 100644
index 0000000000..9f86d0531c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd
@@ -0,0 +1,2 @@
+func test():
+ const arr: Array[int] = ["Hello", "World"]
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
new file mode 100644
index 0000000000..26b6e13d4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int].
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
index d13d713454..ada6030132 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
@@ -1,6 +1,6 @@
-# Error here. `class_name` should be used *before* annotations, not after.
+# Error here. `class_name` should be used *before* annotations, not after (except @tool).
@icon("res://path/to/optional/icon.svg")
class_name HelloWorld
func test():
- pass
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
index 0bcc8acc55..02b33c8692 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"class_name" should be used before annotations.
+"class_name" should be used before annotations (except @tool).
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd
new file mode 100644
index 0000000000..fa0a43094e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd
@@ -0,0 +1,3 @@
+func test():
+ func standalone():
+ print("can't be accessed")
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out
new file mode 100644
index 0000000000..c6830c8258
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Standalone lambdas cannot be accessed. Consider assigning it to a variable.
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
new file mode 100644
index 0000000000..4608c778aa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd
@@ -0,0 +1,4 @@
+func test():
+ match 1:
+ [[[var a]]], 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out
new file mode 100644
index 0000000000..1cdc24683b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Cannot use a variable bind with multiple patterns.
diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd
new file mode 100644
index 0000000000..409da11051
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd
@@ -0,0 +1,4 @@
+func test():
+ var TEST = 1
+ for TEST in 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out
new file mode 100644
index 0000000000..407f094ca0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a variable named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd
new file mode 100644
index 0000000000..b353fd1288
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd
@@ -0,0 +1,3 @@
+func test():
+ var TEST = 1
+ var TEST = 2
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out
new file mode 100644
index 0000000000..407f094ca0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a variable named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
index dcb4ccecb0..3062f0be70 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path after "/".
+Expected node path as string or identifier after "/".
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out
index 67c7e28046..3cdafb04a9 100644
--- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out
@@ -10,5 +10,5 @@ wildcard
[1,2,[1,{1:2,2:var z,..}]]
3
[1,2,[1,{1:2,2:var z,..}]]
-[1, 3, 5, 123]
+[1, 3, 5, "123"]
wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out
index 54083c1afc..5f999f573a 100644
--- a/modules/gdscript/tests/scripts/parser/features/dictionary.out
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary.out
@@ -7,8 +7,8 @@ null
false
empty array
zero Vector2i
-{22:{4:[nesting, arrays]}}
-{4:[nesting, arrays]}
-[nesting, arrays]
+{22:{4:["nesting", "arrays"]}}
+{4:["nesting", "arrays"]}
+["nesting", "arrays"]
nesting
arrays
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
index 5b0ea9df43..5143d040a9 100644
--- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
@@ -1,2 +1,2 @@
GDTEST_OK
-{a:1, b:2, with spaces:3, 2:4}
+{"a":1, "b":2, "with spaces":3, "2":4}
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out
index 62be807a1f..dd28609850 100644
--- a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out
@@ -1,2 +1,2 @@
GDTEST_OK
-{hello:{world:{is:beautiful}}}
+{"hello":{"world":{"is":"beautiful"}}}
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
new file mode 100644
index 0000000000..f04f4de08d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
@@ -0,0 +1,49 @@
+extends Node
+
+func test():
+ var child = Node.new()
+ child.name = "Child"
+ add_child(child)
+ child.owner = self
+
+ var hey = Node.new()
+ hey.name = "Hey"
+ child.add_child(hey)
+ hey.owner = self
+ hey.unique_name_in_owner = true
+
+ var fake_hey = Node.new()
+ fake_hey.name = "Hey"
+ add_child(fake_hey)
+ fake_hey.owner = self
+
+ var sub_child = Node.new()
+ sub_child.name = "SubChild"
+ hey.add_child(sub_child)
+ sub_child.owner = self
+
+ var howdy = Node.new()
+ howdy.name = "Howdy"
+ sub_child.add_child(howdy)
+ howdy.owner = self
+ howdy.unique_name_in_owner = true
+
+ print(hey == $Child/Hey)
+ print(howdy == $Child/Hey/SubChild/Howdy)
+
+ print(%Hey == hey)
+ print($%Hey == hey)
+ print(%"Hey" == hey)
+ print($"%Hey" == hey)
+ print($%"Hey" == hey)
+ print(%Hey/%Howdy == howdy)
+ print($%Hey/%Howdy == howdy)
+ print($"%Hey/%Howdy" == howdy)
+ print($"%Hey"/"%Howdy" == howdy)
+ print(%"Hey"/"%Howdy" == howdy)
+ print($%"Hey"/"%Howdy" == howdy)
+ print($"%Hey"/%"Howdy" == howdy)
+ print(%"Hey"/%"Howdy" == howdy)
+ print($%"Hey"/%"Howdy" == howdy)
+ print(%"Hey/%Howdy" == howdy)
+ print($%"Hey/%Howdy" == howdy)
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out
new file mode 100644
index 0000000000..041c4439b0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out
@@ -0,0 +1,19 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd
new file mode 100644
index 0000000000..f5e26ab1ab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd
@@ -0,0 +1,7 @@
+# https://github.com/godotengine/godot/issues/61231
+
+func test():
+ var my_lambda = func():
+ print("hello")
+ if 0 == 0:
+ my_lambda.call()
diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out
new file mode 100644
index 0000000000..58774d2d3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+hello
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd
new file mode 100644
index 0000000000..2140b6923e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd
@@ -0,0 +1,7 @@
+# https://github.com/godotengine/godot/issues/56751
+
+func test():
+ var x = "local"
+ var lambda = func(param = x):
+ print(param)
+ lambda.call()
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out
new file mode 100644
index 0000000000..ce3241b94d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+local
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
new file mode 100644
index 0000000000..707e4532cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
@@ -0,0 +1,13 @@
+# https://github.com/godotengine/godot/pull/61666
+
+func test():
+ var dict := {"key": "value"}
+ match dict:
+ {"key": var value}:
+ print(value) # used, no warning
+ match dict:
+ {"key": var value}:
+ pass # unused, warning
+ match dict:
+ {"key": var _value}:
+ pass # unused, suppressed warning from underscore
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
new file mode 100644
index 0000000000..057c1b11e5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 9
+>> UNUSED_VARIABLE
+>> The local variable 'value' is declared but never used in the block. If this is intended, prefix it with an underscore: '_value'
+value
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
new file mode 100644
index 0000000000..377dd25e9e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -0,0 +1,43 @@
+func foo(x):
+ match x:
+ {"key1": "value1", "key2": "value2"}:
+ print('{"key1": "value1", "key2": "value2"}')
+ {"key1": "value1", "key2"}:
+ print('{"key1": "value1", "key2"}')
+ {"key1", "key2": "value2"}:
+ print('{"key1", "key2": "value2"}')
+ {"key1", "key2"}:
+ print('{"key1", "key2"}')
+ {"key1": "value1"}:
+ print('{"key1": "value1"}')
+ {"key1"}:
+ print('{"key1"}')
+ _:
+ print("wildcard")
+
+func bar(x):
+ match x:
+ {0}:
+ print("0")
+ {1}:
+ print("1")
+ {2}:
+ print("2")
+ _:
+ print("wildcard")
+
+func test():
+ foo({"key1": "value1", "key2": "value2"})
+ foo({"key1": "value1", "key2": ""})
+ foo({"key1": "", "key2": "value2"})
+ foo({"key1": "", "key2": ""})
+ foo({"key1": "value1"})
+ foo({"key1": ""})
+ foo({"key1": "value1", "key2": "value2", "key3": "value3"})
+ foo({"key1": "value1", "key3": ""})
+ foo({"key2": "value2"})
+ foo({"key3": ""})
+ bar({0: "0"})
+ bar({1: "1"})
+ bar({2: "2"})
+ bar({3: "3"})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
new file mode 100644
index 0000000000..4dee886927
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+{"key1": "value1", "key2": "value2"}
+{"key1": "value1", "key2"}
+{"key1", "key2": "value2"}
+{"key1", "key2"}
+{"key1": "value1"}
+{"key1"}
+wildcard
+wildcard
+wildcard
+wildcard
+0
+1
+2
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
new file mode 100644
index 0000000000..dbe223f5f5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
@@ -0,0 +1,26 @@
+func foo(x):
+ match x:
+ 1, [2]:
+ print('1, [2]')
+ _:
+ print('wildcard')
+
+func bar(x):
+ match x:
+ [1], [2], [3]:
+ print('[1], [2], [3]')
+ [4]:
+ print('[4]')
+ _:
+ print('wildcard')
+
+func test():
+ foo(1)
+ foo([2])
+ foo(2)
+ bar([1])
+ bar([2])
+ bar([3])
+ bar([4])
+ bar([5])
+
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
new file mode 100644
index 0000000000..a12b934d67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+1, [2]
+1, [2]
+wildcard
+[1], [2], [3]
+[1], [2], [3]
+[1], [2], [3]
+[4]
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
new file mode 100644
index 0000000000..a0ae7fb17c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
@@ -0,0 +1,6 @@
+func test():
+ match [1, 2, 3]:
+ [var a, var b, var c]:
+ print(a == 1)
+ print(b == 2)
+ print(c == 3)
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out
new file mode 100644
index 0000000000..316db6d748
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
index 4009160439..8b8c33202f 100644
--- a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
+++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
@@ -1,5 +1,5 @@
GDTEST_OK
-{8:{key:value}}
-{key:value}
+{8:{"key":"value"}}
+{"key":"value"}
value
value
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd
new file mode 100644
index 0000000000..d00d483a73
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd
@@ -0,0 +1,16 @@
+func test():
+ var foo := "bar"
+ match foo:
+ "baz":
+ return
+ _:
+ pass
+ match foo:
+ "baz":
+ return
+ match foo:
+ "bar":
+ pass
+ _:
+ return
+ print("reached")
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out
new file mode 100644
index 0000000000..47db6b631b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+reached
diff --git a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd
new file mode 100644
index 0000000000..d2f3a3e18f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd
@@ -0,0 +1,19 @@
+func test():
+ var dictionary1: Variant = {1:Vector2()}
+ dictionary1[1].x = 2
+ var dictionary2: Dictionary = {3:Vector2()}
+ dictionary2[3].x = 4
+ var array1: Variant = [[Vector2()]]
+ array1[0][0].x = 5
+ var array2: Array = [[Vector2()]]
+ array2[0][0].x = 6
+ var array3: Array[Array] = [[Vector2()]]
+ array3[0][0].x = 7
+ var transform = Transform3D()
+ transform.basis.x = Vector3(8.0, 9.0, 7.0)
+ print(dictionary1)
+ print(dictionary2)
+ print(array1)
+ print(array2)
+ print(array3)
+ print(transform)
diff --git a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
new file mode 100644
index 0000000000..5e7ccf534a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+{1:(2, 0)}
+{3:(4, 0)}
+[[(5, 0)]]
+[[(6, 0)]]
+[[(7, 0)]]
+[X: (8, 9, 7), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)]
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out
index 7670fc0128..d4468737a5 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.out
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out
@@ -21,14 +21,14 @@ hello/world
RID(0)
Node::get_name
Node::[signal]property_list_changed
-{hello:123}
-[hello, 123]
+{"hello":123}
+["hello", 123]
[255, 0, 1]
[-1, 0, 1]
[-1, 0, 1]
[-1, 0, 1]
[-1, 0, 1]
-[hello, world]
+["hello", "world"]
[(1, 1), (0, 0)]
[(1, 1, 1), (0, 0, 0)]
[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)]
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index d8f60d5e9b..cbcd7b2955 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -134,6 +134,34 @@ static void test_parser(const String &p_code, const String &p_script_path, const
#endif
}
+static void recursively_disassemble_functions(const Ref<GDScript> script, const Vector<String> &p_lines) {
+ for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
+ 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 + ")");
+#ifdef TOOLS_ENABLED
+ func->disassemble(p_lines);
+#endif
+ print_line("");
+ print_line("");
+ }
+
+ for (const KeyValue<StringName, Ref<GDScript>> &F : script->get_subclasses()) {
+ const Ref<GDScript> inner_script = F.value;
+ print_line("");
+ print_line(vformat("Inner Class: %s", inner_script->get_script_class_name()));
+ print_line("");
+ recursively_disassemble_functions(inner_script, p_lines);
+ }
+}
+
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);
@@ -172,23 +200,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
return;
}
- for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
- 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 + ")");
-#ifdef TOOLS_ENABLED
- func->disassemble(p_lines);
-#endif
- print_line("");
- print_line("");
- }
+ recursively_disassemble_functions(script, p_lines);
}
void test(TestType p_type) {
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index 8e69ba78c7..b1c2140039 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -38,7 +38,8 @@
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
-static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) {
+static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device) {
+ const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
Vector<uint8_t> ret;
ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret);
@@ -58,12 +59,12 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
glslang::TShader::ForbidIncluder includer;
- if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
- if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) {
+ if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
+ if (capabilities->version_major == 1 && capabilities->version_minor == 0) {
ClientVersion = glslang::EShTargetVulkan_1_0;
TargetVersion = glslang::EShTargetSpv_1_0;
check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0
- } else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) {
+ } else if (capabilities->version_major == 1 && capabilities->version_minor == 1) {
ClientVersion = glslang::EShTargetVulkan_1_1;
TargetVersion = glslang::EShTargetSpv_1_3;
} else {
@@ -90,34 +91,36 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
if (check_subgroup_support) {
uint32_t stage_bit = 1 << p_stage;
- if ((p_capabilities->subgroup_in_shaders & stage_bit) == stage_bit) {
+ uint32_t subgroup_in_shaders = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
+ uint32_t subgroup_operations = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS));
+ if ((subgroup_in_shaders & stage_bit) == stage_bit) {
// stage supports subgroups
preamble += "#define has_GL_KHR_shader_subgroup_basic 1\n";
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_vote 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_arithmetic 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_ballot 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_shuffle 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_shuffle_relative 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_clustered 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_quad 1\n";
}
}
}
- if (p_capabilities->supports_multiview) {
+ if (p_render_device->has_feature(RD::SUPPORTS_MULTIVIEW)) {
preamble += "#define has_VK_KHR_multiview 1\n";
}
@@ -184,13 +187,18 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
return ret;
}
-static String _get_cache_key_function_glsl(const RenderingDevice::Capabilities *p_capabilities) {
+static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) {
+ const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
String version;
- version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(p_capabilities->version_major) + ", minor=" + itos(p_capabilities->version_minor) + " , subgroup_size=" + itos(p_capabilities->subgroup_operations) + " , subgroup_ops=" + itos(p_capabilities->subgroup_operations) + " , subgroup_in_shaders=" + itos(p_capabilities->subgroup_in_shaders);
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
return version;
}
-void preregister_glslang_types() {
+void initialize_glslang_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_CORE) {
+ return;
+ }
+
// Initialize in case it's not initialized. This is done once per thread
// and it's safe to call multiple times.
glslang::InitializeProcess();
@@ -198,9 +206,10 @@ void preregister_glslang_types() {
RenderingDevice::shader_set_get_cache_key_function(_get_cache_key_function_glsl);
}
-void register_glslang_types() {
-}
+void uninitialize_glslang_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_CORE) {
+ return;
+ }
-void unregister_glslang_types() {
glslang::FinalizeProcess();
}
diff --git a/modules/glslang/register_types.h b/modules/glslang/register_types.h
index 9d8dc9dc2a..d9611cc02f 100644
--- a/modules/glslang/register_types.h
+++ b/modules/glslang/register_types.h
@@ -33,8 +33,9 @@
#define MODULE_GLSLANG_HAS_PREREGISTER
-void preregister_glslang_types();
-void register_glslang_types();
-void unregister_glslang_types();
+#include "modules/register_module_types.h"
+
+void initialize_glslang_module(ModuleInitializationLevel p_level);
+void uninitialize_glslang_module(ModuleInitializationLevel p_level);
#endif // GLSLANG_REGISTER_TYPES_H
diff --git a/modules/gltf/README.md b/modules/gltf/README.md
new file mode 100644
index 0000000000..5d8966b201
--- /dev/null
+++ b/modules/gltf/README.md
@@ -0,0 +1,11 @@
+# Godot GLTF import and export module
+
+In a nutshell, the GLTF module works like this:
+
+* The [`structures/`](structures/) folder contains GLTF structures, the
+ small pieces that make up a GLTF file, represented as C++ classes.
+* The [`extensions/`](extensions/) folder contains GLTF extensions, which
+ are optional features that build on top of the base GLTF spec.
+* [`GLTFState`](gltf_state.h) holds collections of structures and extensions.
+* [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements.
+* The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models.
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
index 3379404a00..6634d5df7b 100644
--- a/modules/gltf/SCsub
+++ b/modules/gltf/SCsub
@@ -7,5 +7,7 @@ env_gltf = env_modules.Clone()
# Godot's own source files
env_gltf.add_source_files(env.modules_sources, "*.cpp")
+env_gltf.add_source_files(env.modules_sources, "extensions/*.cpp")
+env_gltf.add_source_files(env.modules_sources, "structures/*.cpp")
if env["tools"]:
env_gltf.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 03b4380ff4..3c28546ad7 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -7,65 +7,52 @@
<tutorials>
</tutorials>
<methods>
- <method name="export_post">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
+ <method name="_export_node" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="gltf_node" type="GLTFNode" />
+ <argument index="2" name="json" type="Dictionary" />
+ <argument index="3" name="node" type="Node" />
<description>
</description>
</method>
- <method name="export_preflight">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <argument index="1" name="node" type="Node" />
+ <method name="_export_post" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
- <method name="get_export_setting" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="key" type="StringName" />
+ <method name="_export_preflight" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="root" type="Node" />
<description>
</description>
</method>
- <method name="get_export_setting_keys" qualifiers="const">
- <return type="Array" />
+ <method name="_import_node" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="gltf_node" type="GLTFNode" />
+ <argument index="2" name="json" type="Dictionary" />
+ <argument index="3" name="node" type="Node" />
<description>
</description>
</method>
- <method name="get_import_setting" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="key" type="StringName" />
+ <method name="_import_post" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="root" type="Node" />
<description>
</description>
</method>
- <method name="get_import_setting_keys" qualifiers="const">
- <return type="Array" />
+ <method name="_import_post_parse" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
- <method name="import_post">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <argument index="1" name="node" type="Node" />
- <description>
- </description>
- </method>
- <method name="import_preflight">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <description>
- </description>
- </method>
- <method name="set_export_setting">
- <return type="void" />
- <argument index="0" name="key" type="StringName" />
- <argument index="1" name="value" type="Variant" />
- <description>
- </description>
- </method>
- <method name="set_import_setting">
- <return type="void" />
- <argument index="0" name="key" type="StringName" />
- <argument index="1" name="value" type="Variant" />
+ <method name="_import_preflight" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index a59fa900bc..44a1723563 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -192,6 +192,8 @@
</method>
</methods>
<members>
+ <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
+ </member>
<member name="buffers" type="Array" setter="set_buffers" getter="get_buffers" default="[]">
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 173d5131cf..8002c185c7 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -58,7 +58,7 @@ void EditorSceneFormatImporterBlend::get_extensions(List<String> *r_extensions)
}
Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
@@ -239,7 +239,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
}
Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) {
+ const HashMap<StringName, Variant> &p_options) {
if (p_path.get_extension().to_lower() != "blend") {
return true;
}
@@ -292,7 +292,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
path = path.plus_file("blender");
#endif
-#if defined(OSX_ENABLED)
+#if defined(MACOS_ENABLED)
if (!FileAccess::exists(path)) {
path = path.plus_file("Blender");
}
@@ -310,7 +310,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
Error err = OS::get_singleton()->execute(path, args, &pipe);
if (err != OK) {
if (r_err) {
- *r_err = TTR("Can't excecute Blender binary.");
+ *r_err = TTR("Can't execute Blender binary.");
}
return false;
}
@@ -449,8 +449,8 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
EditorNode::get_singleton()->get_gui_base()->add_child(configure_blender_dialog);
- configure_blender_dialog->get_ok_button()->set_text(TTR("Confirm Path"));
- configure_blender_dialog->get_cancel_button()->set_text(TTR("Disable '.blend' Import"));
+ configure_blender_dialog->set_ok_button_text(TTR("Confirm Path"));
+ configure_blender_dialog->set_cancel_button_text(TTR("Disable '.blend' Import"));
configure_blender_dialog->get_cancel_button()->set_tooltip(TTR("Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings."));
configure_blender_dialog->connect("confirmed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed));
@@ -468,7 +468,7 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
// Autodetect
auto_detected_path = "";
-#if defined(OSX_ENABLED)
+#if defined(MACOS_ENABLED)
{
Vector<String> mdfind_paths;
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
index 0925333a28..dd1c1b9889 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.h
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -66,12 +66,12 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override;
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) override;
+ const HashMap<StringName, Variant> &p_options) override;
};
class LineEdit;
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp
index 893d2efcec..faad2d315d 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.cpp
+++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp
@@ -30,7 +30,7 @@
#include "editor_scene_importer_fbx.h"
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
#include "../gltf_document.h"
#include "../gltf_state.h"
@@ -49,7 +49,7 @@ void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) co
}
Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
@@ -106,7 +106,7 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
}
Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,
- const String &p_option, const Map<StringName, Variant> &p_options) {
+ const String &p_option, const HashMap<StringName, Variant> &p_options) {
return true;
}
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.h b/modules/gltf/editor/editor_scene_importer_fbx.h
index 84de7fd1cc..b0039b1c8f 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.h
+++ b/modules/gltf/editor/editor_scene_importer_fbx.h
@@ -45,12 +45,12 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override;
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) override;
+ const HashMap<StringName, Variant> &p_options) override;
};
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index 5e7811ad2b..3fadec5167 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -35,7 +35,6 @@
#include "../gltf_document.h"
#include "../gltf_state.h"
-#include "scene/main/node.h"
#include "scene/resources/animation.h"
uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
@@ -48,7 +47,7 @@ void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) c
}
Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
Ref<GLTFDocument> doc;
doc.instantiate();
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h
index b714ada124..b17a1e4eaa 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.h
+++ b/modules/gltf/editor/editor_scene_importer_gltf.h
@@ -33,6 +33,9 @@
#ifdef TOOLS_ENABLED
+#include "../gltf_document_extension.h"
+#include "../gltf_state.h"
+
#include "editor/import/resource_importer_scene.h"
class Animation;
@@ -45,7 +48,7 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
};
diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
index af21a4e804..af21a4e804 100644
--- a/modules/gltf/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
diff --git a/modules/gltf/gltf_light.h b/modules/gltf/extensions/gltf_light.h
index 25e0835a33..58fa299dfd 100644
--- a/modules/gltf/gltf_light.h
+++ b/modules/gltf/extensions/gltf_light.h
@@ -33,6 +33,7 @@
#include "core/config/engine.h"
#include "core/io/resource.h"
+#include "scene/3d/light_3d.h"
class GLTFLight : public Resource {
GDCLASS(GLTFLight, Resource)
diff --git a/modules/gltf/gltf_spec_gloss.cpp b/modules/gltf/extensions/gltf_spec_gloss.cpp
index 83af91bfcc..83af91bfcc 100644
--- a/modules/gltf/gltf_spec_gloss.cpp
+++ b/modules/gltf/extensions/gltf_spec_gloss.cpp
diff --git a/modules/gltf/gltf_spec_gloss.h b/modules/gltf/extensions/gltf_spec_gloss.h
index f8a431bdce..a45fa4296c 100644
--- a/modules/gltf/gltf_spec_gloss.h
+++ b/modules/gltf/extensions/gltf_spec_gloss.h
@@ -64,4 +64,5 @@ public:
Ref<Image> get_spec_gloss_img();
void set_spec_gloss_img(Ref<Image> p_spec_gloss_img);
};
+
#endif // GLTF_SPEC_GLOSS_H
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
new file mode 100644
index 0000000000..c20c87f798
--- /dev/null
+++ b/modules/gltf/gltf_defines.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* gltf_defines.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GLTF_DEFINES_H
+#define GLTF_DEFINES_H
+
+// This file should only be included by other headers.
+
+// Godot classes used by GLTF headers.
+class BoneAttachment3D;
+class CSGShape3D;
+class DirectionalLight3D;
+class GridMap;
+class Light3D;
+class MultiMeshInstance3D;
+class Skeleton3D;
+class Skin;
+
+// GLTF classes.
+struct GLTFAccessor;
+class GLTFAnimation;
+class GLTFBufferView;
+class GLTFCamera;
+class GLTFDocument;
+class GLTFDocumentExtension;
+class GLTFLight;
+class GLTFMesh;
+class GLTFNode;
+class GLTFSkeleton;
+class GLTFSkin;
+class GLTFSpecGloss;
+class GLTFState;
+class GLTFTexture;
+
+// GLTF index aliases.
+using GLTFAccessorIndex = int;
+using GLTFAnimationIndex = int;
+using GLTFBufferIndex = int;
+using GLTFBufferViewIndex = int;
+using GLTFCameraIndex = int;
+using GLTFImageIndex = int;
+using GLTFMaterialIndex = int;
+using GLTFMeshIndex = int;
+using GLTFLightIndex = int;
+using GLTFNodeIndex = int;
+using GLTFSkeletonIndex = int;
+using GLTFSkinIndex = int;
+using GLTFTextureIndex = int;
+
+enum GLTFType {
+ TYPE_SCALAR,
+ TYPE_VEC2,
+ TYPE_VEC3,
+ TYPE_VEC4,
+ TYPE_MAT2,
+ TYPE_MAT3,
+ TYPE_MAT4,
+};
+
+#endif // GLTF_DEFINES_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 281f62c4ad..4ca8482ba3 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -30,19 +30,10 @@
#include "gltf_document.h"
-#include "gltf_accessor.h"
-#include "gltf_animation.h"
-#include "gltf_camera.h"
+#include "extensions/gltf_spec_gloss.h"
#include "gltf_document_extension.h"
#include "gltf_document_extension_convert_importer_mesh.h"
-#include "gltf_light.h"
-#include "gltf_mesh.h"
-#include "gltf_node.h"
-#include "gltf_skeleton.h"
-#include "gltf_skin.h"
-#include "gltf_spec_gloss.h"
#include "gltf_state.h"
-#include "gltf_texture.h"
#include "core/crypto/crypto_core.h"
#include "core/error/error_macros.h"
@@ -128,10 +119,10 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
state->buffers.push_back(Vector<uint8_t>());
}
- /* STEP 1 CONVERT MESH INSTANCES */
+ /* STEP CONVERT MESH INSTANCES */
_convert_mesh_instances(state);
- /* STEP 2 SERIALIZE CAMERAS */
+ /* STEP SERIALIZE CAMERAS */
Error err = _serialize_cameras(state);
if (err != OK) {
return Error::FAILED;
@@ -143,37 +134,37 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
return Error::FAILED;
}
- /* STEP 5 SERIALIZE MESHES (we have enough info now) */
+ /* STEP SERIALIZE MESHES (we have enough info now) */
err = _serialize_meshes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 6 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_materials(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 7 SERIALIZE ANIMATIONS */
+ /* STEP SERIALIZE ANIMATIONS */
err = _serialize_animations(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 8 SERIALIZE ACCESSORS */
+ /* STEP SERIALIZE ACCESSORS */
err = _encode_accessors(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 9 SERIALIZE IMAGES */
+ /* STEP SERIALIZE IMAGES */
err = _serialize_images(state, p_path);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 10 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_textures(state);
if (err != OK) {
return Error::FAILED;
@@ -183,55 +174,68 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
state->buffer_views.write[i]->buffer = 0;
}
- /* STEP 11 SERIALIZE BUFFER VIEWS */
+ /* STEP SERIALIZE BUFFER VIEWS */
err = _encode_buffer_views(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 12 SERIALIZE NODES */
+ /* STEP SERIALIZE NODES */
err = _serialize_nodes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 13 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_scenes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 14 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_lights(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 15 SERIALIZE EXTENSIONS */
+ /* STEP SERIALIZE EXTENSIONS */
err = _serialize_extensions(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 16 SERIALIZE VERSION */
+ /* STEP SERIALIZE VERSION */
err = _serialize_version(state);
if (err != OK) {
return Error::FAILED;
}
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->export_post(state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+
return OK;
}
Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const {
- const String texture_transform = "KHR_texture_transform";
- const String punctual_lights = "KHR_lights_punctual";
Array extensions_used;
- extensions_used.push_back(punctual_lights);
- extensions_used.push_back(texture_transform);
- state->json["extensionsUsed"] = extensions_used;
Array extensions_required;
- extensions_required.push_back(texture_transform);
- state->json["extensionsRequired"] = extensions_required;
+ if (!state->lights.is_empty()) {
+ extensions_used.push_back("KHR_lights_punctual");
+ }
+ if (state->use_khr_texture_transform) {
+ extensions_used.push_back("KHR_texture_transform");
+ extensions_required.push_back("KHR_texture_transform");
+ }
+ if (!extensions_used.is_empty()) {
+ state->json["extensionsUsed"] = extensions_used;
+ }
+ if (!extensions_required.is_empty()) {
+ state->json["extensionsRequired"] = extensions_required;
+ }
return OK;
}
@@ -360,9 +364,9 @@ static Transform3D _arr_to_xform(const Array &p_array) {
ERR_FAIL_COND_V(p_array.size() != 16, Transform3D());
Transform3D xform;
- xform.basis.set_axis(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
- xform.basis.set_axis(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
- xform.basis.set_axis(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
+ xform.basis.set_column(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
+ xform.basis.set_column(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
+ xform.basis.set_column(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
xform.set_origin(Vector3(p_array[12], p_array[13], p_array[14]));
return xform;
@@ -371,17 +375,17 @@ static Transform3D _arr_to_xform(const Array &p_array) {
static Vector<real_t> _xform_to_array(const Transform3D p_transform) {
Vector<real_t> array;
array.resize(16);
- Vector3 axis_x = p_transform.get_basis().get_axis(Vector3::AXIS_X);
+ Vector3 axis_x = p_transform.get_basis().get_column(Vector3::AXIS_X);
array.write[0] = axis_x.x;
array.write[1] = axis_x.y;
array.write[2] = axis_x.z;
array.write[3] = 0.0f;
- Vector3 axis_y = p_transform.get_basis().get_axis(Vector3::AXIS_Y);
+ Vector3 axis_y = p_transform.get_basis().get_column(Vector3::AXIS_Y);
array.write[4] = axis_y.x;
array.write[5] = axis_y.y;
array.write[6] = axis_y.z;
array.write[7] = 0.0f;
- Vector3 axis_z = p_transform.get_basis().get_axis(Vector3::AXIS_Z);
+ Vector3 axis_z = p_transform.get_basis().get_column(Vector3::AXIS_Z);
array.write[8] = axis_z.x;
array.write[9] = axis_z.y;
array.write[10] = axis_z.z;
@@ -442,6 +446,15 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
}
node["children"] = children;
}
+
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ ERR_CONTINUE(!state->scene_nodes.find(i));
+ Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]);
+ ERR_CONTINUE(err != OK);
+ }
+
nodes.push_back(node);
}
state->json["nodes"] = nodes;
@@ -918,58 +931,58 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) {
return OK;
}
-String GLTFDocument::_get_accessor_type_name(const GLTFDocument::GLTFType p_type) {
- if (p_type == GLTFDocument::TYPE_SCALAR) {
+String GLTFDocument::_get_accessor_type_name(const GLTFType p_type) {
+ if (p_type == GLTFType::TYPE_SCALAR) {
return "SCALAR";
}
- if (p_type == GLTFDocument::TYPE_VEC2) {
+ if (p_type == GLTFType::TYPE_VEC2) {
return "VEC2";
}
- if (p_type == GLTFDocument::TYPE_VEC3) {
+ if (p_type == GLTFType::TYPE_VEC3) {
return "VEC3";
}
- if (p_type == GLTFDocument::TYPE_VEC4) {
+ if (p_type == GLTFType::TYPE_VEC4) {
return "VEC4";
}
- if (p_type == GLTFDocument::TYPE_MAT2) {
+ if (p_type == GLTFType::TYPE_MAT2) {
return "MAT2";
}
- if (p_type == GLTFDocument::TYPE_MAT3) {
+ if (p_type == GLTFType::TYPE_MAT3) {
return "MAT3";
}
- if (p_type == GLTFDocument::TYPE_MAT4) {
+ if (p_type == GLTFType::TYPE_MAT4) {
return "MAT4";
}
ERR_FAIL_V("SCALAR");
}
-GLTFDocument::GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
+GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
if (p_string == "SCALAR") {
- return GLTFDocument::TYPE_SCALAR;
+ return GLTFType::TYPE_SCALAR;
}
if (p_string == "VEC2") {
- return GLTFDocument::TYPE_VEC2;
+ return GLTFType::TYPE_VEC2;
}
if (p_string == "VEC3") {
- return GLTFDocument::TYPE_VEC3;
+ return GLTFType::TYPE_VEC3;
}
if (p_string == "VEC4") {
- return GLTFDocument::TYPE_VEC4;
+ return GLTFType::TYPE_VEC4;
}
if (p_string == "MAT2") {
- return GLTFDocument::TYPE_MAT2;
+ return GLTFType::TYPE_MAT2;
}
if (p_string == "MAT3") {
- return GLTFDocument::TYPE_MAT3;
+ return GLTFType::TYPE_MAT3;
}
if (p_string == "MAT4") {
- return GLTFDocument::TYPE_MAT4;
+ return GLTFType::TYPE_MAT4;
}
- ERR_FAIL_V(GLTFDocument::TYPE_SCALAR);
+ ERR_FAIL_V(GLTFType::TYPE_SCALAR);
}
Error GLTFDocument::_parse_accessors(Ref<GLTFState> state) {
@@ -1520,7 +1533,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, c
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_INT;
accessor->max = type_max;
@@ -1604,7 +1617,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, c
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC2;
+ const GLTFType type = GLTFType::TYPE_VEC2;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1653,7 +1666,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state,
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1718,7 +1731,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1765,7 +1778,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state,
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
accessor->max = type_max;
@@ -1814,7 +1827,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> s
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1879,7 +1892,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state,
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1925,7 +1938,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, c
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC3;
+ const GLTFType type = GLTFType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -1960,20 +1973,20 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state,
for (int i = 0; i < p_attribs.size(); i++) {
Transform3D attrib = p_attribs[i];
Basis basis = attrib.get_basis();
- Vector3 axis_0 = basis.get_axis(Vector3::AXIS_X);
+ Vector3 axis_0 = basis.get_column(Vector3::AXIS_X);
attribs.write[i * element_count + 0] = Math::snapped(axis_0.x, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 1] = Math::snapped(axis_0.y, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 2] = Math::snapped(axis_0.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 3] = 0.0;
- Vector3 axis_1 = basis.get_axis(Vector3::AXIS_Y);
+ Vector3 axis_1 = basis.get_column(Vector3::AXIS_Y);
attribs.write[i * element_count + 4] = Math::snapped(axis_1.x, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 5] = Math::snapped(axis_1.y, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 6] = Math::snapped(axis_1.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 7] = 0.0;
- Vector3 axis_2 = basis.get_axis(Vector3::AXIS_Z);
+ Vector3 axis_2 = basis.get_column(Vector3::AXIS_Z);
attribs.write[i * element_count + 8] = Math::snapped(axis_2.x, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 9] = Math::snapped(axis_2.y, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 10] = Math::snapped(axis_2.z, CMP_NORMALIZE_TOLERANCE);
@@ -1993,7 +2006,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state,
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_MAT4;
+ const GLTFType type = GLTFType::TYPE_MAT4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
accessor->max = type_max;
@@ -2105,9 +2118,9 @@ Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> state, cons
ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret);
ret.resize(attribs.size() / 9);
for (int i = 0; i < ret.size(); i++) {
- ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
- ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
- ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
+ ret.write[i].set_column(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
+ ret.write[i].set_column(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
+ ret.write[i].set_column(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
}
return ret;
}
@@ -2123,9 +2136,9 @@ Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> state
ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret);
ret.resize(attribs.size() / 16);
for (int i = 0; i < ret.size(); i++) {
- ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
- ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
- ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
+ ret.write[i].basis.set_column(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
+ ret.write[i].basis.set_column(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
+ ret.write[i].basis.set_column(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14]));
}
return ret;
@@ -2294,7 +2307,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true);
}
}
- Map<int, int> joint_i_to_bone_i;
+ HashMap<int, int> joint_i_to_bone_i;
for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
GLTFSkinIndex skin_i = -1;
if (state->nodes[node_i]->mesh == gltf_mesh_i) {
@@ -2468,9 +2481,9 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
mat = import_mesh->get_surface_material(surface_i);
}
if (mat.is_valid()) {
- Map<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Element *material_cache_i = state->material_cache.find(mat);
- if (material_cache_i && material_cache_i->get() != -1) {
- primitive["material"] = material_cache_i->get();
+ HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat);
+ if (material_cache_i && material_cache_i->value != -1) {
+ primitive["material"] = material_cache_i->value;
} else {
GLTFMaterialIndex mat_i = state->materials.size();
state->materials.push_back(mat);
@@ -3064,9 +3077,9 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
// We'll assume that we use either URI or bufferView, so let's warn the user
// if their image somehow uses both. And fail if it has neither.
- ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specific an 'uri' or 'bufferView'.");
+ ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specify an 'uri' or 'bufferView'.");
if (d.has("uri") && d.has("bufferView")) {
- WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'bufferView' will take precedence.");
+ WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'uri' will take precedence.");
}
String mimetype;
@@ -3186,12 +3199,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
state->images.push_back(Ref<Texture2D>());
continue;
}
-
- Ref<ImageTexture> t;
- t.instantiate();
- t->create_from_image(img);
-
- state->images.push_back(t);
+ state->images.push_back(ImageTexture::create_from_image(img));
}
print_verbose("glTF: Total images: " + itos(state->images.size()));
@@ -3294,7 +3302,11 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
if (gltf_texture_index != -1) {
bct["index"] = gltf_texture_index;
- bct["extensions"] = _serialize_texture_transform_uv1(material);
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ bct["extensions"] = extensions;
+ state->use_khr_texture_transform = true;
+ }
mr["baseColorTexture"] = bct;
}
}
@@ -3412,7 +3424,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
}
orm_image->generate_mipmaps();
- orm_texture->create_from_image(orm_image);
+ orm_texture->set_image(orm_image);
GLTFTextureIndex orm_texture_index = -1;
if (has_ao || has_roughness || has_metalness) {
orm_texture->set_name(material->get_name() + "_orm");
@@ -3425,7 +3437,11 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
if (has_roughness || has_metalness) {
mrt["index"] = orm_texture_index;
- mrt["extensions"] = _serialize_texture_transform_uv1(material);
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ mrt["extensions"] = extensions;
+ state->use_khr_texture_transform = true;
+ }
mr["metallicRoughnessTexture"] = mrt;
}
}
@@ -3460,7 +3476,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
img->set_pixel(x, y, c);
}
}
- tex->create_from_image(img);
+ tex->set_image(img);
}
}
}
@@ -3768,13 +3784,8 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Re
}
rm_img->generate_mipmaps();
r_spec_gloss->diffuse_img->generate_mipmaps();
- Ref<ImageTexture> diffuse_image_texture;
- diffuse_image_texture.instantiate();
- diffuse_image_texture->create_from_image(r_spec_gloss->diffuse_img);
- p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_image_texture);
- Ref<ImageTexture> rm_image_texture;
- rm_image_texture.instantiate();
- rm_image_texture->create_from_image(rm_img);
+ p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, ImageTexture::create_from_image(r_spec_gloss->diffuse_img));
+ Ref<ImageTexture> rm_image_texture = ImageTexture::create_from_image(rm_img);
if (has_roughness) {
p_material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, rm_image_texture);
p_material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN);
@@ -4519,6 +4530,9 @@ void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> state) {
}
Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) {
+ if (state->lights.is_empty()) {
+ return OK;
+ }
Array lights;
for (GLTFLightIndex i = 0; i < state->lights.size(); i++) {
Dictionary d;
@@ -4545,10 +4559,6 @@ Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) {
lights.push_back(d);
}
- if (!state->lights.size()) {
- return OK;
- }
-
Dictionary extensions;
if (state->json.has("extensions")) {
extensions = state->json["extensions"];
@@ -5160,19 +5170,16 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex
}
const float range = CLAMP(l->range, 0, 4096);
- // Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
- // We want to have double intensity give double brightness, so we need half the attenuation.
- const float attenuation = range / (intensity * 2048);
if (l->light_type == "point") {
OmniLight3D *light = memnew(OmniLight3D);
- light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(OmniLight3D::PARAM_ENERGY, intensity);
light->set_param(OmniLight3D::PARAM_RANGE, range);
light->set_color(l->color);
return light;
}
if (l->light_type == "spot") {
SpotLight3D *light = memnew(SpotLight3D);
- light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(SpotLight3D::PARAM_ENERGY, intensity);
light->set_param(SpotLight3D::PARAM_RANGE, range);
light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l->outer_cone_angle));
light->set_color(l->color);
@@ -5211,7 +5218,7 @@ GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_
Ref<GLTFCamera> c;
c.instantiate();
- if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) {
+ if (p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE) {
c->set_perspective(true);
}
c->set_fov_size(p_camera->get_fov());
@@ -5237,14 +5244,12 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig
l->light_type = "point";
OmniLight3D *light = cast_to<OmniLight3D>(p_light);
l->range = light->get_param(OmniLight3D::PARAM_RANGE);
- float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION);
- l->intensity = l->range / (attenuation * 2048);
+ l->intensity = light->get_param(OmniLight3D::PARAM_ENERGY);
} else if (cast_to<SpotLight3D>(p_light)) {
l->light_type = "spot";
SpotLight3D *light = cast_to<SpotLight3D>(p_light);
l->range = light->get_param(SpotLight3D::PARAM_RANGE);
- float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION);
- l->intensity = l->range / (attenuation * 2048);
+ l->intensity = light->get_param(SpotLight3D::PARAM_ENERGY);
l->outer_cone_angle = Math::deg2rad(light->get_param(SpotLight3D::PARAM_SPOT_ANGLE));
// This equation is the inverse of the import equation (which has a desmos link).
@@ -5658,7 +5663,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
if (!current_node) {
current_node = _generate_spatial(state, node_index);
}
- scene_parent->add_child(current_node);
+ scene_parent->add_child(current_node, true);
if (current_node != scene_root) {
current_node->set_owner(scene_root);
}
@@ -5904,9 +5909,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Node *root = ap->get_parent();
ERR_FAIL_COND(root == nullptr);
- Map<GLTFNodeIndex, Node *>::Element *node_element = state->scene_nodes.find(node_index);
- ERR_CONTINUE_MSG(node_element == nullptr, vformat("Unable to find node %d for animation", node_index));
- node_path = root->get_path_to(node_element->get());
+ HashMap<GLTFNodeIndex, Node *>::Iterator node_element = state->scene_nodes.find(node_index);
+ ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation", node_index));
+ node_path = root->get_path_to(node_element->value);
if (gltf_node->skeleton >= 0) {
const Skeleton3D *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton;
@@ -6116,11 +6121,11 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
if (node->mesh < 0) {
continue;
}
- Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(mi_node_i);
if (!mi_element) {
continue;
}
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get());
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->value);
if (!mi) {
continue;
}
@@ -6250,11 +6255,11 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
if (node->skin >= 0 && node->mesh >= 0) {
const GLTFSkinIndex skin_i = node->skin;
- Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i);
- ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i));
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(node_i);
+ ERR_CONTINUE_MSG(!mi_element, vformat("Unable to find node %d", node_i));
- ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->get());
- ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->get()->get_class_name()));
+ ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->value);
+ ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->value->get_class_name()));
const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton;
Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i];
@@ -6436,10 +6441,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &position_scene_node_i : state->scene_nodes) {
if (position_scene_node_i.value == node) {
GLTFNodeIndex node_index = position_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *position_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator position_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (position_track_i) {
- track = position_track_i->get();
+ track = position_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6452,10 +6457,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &rotation_degree_scene_node_i : state->scene_nodes) {
if (rotation_degree_scene_node_i.value == node) {
GLTFNodeIndex node_index = rotation_degree_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (rotation_degree_track_i) {
- track = rotation_degree_track_i->get();
+ track = rotation_degree_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6468,10 +6473,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &scale_scene_node_i : state->scene_nodes) {
if (scale_scene_node_i.value == node) {
GLTFNodeIndex node_index = scale_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *scale_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator scale_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (scale_track_i) {
- track = scale_track_i->get();
+ track = scale_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6503,7 +6508,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
ERR_CONTINUE(mesh_index == -1);
- Map<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
+ HashMap<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track();
if (!tracks.has(mesh_index)) {
for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
@@ -6565,10 +6570,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone];
- Map<int, GLTFAnimation::Track>::Element *property_track_i = gltf_animation->get_tracks().find(node_i);
+ HashMap<int, GLTFAnimation::Track>::Iterator property_track_i = gltf_animation->get_tracks().find(node_i);
GLTFAnimation::Track track;
if (property_track_i) {
- track = property_track_i->get();
+ track = property_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
@@ -6580,10 +6585,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
if (scene_node_i.value == godot_node) {
GLTFNodeIndex node_i = scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_i);
+ HashMap<int, GLTFAnimation::Track>::Iterator node_track_i = gltf_animation->get_tracks().find(node_i);
GLTFAnimation::Track track;
if (node_track_i) {
- track = node_track_i->get();
+ track = node_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
@@ -6642,53 +6647,56 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess>
for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
ERR_CONTINUE(ext.is_null());
- err = ext->import_preflight(this);
- ERR_FAIL_COND_V(err != OK, FAILED);
+ err = ext->import_preflight(state);
+ ERR_FAIL_COND_V(err != OK, err);
}
err = _parse_gltf_state(state, p_path, p_bake_fps);
ERR_FAIL_COND_V(err != OK, err);
return OK;
}
-Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) {
- Dictionary extension;
- Ref<BaseMaterial3D> mat = p_material;
- if (mat.is_valid()) {
- Dictionary texture_transform;
+Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) {
+ Dictionary texture_transform;
+ bool is_offset = p_offset != Vector2(0.0, 0.0);
+ if (is_offset) {
Array offset;
offset.resize(2);
- offset[0] = mat->get_uv2_offset().x;
- offset[1] = mat->get_uv2_offset().y;
+ offset[0] = p_offset.x;
+ offset[1] = p_offset.y;
texture_transform["offset"] = offset;
+ }
+ bool is_scaled = p_scale != Vector2(1.0, 1.0);
+ if (is_scaled) {
Array scale;
scale.resize(2);
- scale[0] = mat->get_uv2_scale().x;
- scale[1] = mat->get_uv2_scale().y;
+ scale[0] = p_scale.x;
+ scale[1] = p_scale.y;
texture_transform["scale"] = scale;
- // Godot doesn't support texture rotation
+ }
+ Dictionary extension;
+ // Note: Godot doesn't support texture rotation.
+ if (is_offset || is_scaled) {
extension["KHR_texture_transform"] = texture_transform;
}
return extension;
}
Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) {
- Dictionary extension;
if (p_material.is_valid()) {
- Dictionary texture_transform;
- Array offset;
- offset.resize(2);
- offset[0] = p_material->get_uv1_offset().x;
- offset[1] = p_material->get_uv1_offset().y;
- texture_transform["offset"] = offset;
- Array scale;
- scale.resize(2);
- scale[0] = p_material->get_uv1_scale().x;
- scale[1] = p_material->get_uv1_scale().y;
- texture_transform["scale"] = scale;
- // Godot doesn't support texture rotation
- extension["KHR_texture_transform"] = texture_transform;
+ Vector3 offset = p_material->get_uv1_offset();
+ Vector3 scale = p_material->get_uv1_scale();
+ return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
}
- return extension;
+ return Dictionary();
+}
+
+Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) {
+ if (p_material.is_valid()) {
+ Vector3 offset = p_material->get_uv2_offset();
+ Vector3 scale = p_material->get_uv2_scale();
+ return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y));
+ }
+ return Dictionary();
}
Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
@@ -6870,12 +6878,10 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> state) {
Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_path) {
ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER);
-
Error err = _serialize(state, p_path);
if (err != OK) {
return err;
}
-
err = _serialize_file(state, p_path);
if (err != OK) {
return Error::FAILED;
@@ -6886,6 +6892,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa
Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
+ Error err = OK;
GLTFNodeIndex gltf_root = state->root_nodes.write[0];
Node *gltf_root_node = state->get_scene_node(gltf_root);
Node *root = gltf_root_node->get_parent();
@@ -6899,12 +6906,26 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
_import_animation(state, ap, i, p_bake_fps);
}
}
-
+ for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
+ ERR_CONTINUE(!E.value);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ ERR_CONTINUE(!state->json.has("nodes"));
+ Array nodes = state->json["nodes"];
+ ERR_CONTINUE(E.key >= nodes.size());
+ ERR_CONTINUE(E.key < 0);
+ Dictionary node_json = nodes[E.key];
+ Ref<GLTFNode> gltf_node = state->nodes[E.key];
+ err = ext->import_node(state, gltf_node, node_json, E.value);
+ ERR_CONTINUE(err != OK);
+ }
+ }
for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
ERR_CONTINUE(ext.is_null());
- Error err = ext->import_post(this, root);
- ERR_FAIL_COND_V(err != OK, nullptr);
+ err = ext->import_post(state, root);
+ ERR_CONTINUE(err != OK);
}
ERR_FAIL_NULL_V(root, nullptr);
return root;
@@ -6915,17 +6936,17 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32
state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ Error err = ext->export_preflight(p_node);
+ ERR_FAIL_COND_V(err != OK, FAILED);
+ }
_convert_scene_node(state, p_node, -1, -1);
if (!state->buffers.size()) {
state->buffers.push_back(Vector<uint8_t>());
}
-
- /* STEP 1 CONVERT MESH INSTANCES */
- _convert_mesh_instances(state);
-
- /* STEP 2 CREATE SKINS */
- Error err = _serialize_skins(state);
- return err;
+ return OK;
}
Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) {
@@ -6938,8 +6959,15 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
- err = _parse(state, p_base_path.get_base_dir(), file_access, p_bake_fps);
- ERR_FAIL_COND_V(err != OK, FAILED);
+ state->base_path = p_base_path.get_base_dir();
+ err = _parse(state, state->base_path, file_access, p_bake_fps);
+ ERR_FAIL_COND_V(err != OK, err);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->import_post_parse(state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
return OK;
}
@@ -7030,6 +7058,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_sear
for (int32_t root_i = 0; root_i < state->root_nodes.size(); root_i++) {
_generate_scene_node(state, root, root, state->root_nodes[root_i]);
}
+
return OK;
}
@@ -7049,9 +7078,16 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
if (base_path.is_empty()) {
base_path = p_path.get_base_dir();
}
+ r_state->base_path = base_path;
err = _parse(r_state, base_path, f, p_bake_fps);
- ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- return err;
+ ERR_FAIL_COND_V(err != OK, err);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->import_post_parse(r_state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+ return OK;
}
Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 19bc507a8d..36a2f94a4e 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -31,55 +31,19 @@
#ifndef GLTF_DOCUMENT_H
#define GLTF_DOCUMENT_H
-#include "gltf_animation.h"
+#include "gltf_defines.h"
+#include "structures/gltf_animation.h"
-#include "core/error/error_list.h"
-#include "core/variant/dictionary.h"
-#include "core/variant/variant.h"
-#include "gltf_document_extension_convert_importer_mesh.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
-#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/material.h"
-#include "scene/resources/texture.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
-#include <cstdint>
-
-class GLTFState;
-class GLTFSkin;
-class GLTFNode;
-class GLTFSpecGloss;
-class GLTFSkeleton;
-class CSGShape3D;
-class GridMap;
-class MultiMeshInstance3D;
-class GLTFDocumentExtension;
-
-using GLTFAccessorIndex = int;
-using GLTFAnimationIndex = int;
-using GLTFBufferIndex = int;
-using GLTFBufferViewIndex = int;
-using GLTFCameraIndex = int;
-using GLTFImageIndex = int;
-using GLTFMaterialIndex = int;
-using GLTFMeshIndex = int;
-using GLTFLightIndex = int;
-using GLTFNodeIndex = int;
-using GLTFSkeletonIndex = int;
-using GLTFSkinIndex = int;
-using GLTFTextureIndex = int;
-
class GLTFDocument : public Resource {
GDCLASS(GLTFDocument, Resource);
- friend class GLTFState;
- friend class GLTFSkin;
- friend class GLTFSkeleton;
TypedArray<GLTFDocumentExtension> document_extensions;
private:
@@ -88,15 +52,6 @@ private:
public:
GLTFDocument();
const int32_t JOINT_GROUP_SIZE = 4;
- enum GLTFType {
- TYPE_SCALAR,
- TYPE_VEC2,
- TYPE_VEC3,
- TYPE_VEC4,
- TYPE_MAT2,
- TYPE_MAT3,
- TYPE_MAT4,
- };
enum {
ARRAY_BUFFER = 34962,
@@ -125,58 +80,6 @@ public:
TypedArray<GLTFDocumentExtension> get_extensions() const;
private:
- template <class T>
- static Array to_array(const Vector<T> &p_inp) {
- Array ret;
- for (int i = 0; i < p_inp.size(); i++) {
- ret.push_back(p_inp[i]);
- }
- return ret;
- }
-
- template <class T>
- static Array to_array(const Set<T> &p_inp) {
- Array ret;
- typename Set<T>::Element *elem = p_inp.front();
- while (elem) {
- ret.push_back(elem->get());
- elem = elem->next();
- }
- return ret;
- }
-
- template <class T>
- static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
- r_out.clear();
- for (int i = 0; i < p_inp.size(); i++) {
- r_out.push_back(p_inp[i]);
- }
- }
-
- template <class T>
- static void set_from_array(Set<T> &r_out, const Array &p_inp) {
- r_out.clear();
- for (int i = 0; i < p_inp.size(); i++) {
- r_out.insert(p_inp[i]);
- }
- }
- template <class K, class V>
- static Dictionary to_dict(const Map<K, V> &p_inp) {
- Dictionary ret;
- for (typename Map<K, V>::Element *E = p_inp.front(); E; E = E->next()) {
- ret[E->key()] = E->value();
- }
- return ret;
- }
-
- template <class K, class V>
- static void set_from_dict(Map<K, V> &r_out, const Dictionary &p_inp) {
- r_out.clear();
- Array keys = p_inp.keys();
- for (int i = 0; i < keys.size(); i++) {
- r_out[keys[i]] = p_inp[keys[i]];
- }
- }
void _build_parent_hierachy(Ref<GLTFState> state);
double _filter_number(double p_float);
String _get_component_type_name(const uint32_t p_component);
@@ -184,7 +87,7 @@ private:
Error _parse_scenes(Ref<GLTFState> state);
Error _parse_nodes(Ref<GLTFState> state);
String _get_type_name(const GLTFType p_component);
- String _get_accessor_type_name(const GLTFDocument::GLTFType p_type);
+ String _get_accessor_type_name(const GLTFType p_type);
String _gen_unique_name(Ref<GLTFState> state, const String &p_name);
String _sanitize_animation_name(const String &name);
String _gen_unique_animation_name(Ref<GLTFState> state, const String &p_name);
diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp
index 192a1d347c..d0bd7651e0 100644
--- a/modules/gltf/gltf_document_extension.cpp
+++ b/modules/gltf/gltf_document_extension.cpp
@@ -30,59 +30,79 @@
#include "gltf_document_extension.h"
-#include "gltf_document.h"
-
void GLTFDocumentExtension::_bind_methods() {
- // Import
- ClassDB::bind_method(D_METHOD("get_import_setting_keys"),
- &GLTFDocumentExtension::get_import_setting_keys);
- ClassDB::bind_method(D_METHOD("import_preflight", "document"),
- &GLTFDocumentExtension::import_preflight);
- ClassDB::bind_method(D_METHOD("get_import_setting", "key"),
- &GLTFDocumentExtension::get_import_setting);
- ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"),
- &GLTFDocumentExtension::set_import_setting);
- ClassDB::bind_method(D_METHOD("import_post", "document", "node"),
- &GLTFDocumentExtension::import_post);
- // Export
- ClassDB::bind_method(D_METHOD("get_export_setting_keys"),
- &GLTFDocumentExtension::get_export_setting_keys);
- ClassDB::bind_method(D_METHOD("get_export_setting", "key"),
- &GLTFDocumentExtension::get_export_setting);
- ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"),
- &GLTFDocumentExtension::set_export_setting);
- ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"),
- &GLTFDocumentExtension::export_preflight);
- ClassDB::bind_method(D_METHOD("export_post", "document"),
- &GLTFDocumentExtension::export_post);
+ GDVIRTUAL_BIND(_import_preflight, "state");
+ GDVIRTUAL_BIND(_import_post_parse, "state");
+ GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node");
+ GDVIRTUAL_BIND(_import_post, "state", "root");
+ GDVIRTUAL_BIND(_export_preflight, "root");
+ GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node");
+ GDVIRTUAL_BIND(_export_post, "state");
}
-Array GLTFDocumentExtension::get_import_setting_keys() const {
- return import_settings.keys();
+Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_post, p_state, p_root, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const {
- if (!import_settings.has(p_key)) {
- return Variant();
+Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) {
+ return Error(err);
}
- return import_settings[p_key];
+ return OK;
}
-void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) {
- import_settings[p_key] = p_var;
+Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_post_parse, p_state, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Array GLTFDocumentExtension::get_export_setting_keys() const {
- return import_settings.keys();
+Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_post, p_state, err)) {
+ return Error(err);
+ }
+ return OK;
+}
+Error GLTFDocumentExtension::export_preflight(Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_preflight, p_root, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const {
- if (!import_settings.has(p_key)) {
- return Variant();
+Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) {
+ return Error(err);
}
- return import_settings[p_key];
+ return OK;
}
-void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) {
- import_settings[p_key] = p_var;
+Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err)) {
+ return Error(err);
+ }
+ return OK;
}
diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h
index f7a3531282..0ef9109584 100644
--- a/modules/gltf/gltf_document_extension.h
+++ b/modules/gltf/gltf_document_extension.h
@@ -31,33 +31,30 @@
#ifndef GLTF_DOCUMENT_EXTENSION_H
#define GLTF_DOCUMENT_EXTENSION_H
-#include "core/io/resource.h"
-#include "core/variant/dictionary.h"
-#include "core/variant/typed_array.h"
-#include "core/variant/variant.h"
-class GLTFDocument;
+#include "gltf_state.h"
+#include "structures/gltf_node.h"
+
class GLTFDocumentExtension : public Resource {
GDCLASS(GLTFDocumentExtension, Resource);
- Dictionary import_settings;
- Dictionary export_settings;
-
protected:
static void _bind_methods();
public:
- virtual Array get_import_setting_keys() const;
- virtual Variant get_import_setting(const StringName &p_key) const;
- virtual void set_import_setting(const StringName &p_key, Variant p_var);
- virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; }
- virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
-
-public:
- virtual Array get_export_setting_keys() const;
- virtual Variant get_export_setting(const StringName &p_key) const;
- virtual void set_export_setting(const StringName &p_key, Variant p_var);
- virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
- virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; }
+ virtual Error import_preflight(Ref<GLTFState> p_state);
+ virtual Error import_post_parse(Ref<GLTFState> p_state);
+ virtual Error export_post(Ref<GLTFState> p_state);
+ virtual Error import_post(Ref<GLTFState> p_state, Node *p_node);
+ virtual Error export_preflight(Node *p_state);
+ virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
+ virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
+ GDVIRTUAL1R(int, _import_preflight, Ref<GLTFState>);
+ GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>);
+ GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
+ GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *);
+ GDVIRTUAL1R(int, _export_preflight, Node *);
+ GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
+ GDVIRTUAL1R(int, _export_post, Ref<GLTFState>);
};
#endif // GLTF_DOCUMENT_EXTENSION_H
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
index 47a3e5598f..1620900a04 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
@@ -29,41 +29,40 @@
/*************************************************************************/
#include "gltf_document_extension_convert_importer_mesh.h"
+
+#include "gltf_state.h"
+
#include "core/error/error_macros.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/importer_mesh.h"
-#include <cstddef>
-
void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
}
-Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) {
- ERR_FAIL_NULL_V(p_document, ERR_INVALID_PARAMETER);
- ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_state, Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
List<Node *> queue;
- queue.push_back(p_node);
+ queue.push_back(p_root);
List<Node *> delete_queue;
while (!queue.is_empty()) {
List<Node *>::Element *E = queue.front();
Node *node = E->get();
- {
- ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node);
- if (mesh_3d) {
- MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
- Ref<ImporterMesh> mesh = mesh_3d->get_mesh();
- if (mesh.is_valid()) {
- Ref<ArrayMesh> array_mesh = mesh->get_mesh();
- mesh_instance_node_3d->set_name(node->get_name());
- mesh_instance_node_3d->set_transform(mesh_3d->get_transform());
- mesh_instance_node_3d->set_mesh(array_mesh);
- mesh_instance_node_3d->set_skin(mesh_3d->get_skin());
- mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
- node->replace_by(mesh_instance_node_3d);
- delete_queue.push_back(node);
- } else {
- memdelete(mesh_instance_node_3d);
- }
+ ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node);
+ if (mesh_3d) {
+ MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
+ Ref<ImporterMesh> mesh = mesh_3d->get_mesh();
+ if (mesh.is_valid()) {
+ Ref<ArrayMesh> array_mesh = mesh->get_mesh();
+ mesh_instance_node_3d->set_name(node->get_name());
+ mesh_instance_node_3d->set_transform(mesh_3d->get_transform());
+ mesh_instance_node_3d->set_mesh(array_mesh);
+ mesh_instance_node_3d->set_skin(mesh_3d->get_skin());
+ mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
+ node->replace_by(mesh_instance_node_3d);
+ delete_queue.push_back(node);
+ } else {
+ memdelete(mesh_instance_node_3d);
}
}
int child_count = node->get_child_count();
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
index 2d51143140..00e664e73f 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.h
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
@@ -28,21 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GLTF_EXTENSION_EDITOR_H
-#define GLTF_EXTENSION_EDITOR_H
+#ifndef GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H
+#define GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H
-#include "core/io/resource.h"
-#include "core/variant/dictionary.h"
-
-#include "gltf_document.h"
#include "gltf_document_extension.h"
+
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
-#include "scene/main/node.h"
#include "scene/resources/importer_mesh.h"
-class GLTFDocumentExtension;
-class GLTFDocument;
class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
@@ -50,6 +44,7 @@ protected:
static void _bind_methods();
public:
- Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override;
+ Error import_post(Ref<GLTFState> p_state, Node *p_root) override;
};
-#endif // GLTF_EXTENSION_EDITOR_H
+
+#endif // GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index 6ead2f69c3..a5f7bcf9d6 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -57,6 +57,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_materials", "materials"), &GLTFState::set_materials);
ClassDB::bind_method(D_METHOD("get_scene_name"), &GLTFState::get_scene_name);
ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name);
+ ClassDB::bind_method(D_METHOD("get_base_path"), &GLTFState::get_base_path);
+ ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path);
ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes);
ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
@@ -93,6 +95,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector<Ref<GLTFMesh>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material>
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture>
@@ -102,7 +105,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set<String>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>>
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // Map<GLTFSkeletonIndex,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex,
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
}
@@ -147,51 +150,51 @@ void GLTFState::set_use_named_skin_binds(bool p_use_named_skin_binds) {
}
Array GLTFState::get_nodes() {
- return GLTFDocument::to_array(nodes);
+ return GLTFTemplateConvert::to_array(nodes);
}
void GLTFState::set_nodes(Array p_nodes) {
- GLTFDocument::set_from_array(nodes, p_nodes);
+ GLTFTemplateConvert::set_from_array(nodes, p_nodes);
}
Array GLTFState::get_buffers() {
- return GLTFDocument::to_array(buffers);
+ return GLTFTemplateConvert::to_array(buffers);
}
void GLTFState::set_buffers(Array p_buffers) {
- GLTFDocument::set_from_array(buffers, p_buffers);
+ GLTFTemplateConvert::set_from_array(buffers, p_buffers);
}
Array GLTFState::get_buffer_views() {
- return GLTFDocument::to_array(buffer_views);
+ return GLTFTemplateConvert::to_array(buffer_views);
}
void GLTFState::set_buffer_views(Array p_buffer_views) {
- GLTFDocument::set_from_array(buffer_views, p_buffer_views);
+ GLTFTemplateConvert::set_from_array(buffer_views, p_buffer_views);
}
Array GLTFState::get_accessors() {
- return GLTFDocument::to_array(accessors);
+ return GLTFTemplateConvert::to_array(accessors);
}
void GLTFState::set_accessors(Array p_accessors) {
- GLTFDocument::set_from_array(accessors, p_accessors);
+ GLTFTemplateConvert::set_from_array(accessors, p_accessors);
}
Array GLTFState::get_meshes() {
- return GLTFDocument::to_array(meshes);
+ return GLTFTemplateConvert::to_array(meshes);
}
void GLTFState::set_meshes(Array p_meshes) {
- GLTFDocument::set_from_array(meshes, p_meshes);
+ GLTFTemplateConvert::set_from_array(meshes, p_meshes);
}
Array GLTFState::get_materials() {
- return GLTFDocument::to_array(materials);
+ return GLTFTemplateConvert::to_array(materials);
}
void GLTFState::set_materials(Array p_materials) {
- GLTFDocument::set_from_array(materials, p_materials);
+ GLTFTemplateConvert::set_from_array(materials, p_materials);
}
String GLTFState::get_scene_name() {
@@ -203,91 +206,91 @@ void GLTFState::set_scene_name(String p_scene_name) {
}
Array GLTFState::get_root_nodes() {
- return GLTFDocument::to_array(root_nodes);
+ return GLTFTemplateConvert::to_array(root_nodes);
}
void GLTFState::set_root_nodes(Array p_root_nodes) {
- GLTFDocument::set_from_array(root_nodes, p_root_nodes);
+ GLTFTemplateConvert::set_from_array(root_nodes, p_root_nodes);
}
Array GLTFState::get_textures() {
- return GLTFDocument::to_array(textures);
+ return GLTFTemplateConvert::to_array(textures);
}
void GLTFState::set_textures(Array p_textures) {
- GLTFDocument::set_from_array(textures, p_textures);
+ GLTFTemplateConvert::set_from_array(textures, p_textures);
}
Array GLTFState::get_images() {
- return GLTFDocument::to_array(images);
+ return GLTFTemplateConvert::to_array(images);
}
void GLTFState::set_images(Array p_images) {
- GLTFDocument::set_from_array(images, p_images);
+ GLTFTemplateConvert::set_from_array(images, p_images);
}
Array GLTFState::get_skins() {
- return GLTFDocument::to_array(skins);
+ return GLTFTemplateConvert::to_array(skins);
}
void GLTFState::set_skins(Array p_skins) {
- GLTFDocument::set_from_array(skins, p_skins);
+ GLTFTemplateConvert::set_from_array(skins, p_skins);
}
Array GLTFState::get_cameras() {
- return GLTFDocument::to_array(cameras);
+ return GLTFTemplateConvert::to_array(cameras);
}
void GLTFState::set_cameras(Array p_cameras) {
- GLTFDocument::set_from_array(cameras, p_cameras);
+ GLTFTemplateConvert::set_from_array(cameras, p_cameras);
}
Array GLTFState::get_lights() {
- return GLTFDocument::to_array(lights);
+ return GLTFTemplateConvert::to_array(lights);
}
void GLTFState::set_lights(Array p_lights) {
- GLTFDocument::set_from_array(lights, p_lights);
+ GLTFTemplateConvert::set_from_array(lights, p_lights);
}
Array GLTFState::get_unique_names() {
- return GLTFDocument::to_array(unique_names);
+ return GLTFTemplateConvert::to_array(unique_names);
}
void GLTFState::set_unique_names(Array p_unique_names) {
- GLTFDocument::set_from_array(unique_names, p_unique_names);
+ GLTFTemplateConvert::set_from_array(unique_names, p_unique_names);
}
Array GLTFState::get_unique_animation_names() {
- return GLTFDocument::to_array(unique_animation_names);
+ return GLTFTemplateConvert::to_array(unique_animation_names);
}
void GLTFState::set_unique_animation_names(Array p_unique_animation_names) {
- GLTFDocument::set_from_array(unique_animation_names, p_unique_animation_names);
+ GLTFTemplateConvert::set_from_array(unique_animation_names, p_unique_animation_names);
}
Array GLTFState::get_skeletons() {
- return GLTFDocument::to_array(skeletons);
+ return GLTFTemplateConvert::to_array(skeletons);
}
void GLTFState::set_skeletons(Array p_skeletons) {
- GLTFDocument::set_from_array(skeletons, p_skeletons);
+ GLTFTemplateConvert::set_from_array(skeletons, p_skeletons);
}
Dictionary GLTFState::get_skeleton_to_node() {
- return GLTFDocument::to_dict(skeleton_to_node);
+ return GLTFTemplateConvert::to_dict(skeleton_to_node);
}
void GLTFState::set_skeleton_to_node(Dictionary p_skeleton_to_node) {
- GLTFDocument::set_from_dict(skeleton_to_node, p_skeleton_to_node);
+ GLTFTemplateConvert::set_from_dict(skeleton_to_node, p_skeleton_to_node);
}
Array GLTFState::get_animations() {
- return GLTFDocument::to_array(animations);
+ return GLTFTemplateConvert::to_array(animations);
}
void GLTFState::set_animations(Array p_animations) {
- GLTFDocument::set_from_array(animations, p_animations);
+ GLTFTemplateConvert::set_from_array(animations, p_animations);
}
Node *GLTFState::get_scene_node(GLTFNodeIndex idx) {
@@ -313,3 +316,11 @@ void GLTFState::set_discard_meshes_and_materials(bool p_discard_meshes_and_mater
bool GLTFState::get_discard_meshes_and_materials() {
return discard_meshes_and_materials;
}
+
+String GLTFState::get_base_path() {
+ return base_path;
+}
+
+void GLTFState::set_base_path(String p_base_path) {
+ base_path = p_base_path;
+}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index 42ca079f1c..d2a4948f06 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -31,23 +31,19 @@
#ifndef GLTF_STATE_H
#define GLTF_STATE_H
-#include "gltf_accessor.h"
-#include "gltf_animation.h"
-#include "gltf_buffer_view.h"
-#include "gltf_camera.h"
-#include "gltf_document.h"
-#include "gltf_document_extension.h"
-#include "gltf_light.h"
-#include "gltf_mesh.h"
-#include "gltf_node.h"
-#include "gltf_skeleton.h"
-#include "gltf_skin.h"
-#include "gltf_texture.h"
-
-#include "core/io/resource.h"
-#include "core/templates/map.h"
-#include "core/templates/pair.h"
-#include "core/templates/vector.h"
+#include "extensions/gltf_light.h"
+#include "gltf_template_convert.h"
+#include "structures/gltf_accessor.h"
+#include "structures/gltf_animation.h"
+#include "structures/gltf_buffer_view.h"
+#include "structures/gltf_camera.h"
+#include "structures/gltf_mesh.h"
+#include "structures/gltf_node.h"
+#include "structures/gltf_skeleton.h"
+#include "structures/gltf_skin.h"
+#include "structures/gltf_texture.h"
+
+#include "core/templates/rb_map.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/texture.h"
@@ -56,12 +52,14 @@ class GLTFState : public Resource {
friend class GLTFDocument;
String filename;
+ String base_path;
Dictionary json;
int major_version = 0;
int minor_version = 0;
Vector<uint8_t> glb_data;
bool use_named_skin_binds = false;
+ bool use_khr_texture_transform = false;
bool discard_meshes_and_materials = false;
Vector<Ref<GLTFNode>> nodes;
@@ -72,7 +70,7 @@ class GLTFState : public Resource {
Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to.
Vector<AnimationPlayer *> animation_players;
- Map<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
+ HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
Vector<Ref<BaseMaterial3D>> materials;
String scene_name;
@@ -83,16 +81,16 @@ class GLTFState : public Resource {
Vector<Ref<GLTFSkin>> skins;
Vector<Ref<GLTFCamera>> cameras;
Vector<Ref<GLTFLight>> lights;
- Set<String> unique_names;
- Set<String> unique_animation_names;
+ HashSet<String> unique_names;
+ HashSet<String> unique_animation_names;
Vector<Ref<GLTFSkeleton>> skeletons;
- Map<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node;
+ HashMap<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node;
Vector<Ref<GLTFAnimation>> animations;
- Map<GLTFNodeIndex, Node *> scene_nodes;
+ HashMap<GLTFNodeIndex, Node *> scene_nodes;
- Map<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
- Map<ObjectID, Map<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+ HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
+ HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
protected:
static void _bind_methods();
@@ -137,6 +135,9 @@ public:
String get_scene_name();
void set_scene_name(String p_scene_name);
+ String get_base_path();
+ void set_base_path(String p_base_path);
+
Array get_root_nodes();
void set_root_nodes(Array p_root_nodes);
@@ -176,7 +177,7 @@ public:
AnimationPlayer *get_animation_player(int idx);
- //void set_scene_nodes(Map<GLTFNodeIndex, Node *> p_scene_nodes) {
+ //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) {
// this->scene_nodes = p_scene_nodes;
//}
@@ -184,11 +185,12 @@ public:
// this->animation_players = p_animation_players;
//}
- //Map<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
+ //RBMap<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
// return this->material_cache;
//}
- //void set_material_cache(Map<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
+ //void set_material_cache(RBMap<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
// this->material_cache = p_material_cache;
//}
};
+
#endif // GLTF_STATE_H
diff --git a/modules/gltf/gltf_template_convert.h b/modules/gltf/gltf_template_convert.h
new file mode 100644
index 0000000000..c915d3deb0
--- /dev/null
+++ b/modules/gltf/gltf_template_convert.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* gltf_template_convert.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GLTF_TEMPLATE_CONVERT_H
+#define GLTF_TEMPLATE_CONVERT_H
+
+#include "core/templates/hash_set.h"
+#include "core/variant/array.h"
+#include "core/variant/dictionary.h"
+
+namespace GLTFTemplateConvert {
+template <class T>
+static Array to_array(const Vector<T> &p_inp) {
+ Array ret;
+ for (int i = 0; i < p_inp.size(); i++) {
+ ret.push_back(p_inp[i]);
+ }
+ return ret;
+}
+
+template <class T>
+static Array to_array(const HashSet<T> &p_inp) {
+ Array ret;
+ typename HashSet<T>::Iterator elem = p_inp.begin();
+ while (elem) {
+ ret.push_back(*elem);
+ ++elem;
+ }
+ return ret;
+}
+
+template <class T>
+static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
+ r_out.clear();
+ for (int i = 0; i < p_inp.size(); i++) {
+ r_out.push_back(p_inp[i]);
+ }
+}
+
+template <class T>
+static void set_from_array(HashSet<T> &r_out, const Array &p_inp) {
+ r_out.clear();
+ for (int i = 0; i < p_inp.size(); i++) {
+ r_out.insert(p_inp[i]);
+ }
+}
+
+template <class K, class V>
+static Dictionary to_dict(const HashMap<K, V> &p_inp) {
+ Dictionary ret;
+ for (const KeyValue<K, V> &E : p_inp) {
+ ret[E.key] = E.value;
+ }
+ return ret;
+}
+
+template <class K, class V>
+static void set_from_dict(HashMap<K, V> &r_out, const Dictionary &p_inp) {
+ r_out.clear();
+ Array keys = p_inp.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ r_out[keys[i]] = p_inp[keys[i]];
+ }
+}
+} //namespace GLTFTemplateConvert
+
+#endif // GLTF_TEMPLATE_CONVERT_H
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index b656788a10..1e1204aa57 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -32,21 +32,21 @@
#ifndef _3D_DISABLED
-#include "gltf_accessor.h"
-#include "gltf_animation.h"
-#include "gltf_buffer_view.h"
-#include "gltf_camera.h"
+#include "extensions/gltf_light.h"
+#include "extensions/gltf_spec_gloss.h"
#include "gltf_document.h"
#include "gltf_document_extension.h"
#include "gltf_document_extension_convert_importer_mesh.h"
-#include "gltf_light.h"
-#include "gltf_mesh.h"
-#include "gltf_node.h"
-#include "gltf_skeleton.h"
-#include "gltf_skin.h"
-#include "gltf_spec_gloss.h"
#include "gltf_state.h"
-#include "gltf_texture.h"
+#include "structures/gltf_accessor.h"
+#include "structures/gltf_animation.h"
+#include "structures/gltf_buffer_view.h"
+#include "structures/gltf_camera.h"
+#include "structures/gltf_mesh.h"
+#include "structures/gltf_node.h"
+#include "structures/gltf_skeleton.h"
+#include "structures/gltf_skin.h"
+#include "structures/gltf_texture.h"
#ifdef TOOLS_ENABLED
#include "core/config/project_settings.h"
@@ -101,45 +101,52 @@ static void _editor_init() {
}
#endif // TOOLS_ENABLED
-void register_gltf_types() {
- // glTF API available at runtime.
- GDREGISTER_CLASS(GLTFAccessor);
- GDREGISTER_CLASS(GLTFAnimation);
- GDREGISTER_CLASS(GLTFBufferView);
- GDREGISTER_CLASS(GLTFCamera);
- GDREGISTER_CLASS(GLTFDocument);
- GDREGISTER_CLASS(GLTFDocumentExtension);
- GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh);
- GDREGISTER_CLASS(GLTFLight);
- GDREGISTER_CLASS(GLTFMesh);
- GDREGISTER_CLASS(GLTFNode);
- GDREGISTER_CLASS(GLTFSkeleton);
- GDREGISTER_CLASS(GLTFSkin);
- GDREGISTER_CLASS(GLTFSpecGloss);
- GDREGISTER_CLASS(GLTFState);
- GDREGISTER_CLASS(GLTFTexture);
+void initialize_gltf_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ // glTF API available at runtime.
+ GDREGISTER_CLASS(GLTFAccessor);
+ GDREGISTER_CLASS(GLTFAnimation);
+ GDREGISTER_CLASS(GLTFBufferView);
+ GDREGISTER_CLASS(GLTFCamera);
+ GDREGISTER_CLASS(GLTFDocument);
+ GDREGISTER_CLASS(GLTFDocumentExtension);
+ GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh);
+ GDREGISTER_CLASS(GLTFLight);
+ GDREGISTER_CLASS(GLTFMesh);
+ GDREGISTER_CLASS(GLTFNode);
+ GDREGISTER_CLASS(GLTFSkeleton);
+ GDREGISTER_CLASS(GLTFSkin);
+ GDREGISTER_CLASS(GLTFSpecGloss);
+ GDREGISTER_CLASS(GLTFState);
+ GDREGISTER_CLASS(GLTFTexture);
+ }
#ifdef TOOLS_ENABLED
- // Editor-specific API.
- ClassDB::APIType prev_api = ClassDB::get_current_api();
- ClassDB::set_current_api(ClassDB::API_EDITOR);
-
- GDREGISTER_CLASS(EditorSceneFormatImporterGLTF);
- EditorPlugins::add_by_type<SceneExporterGLTFPlugin>();
-
- // Project settings defined here so doctool finds them.
- GLOBAL_DEF_RST("filesystem/import/blender/enabled", true);
- GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
- GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
- GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
-
- ClassDB::set_current_api(prev_api);
- EditorNode::add_init_callback(_editor_init);
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ // Editor-specific API.
+ ClassDB::APIType prev_api = ClassDB::get_current_api();
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
+
+ GDREGISTER_CLASS(EditorSceneFormatImporterGLTF);
+ EditorPlugins::add_by_type<SceneExporterGLTFPlugin>();
+
+ // Project settings defined here so doctool finds them.
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled", true);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
+ GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
+ GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
+
+ ClassDB::set_current_api(prev_api);
+ EditorNode::add_init_callback(_editor_init);
+ }
#endif // TOOLS_ENABLED
}
-void unregister_gltf_types() {
+void uninitialize_gltf_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
#endif // _3D_DISABLED
diff --git a/modules/gltf/register_types.h b/modules/gltf/register_types.h
index 4a9c31241c..90b9a83c88 100644
--- a/modules/gltf/register_types.h
+++ b/modules/gltf/register_types.h
@@ -28,5 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_gltf_types();
-void unregister_gltf_types();
+#include "modules/register_module_types.h"
+
+void initialize_gltf_module(ModuleInitializationLevel p_level);
+void uninitialize_gltf_module(ModuleInitializationLevel p_level);
diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp
index 1daf2f90a7..1b8911fe72 100644
--- a/modules/gltf/gltf_accessor.cpp
+++ b/modules/gltf/structures/gltf_accessor.cpp
@@ -30,8 +30,6 @@
#include "gltf_accessor.h"
-#include "gltf_document_extension.h"
-
void GLTFAccessor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view);
ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view);
@@ -67,7 +65,7 @@ void GLTFAccessor::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "component_type"), "set_component_type", "get_component_type"); // int
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalized"), "set_normalized", "get_normalized"); // bool
ADD_PROPERTY(PropertyInfo(Variant::INT, "count"), "set_count", "get_count"); // int
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFDocument::GLTFType
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFType
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector<real_t>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector<real_t>
ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_count"), "set_sparse_count", "get_sparse_count"); // int
@@ -123,7 +121,7 @@ int GLTFAccessor::get_type() {
}
void GLTFAccessor::set_type(int p_type) {
- type = (GLTFDocument::GLTFType)p_type; // TODO: Register enum
+ type = (GLTFType)p_type; // TODO: Register enum
}
Vector<double> GLTFAccessor::get_min() {
diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h
index 6bf1bf543a..bfb71d57fe 100644
--- a/modules/gltf/gltf_accessor.h
+++ b/modules/gltf/structures/gltf_accessor.h
@@ -33,8 +33,7 @@
#include "core/io/resource.h"
-#include "gltf_document.h"
-#include "gltf_document_extension.h"
+#include "../gltf_defines.h"
struct GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
@@ -46,7 +45,7 @@ private:
int component_type = 0;
bool normalized = false;
int count = 0;
- GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ GLTFType type = GLTFType::TYPE_SCALAR;
Vector<double> min;
Vector<double> max;
int sparse_count = 0;
@@ -102,4 +101,5 @@ public:
int get_sparse_values_byte_offset();
void set_sparse_values_byte_offset(int p_sparse_values_byte_offset);
};
+
#endif // GLTF_ACCESSOR_H
diff --git a/modules/gltf/gltf_animation.cpp b/modules/gltf/structures/gltf_animation.cpp
index c857be4b2c..e598c870ab 100644
--- a/modules/gltf/gltf_animation.cpp
+++ b/modules/gltf/structures/gltf_animation.cpp
@@ -45,7 +45,7 @@ void GLTFAnimation::set_loop(bool p_val) {
loop = p_val;
}
-Map<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
+HashMap<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
return tracks;
}
diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/structures/gltf_animation.h
index ba8ae8a273..3777f579f6 100644
--- a/modules/gltf/gltf_animation.h
+++ b/modules/gltf/structures/gltf_animation.h
@@ -64,11 +64,12 @@ public:
public:
bool get_loop() const;
void set_loop(bool p_val);
- Map<int, GLTFAnimation::Track> &get_tracks();
+ HashMap<int, GLTFAnimation::Track> &get_tracks();
GLTFAnimation();
private:
bool loop = false;
- Map<int, Track> tracks;
+ HashMap<int, Track> tracks;
};
+
#endif // GLTF_ANIMATION_H
diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/structures/gltf_buffer_view.cpp
index fc467367c6..ba19ed8628 100644
--- a/modules/gltf/gltf_buffer_view.cpp
+++ b/modules/gltf/structures/gltf_buffer_view.cpp
@@ -30,7 +30,7 @@
#include "gltf_buffer_view.h"
-#include "gltf_document_extension.h"
+#include "../gltf_document_extension.h"
void GLTFBufferView::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
diff --git a/modules/gltf/gltf_buffer_view.h b/modules/gltf/structures/gltf_buffer_view.h
index 560d56f35c..b1f500de25 100644
--- a/modules/gltf/gltf_buffer_view.h
+++ b/modules/gltf/structures/gltf_buffer_view.h
@@ -31,8 +31,8 @@
#ifndef GLTF_BUFFER_VIEW_H
#define GLTF_BUFFER_VIEW_H
+#include "../gltf_defines.h"
#include "core/io/resource.h"
-#include "gltf_document.h"
class GLTFBufferView : public Resource {
GDCLASS(GLTFBufferView, Resource);
@@ -65,4 +65,5 @@ public:
void set_indices(bool p_indices);
// matrices need to be transformed to this
};
+
#endif // GLTF_BUFFER_VIEW_H
diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp
index f3ea6a1c4c..f3ea6a1c4c 100644
--- a/modules/gltf/gltf_camera.cpp
+++ b/modules/gltf/structures/gltf_camera.cpp
diff --git a/modules/gltf/gltf_camera.h b/modules/gltf/structures/gltf_camera.h
index c696d4cc6b..b7df741825 100644
--- a/modules/gltf/gltf_camera.h
+++ b/modules/gltf/structures/gltf_camera.h
@@ -55,4 +55,5 @@ public:
float get_depth_near() const { return depth_near; }
void set_depth_near(float p_val) { depth_near = p_val; }
};
+
#endif // GLTF_CAMERA_H
diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/structures/gltf_mesh.cpp
index 3add8304b1..3add8304b1 100644
--- a/modules/gltf/gltf_mesh.cpp
+++ b/modules/gltf/structures/gltf_mesh.cpp
diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/structures/gltf_mesh.h
index dc26120b48..dc26120b48 100644
--- a/modules/gltf/gltf_mesh.h
+++ b/modules/gltf/structures/gltf_mesh.h
diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp
index 86280603fa..86280603fa 100644
--- a/modules/gltf/gltf_node.cpp
+++ b/modules/gltf/structures/gltf_node.cpp
diff --git a/modules/gltf/gltf_node.h b/modules/gltf/structures/gltf_node.h
index 929ad3eca0..1a57ea32e2 100644
--- a/modules/gltf/gltf_node.h
+++ b/modules/gltf/structures/gltf_node.h
@@ -31,8 +31,8 @@
#ifndef GLTF_NODE_H
#define GLTF_NODE_H
+#include "../gltf_defines.h"
#include "core/io/resource.h"
-#include "gltf_document.h"
class GLTFNode : public Resource {
GDCLASS(GLTFNode, Resource);
@@ -97,4 +97,5 @@ public:
GLTFLightIndex get_light();
void set_light(GLTFLightIndex p_light);
};
+
#endif // GLTF_NODE_H
diff --git a/modules/gltf/gltf_skeleton.cpp b/modules/gltf/structures/gltf_skeleton.cpp
index e80376f130..90a6b0f50f 100644
--- a/modules/gltf/gltf_skeleton.cpp
+++ b/modules/gltf/structures/gltf_skeleton.cpp
@@ -30,6 +30,9 @@
#include "gltf_skeleton.h"
+#include "../gltf_template_convert.h"
+#include "scene/3d/bone_attachment_3d.h"
+
void GLTFSkeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_joints"), &GLTFSkeleton::get_joints);
ClassDB::bind_method(D_METHOD("set_joints", "joints"), &GLTFSkeleton::set_joints);
@@ -46,7 +49,7 @@ void GLTFSkeleton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // Map<int32_t,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // RBMap<int32_t,
}
Vector<GLTFNodeIndex> GLTFSkeleton::get_joints() {
@@ -70,19 +73,19 @@ Skeleton3D *GLTFSkeleton::get_godot_skeleton() {
}
Array GLTFSkeleton::get_unique_names() {
- return GLTFDocument::to_array(unique_names);
+ return GLTFTemplateConvert::to_array(unique_names);
}
void GLTFSkeleton::set_unique_names(Array p_unique_names) {
- GLTFDocument::set_from_array(unique_names, p_unique_names);
+ GLTFTemplateConvert::set_from_array(unique_names, p_unique_names);
}
Dictionary GLTFSkeleton::get_godot_bone_node() {
- return GLTFDocument::to_dict(godot_bone_node);
+ return GLTFTemplateConvert::to_dict(godot_bone_node);
}
void GLTFSkeleton::set_godot_bone_node(Dictionary p_indict) {
- GLTFDocument::set_from_dict(godot_bone_node, p_indict);
+ GLTFTemplateConvert::set_from_dict(godot_bone_node, p_indict);
}
BoneAttachment3D *GLTFSkeleton::get_bone_attachment(int idx) {
diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/structures/gltf_skeleton.h
index 7d07d528cb..db88623213 100644
--- a/modules/gltf/gltf_skeleton.h
+++ b/modules/gltf/structures/gltf_skeleton.h
@@ -31,8 +31,8 @@
#ifndef GLTF_SKELETON_H
#define GLTF_SKELETON_H
+#include "../gltf_defines.h"
#include "core/io/resource.h"
-#include "gltf_document.h"
class GLTFSkeleton : public Resource {
GDCLASS(GLTFSkeleton, Resource);
@@ -50,9 +50,9 @@ private:
Skeleton3D *godot_skeleton = nullptr;
// Set of unique bone names for the skeleton
- Set<String> unique_names;
+ HashSet<String> unique_names;
- Map<int32_t, GLTFNodeIndex> godot_bone_node;
+ HashMap<int32_t, GLTFNodeIndex> godot_bone_node;
Vector<BoneAttachment3D *> bone_attachments;
@@ -78,10 +78,10 @@ public:
Array get_unique_names();
void set_unique_names(Array p_unique_names);
- //Map<int32_t, GLTFNodeIndex> get_godot_bone_node() {
+ //RBMap<int32_t, GLTFNodeIndex> get_godot_bone_node() {
// return this->godot_bone_node;
//}
- //void set_godot_bone_node(Map<int32_t, GLTFNodeIndex> p_godot_bone_node) {
+ //void set_godot_bone_node(RBMap<int32_t, GLTFNodeIndex> p_godot_bone_node) {
// this->godot_bone_node = p_godot_bone_node;
//}
Dictionary get_godot_bone_node();
@@ -98,4 +98,5 @@ public:
int32_t get_bone_attachment_count();
};
+
#endif // GLTF_SKELETON_H
diff --git a/modules/gltf/gltf_skin.cpp b/modules/gltf/structures/gltf_skin.cpp
index 283fc34ff5..2e46ee3be2 100644
--- a/modules/gltf/gltf_skin.cpp
+++ b/modules/gltf/structures/gltf_skin.cpp
@@ -30,6 +30,9 @@
#include "gltf_skin.h"
+#include "../gltf_template_convert.h"
+#include "scene/resources/skin.h"
+
void GLTFSkin::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_skin_root"), &GLTFSkin::get_skin_root);
ClassDB::bind_method(D_METHOD("set_skin_root", "skin_root"), &GLTFSkin::set_skin_root);
@@ -59,8 +62,8 @@ void GLTFSkin::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // int
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // Map<int,
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // Map<int,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // RBMap<int,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // RBMap<int,
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "godot_skin"), "set_godot_skin", "get_godot_skin"); // Ref<Skin>
}
@@ -81,11 +84,11 @@ void GLTFSkin::set_joints_original(Vector<GLTFNodeIndex> p_joints_original) {
}
Array GLTFSkin::get_inverse_binds() {
- return GLTFDocument::to_array(inverse_binds);
+ return GLTFTemplateConvert::to_array(inverse_binds);
}
void GLTFSkin::set_inverse_binds(Array p_inverse_binds) {
- GLTFDocument::set_from_array(inverse_binds, p_inverse_binds);
+ GLTFTemplateConvert::set_from_array(inverse_binds, p_inverse_binds);
}
Vector<GLTFNodeIndex> GLTFSkin::get_joints() {
@@ -121,25 +124,25 @@ void GLTFSkin::set_skeleton(int p_skeleton) {
}
Dictionary GLTFSkin::get_joint_i_to_bone_i() {
- return GLTFDocument::to_dict(joint_i_to_bone_i);
+ return GLTFTemplateConvert::to_dict(joint_i_to_bone_i);
}
void GLTFSkin::set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i) {
- GLTFDocument::set_from_dict(joint_i_to_bone_i, p_joint_i_to_bone_i);
+ GLTFTemplateConvert::set_from_dict(joint_i_to_bone_i, p_joint_i_to_bone_i);
}
Dictionary GLTFSkin::get_joint_i_to_name() {
Dictionary ret;
- Map<int, StringName>::Element *elem = joint_i_to_name.front();
+ HashMap<int, StringName>::Iterator elem = joint_i_to_name.begin();
while (elem) {
- ret[elem->key()] = String(elem->value());
- elem = elem->next();
+ ret[elem->key] = String(elem->value);
+ ++elem;
}
return ret;
}
void GLTFSkin::set_joint_i_to_name(Dictionary p_joint_i_to_name) {
- joint_i_to_name = Map<int, StringName>();
+ joint_i_to_name = HashMap<int, StringName>();
Array keys = p_joint_i_to_name.keys();
for (int i = 0; i < keys.size(); i++) {
joint_i_to_name[keys[i]] = p_joint_i_to_name[keys[i]];
diff --git a/modules/gltf/gltf_skin.h b/modules/gltf/structures/gltf_skin.h
index 31cb892f19..59b6a300ac 100644
--- a/modules/gltf/gltf_skin.h
+++ b/modules/gltf/structures/gltf_skin.h
@@ -31,8 +31,8 @@
#ifndef GLTF_SKIN_H
#define GLTF_SKIN_H
+#include "../gltf_defines.h"
#include "core/io/resource.h"
-#include "gltf_document.h"
class GLTFSkin : public Resource {
GDCLASS(GLTFSkin, Resource);
@@ -65,8 +65,8 @@ private:
// A mapping from the joint indices (in the order of joints_original) to the
// Godot Skeleton's bone_indices
- Map<int, int> joint_i_to_bone_i;
- Map<int, StringName> joint_i_to_name;
+ HashMap<int, int> joint_i_to_bone_i;
+ HashMap<int, StringName> joint_i_to_name;
// The Actual Skin that will be created as a mapping between the IBM's of
// this skin to the generated skeleton for the mesh instances.
@@ -106,4 +106,5 @@ public:
Ref<Skin> get_godot_skin();
void set_godot_skin(Ref<Skin> p_godot_skin);
};
+
#endif // GLTF_SKIN_H
diff --git a/modules/gltf/gltf_texture.cpp b/modules/gltf/structures/gltf_texture.cpp
index 2a21cb3df8..2a21cb3df8 100644
--- a/modules/gltf/gltf_texture.cpp
+++ b/modules/gltf/structures/gltf_texture.cpp
diff --git a/modules/gltf/gltf_texture.h b/modules/gltf/structures/gltf_texture.h
index 54dd61f9a5..b1d12dddfa 100644
--- a/modules/gltf/gltf_texture.h
+++ b/modules/gltf/structures/gltf_texture.h
@@ -31,8 +31,8 @@
#ifndef GLTF_TEXTURE_H
#define GLTF_TEXTURE_H
+#include "../gltf_defines.h"
#include "core/io/resource.h"
-#include "gltf_document.h"
class GLTFTexture : public Resource {
GDCLASS(GLTFTexture, Resource);
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 407ce961c8..499f54e3ba 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -73,6 +73,13 @@
Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
</description>
</method>
+ <method name="get_navigation_layer_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32.
+ </description>
+ </method>
<method name="get_used_cells" qualifiers="const">
<return type="Array" />
<description>
@@ -133,6 +140,14 @@
Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_layer_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
+ <description>
+ Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32.
+ </description>
+ </method>
<method name="world_to_map" qualifiers="const">
<return type="Vector3i" />
<argument index="0" name="world_position" type="Vector3" />
@@ -177,7 +192,7 @@
The assigned [MeshLibrary].
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
- The navigation layers the GridMap generates its navigable regions in.
+ A bitmask determining all navigation layers the GridMap generated navigation regions belong to. These navigation layers can be checked upon when requesting a path with [method NavigationServer3D.map_get_path].
</member>
<member name="physics_material" type="PhysicsMaterial" setter="set_physics_material" getter="get_physics_material">
Overrides the default friction and bounce physics properties for the whole [GridMap].
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 68968325dd..09f0ff32f0 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -545,7 +545,7 @@ void GridMapEditor::_update_paste_indicator() {
Basis rot;
rot.set_orthogonal_index(paste_indicator.orientation);
xf.basis = rot * xf.basis;
- xf.translate((-center * node->get_cell_size()) / scale);
+ xf.translate_local((-center * node->get_cell_size()) / scale);
RenderingServer::get_singleton()->instance_set_transform(paste_instance, node->get_global_transform() * xf);
@@ -553,7 +553,7 @@ void GridMapEditor::_update_paste_indicator() {
xf = Transform3D();
xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
xf.basis = rot * xf.basis;
- xf.translate(item.grid_offset * node->get_cell_size());
+ xf.translate_local(item.grid_offset * node->get_cell_size());
Basis item_rot;
item_rot.set_orthogonal_index(item.orientation);
@@ -930,6 +930,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
}
update_palette();
+ _update_cursor_instance();
set_process(true);
@@ -957,7 +958,7 @@ void GridMapEditor::update_grid() {
}
void GridMapEditor::_draw_grids(const Vector3 &cell_size) {
- Vector3 edited_floor = node->has_meta("_editor_floor_") ? node->get_meta("_editor_floor_") : Variant();
+ Vector3 edited_floor = node->get_meta("_editor_floor_", Vector3());
for (int i = 0; i < 3; i++) {
RS::get_singleton()->mesh_clear(grid[i]);
@@ -1220,7 +1221,7 @@ GridMapEditor::GridMapEditor() {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
- search_box->set_placeholder(TTR("Filter meshes"));
+ search_box->set_placeholder(TTR("Filter Meshes"));
hb->add_child(search_box);
search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed));
search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input));
@@ -1230,14 +1231,14 @@ GridMapEditor::GridMapEditor() {
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
hb->add_child(mode_thumbnail);
- mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
+ mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_THUMBNAIL));
mode_list = memnew(Button);
mode_list->set_flat(true);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
hb->add_child(mode_list);
- mode_list->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_LIST));
+ mode_list->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_LIST));
size_slider = memnew(HSlider);
size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1259,9 +1260,9 @@ GridMapEditor::GridMapEditor() {
info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes."));
info_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- info_message->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
mesh_library_palette->add_child(info_message);
edit_axis = Vector3::AXIS_Y;
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 02fe4d93de..7d80cbef7c 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -103,9 +103,10 @@ bool GridMap::_get(const StringName &p_name, Variant &r_ret) const {
{
int *w = cells.ptrw();
int i = 0;
- for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next(), i++) {
- encode_uint64(E->key().key, (uint8_t *)&w[i * 3]);
- encode_uint32(E->get().cell, (uint8_t *)&w[i * 3 + 2]);
+ for (const KeyValue<IndexKey, Cell> &E : cell_map) {
+ encode_uint64(E.key.key, (uint8_t *)&w[i * 3]);
+ encode_uint32(E.value.cell, (uint8_t *)&w[i * 3 + 2]);
+ i++;
}
}
@@ -225,15 +226,33 @@ bool GridMap::is_baking_navigation() {
return bake_navigation;
}
-void GridMap::set_navigation_layers(uint32_t p_layers) {
- navigation_layers = p_layers;
+void GridMap::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
_recreate_octant_data();
}
-uint32_t GridMap::get_navigation_layers() {
+uint32_t GridMap::get_navigation_layers() const {
return navigation_layers;
}
+void GridMap::set_navigation_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive.");
+ uint32_t _navigation_layers = get_navigation_layers();
+ if (p_value) {
+ _navigation_layers |= 1 << (p_layer_number - 1);
+ } else {
+ _navigation_layers &= ~(1 << (p_layer_number - 1));
+ }
+ set_navigation_layers(_navigation_layers);
+}
+
+bool GridMap::get_navigation_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive.");
+ return get_navigation_layers() & (1 << (p_layer_number - 1));
+}
+
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
if (!mesh_library.is_null()) {
mesh_library->unregister_owner(this);
@@ -432,6 +451,18 @@ void GridMap::_octant_transform(const OctantKey &p_key) {
RS::get_singleton()->instance_set_transform(g.collision_debug_instance, get_global_transform());
}
+ // update transform for NavigationServer regions and navigation debugmesh instances
+ for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ if (bake_navigation) {
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_transform(E.value.region, get_global_transform() * E.value.xform);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(E.value.navmesh_debug_instance, get_global_transform() * E.value.xform);
+ }
+ }
+ }
+
for (int i = 0; i < g.multimesh_instances.size(); i++) {
RS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
}
@@ -455,6 +486,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
//erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -480,17 +514,17 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
* and set said multimesh bounding box to one containing all cells which have this item
*/
- Map<int, List<Pair<Transform3D, IndexKey>>> multimesh_items;
+ HashMap<int, List<Pair<Transform3D, IndexKey>>> multimesh_items;
- for (Set<IndexKey>::Element *E = g.cells.front(); E; E = E->next()) {
- ERR_CONTINUE(!cell_map.has(E->get()));
- const Cell &c = cell_map[E->get()];
+ for (const IndexKey &E : g.cells) {
+ ERR_CONTINUE(!cell_map.has(E));
+ const Cell &c = cell_map[E];
if (!mesh_library.is_valid() || !mesh_library->has_item(c.item)) {
continue;
}
- Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z);
+ Vector3 cellpos = Vector3(E.x, E.y, E.z);
Vector3 ofs = _get_offset();
Transform3D xform;
@@ -506,7 +540,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Pair<Transform3D, IndexKey> p;
p.first = xform * mesh_library->get_item_mesh_transform(c.item);
- p.second = E->get();
+ p.second = E;
multimesh_items[c.item].push_back(p);
}
}
@@ -532,14 +566,29 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
if (bake_navigation) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh);
- NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * mesh_library->get_item_navmesh_transform(c.item));
+ NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
nm.region = region;
+
+ // add navigation debugmesh visual instances if debug is enabled
+ SceneTree *st = SceneTree::get_singleton();
+ if (st && st->is_debugging_navigation_hint()) {
+ if (!nm.navmesh_debug_instance.is_valid()) {
+ RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid();
+ nm.navmesh_debug_instance = RS::get_singleton()->instance_create();
+ RS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid);
+ RS::get_singleton()->mesh_surface_set_material(navmesh_debug_rid, 0, st->get_debug_navigation_material()->get_rid());
+ }
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform);
+ }
+ }
}
- g.navmesh_ids[E->get()] = nm;
+ g.navmesh_ids[E] = nm;
}
}
@@ -628,7 +677,7 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F.key].item);
if (nm.is_valid()) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, nm);
NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F.value.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
@@ -659,6 +708,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
NavigationServer3D::get_singleton()->free(F.value.region);
F.value.region = RID();
}
+ if (F.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(F.value.navmesh_debug_instance);
+ F.value.navmesh_debug_instance = RID();
+ }
}
}
@@ -677,7 +730,12 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
// Erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
- NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -770,7 +828,7 @@ void GridMap::_queue_octants_dirty() {
void GridMap::_recreate_octant_data() {
recreating_octants = true;
- Map<IndexKey, Cell> cell_copy = cell_map;
+ HashMap<IndexKey, Cell, IndexKey> cell_copy = cell_map;
_clear_internal();
for (const KeyValue<IndexKey, Cell> &E : cell_copy) {
set_cell_item(Vector3i(E.key), E.value.item, E.value.rot);
@@ -797,7 +855,7 @@ void GridMap::clear() {
clear_baked_meshes();
}
-void GridMap::resource_changed(const RES &p_res) {
+void GridMap::resource_changed(const Ref<Resource> &p_res) {
_recreate_octant_data();
}
@@ -845,6 +903,9 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layers", "layers"), &GridMap::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers);
+ ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &GridMap::set_navigation_layer_value);
+ ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &GridMap::get_navigation_layer_value);
+
ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library);
ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library);
@@ -889,7 +950,7 @@ void GridMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
ADD_GROUP("Cell", "cell_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size", PROPERTY_HINT_NONE, "suffix:m"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_octant_size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_octant_size", "get_octant_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_x"), "set_center_x", "get_center_x");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_y"), "set_center_y", "get_center_y");
@@ -969,7 +1030,7 @@ Array GridMap::get_meshes() const {
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
- meshes.push_back(xform);
+ meshes.push_back(xform * mesh_library->get_item_mesh_transform(id));
meshes.push_back(mesh);
}
@@ -998,7 +1059,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
}
//generate
- Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>> surface_map;
+ HashMap<OctantKey, HashMap<Ref<Material>, Ref<SurfaceTool>>, OctantKey> surface_map;
for (KeyValue<IndexKey, Cell> &E : cell_map) {
IndexKey key = E.key;
@@ -1028,10 +1089,10 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
ok.z = key.z / octant_size;
if (!surface_map.has(ok)) {
- surface_map[ok] = Map<Ref<Material>, Ref<SurfaceTool>>();
+ surface_map[ok] = HashMap<Ref<Material>, Ref<SurfaceTool>>();
}
- Map<Ref<Material>, Ref<SurfaceTool>> &mat_map = surface_map[ok];
+ HashMap<Ref<Material>, Ref<SurfaceTool>> &mat_map = surface_map[ok];
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
@@ -1051,7 +1112,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
}
}
- for (KeyValue<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>> &E : surface_map) {
+ for (KeyValue<OctantKey, HashMap<Ref<Material>, Ref<SurfaceTool>>> &E : surface_map) {
Ref<ArrayMesh> mesh;
mesh.instantiate();
for (KeyValue<Ref<Material>, Ref<SurfaceTool>> &F : E.value) {
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index b09cabfe25..078a1d9de5 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -56,9 +56,15 @@ class GridMap : public Node3D {
};
uint64_t key = 0;
+ static uint32_t hash(const IndexKey &p_key) {
+ return hash_one_uint64(p_key.key);
+ }
_FORCE_INLINE_ bool operator<(const IndexKey &p_key) const {
return key < p_key.key;
}
+ _FORCE_INLINE_ bool operator==(const IndexKey &p_key) const {
+ return key == p_key.key;
+ }
_FORCE_INLINE_ operator Vector3i() const {
return Vector3i(x, y, z);
@@ -92,6 +98,7 @@ class GridMap : public Node3D {
struct NavMesh {
RID region;
Transform3D xform;
+ RID navmesh_debug_instance;
};
struct MultimeshInstance {
@@ -107,13 +114,13 @@ class GridMap : public Node3D {
};
Vector<MultimeshInstance> multimesh_instances;
- Set<IndexKey> cells;
+ HashSet<IndexKey> cells;
RID collision_debug;
RID collision_debug_instance;
bool dirty = false;
RID static_body;
- Map<IndexKey, NavMesh> navmesh_ids;
+ HashMap<IndexKey, NavMesh> navmesh_ids;
};
union OctantKey {
@@ -126,8 +133,11 @@ class GridMap : public Node3D {
uint64_t key = 0;
- _FORCE_INLINE_ bool operator<(const OctantKey &p_key) const {
- return key < p_key.key;
+ static uint32_t hash(const OctantKey &p_key) {
+ return hash_one_uint64(p_key.key);
+ }
+ _FORCE_INLINE_ bool operator==(const OctantKey &p_key) const {
+ return key == p_key.key;
}
//OctantKey(const IndexKey& p_k, int p_item) { indexkey=p_k.key; item=p_item; }
@@ -154,8 +164,8 @@ class GridMap : public Node3D {
Ref<MeshLibrary> mesh_library;
- Map<OctantKey, Octant *> octant_map;
- Map<IndexKey, Cell> cell_map;
+ HashMap<OctantKey, Octant *, OctantKey> octant_map;
+ HashMap<IndexKey, Cell, IndexKey> cell_map;
void _recreate_octant_data();
@@ -181,7 +191,7 @@ class GridMap : public Node3D {
void _queue_octants_dirty();
void _update_octants_callback();
- void resource_changed(const RES &p_res);
+ void resource_changed(const Ref<Resource> &p_res);
void _clear_internal();
@@ -228,8 +238,11 @@ public:
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
- void set_navigation_layers(uint32_t p_layers);
- uint32_t get_navigation_layers();
+ void set_navigation_layers(uint32_t p_navigation_layers);
+ uint32_t get_navigation_layers() const;
+
+ void set_navigation_layer_value(int p_layer_number, bool p_value);
+ bool get_navigation_layer_value(int p_layer_number) const;
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
Ref<MeshLibrary> get_mesh_library() const;
diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp
index d7c9f5c92e..9efd18a265 100644
--- a/modules/gridmap/register_types.cpp
+++ b/modules/gridmap/register_types.cpp
@@ -39,14 +39,21 @@
#include "editor/grid_map_editor_plugin.h"
#endif
-void register_gridmap_types() {
- GDREGISTER_CLASS(GridMap);
+void initialize_gridmap_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(GridMap);
+ }
#ifdef TOOLS_ENABLED
- EditorPlugins::add_by_type<GridMapEditorPlugin>();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<GridMapEditorPlugin>();
+ }
#endif
}
-void unregister_gridmap_types() {
+void uninitialize_gridmap_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
#endif // _3D_DISABLED
diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h
index fa3511c5d1..28f14cd398 100644
--- a/modules/gridmap/register_types.h
+++ b/modules/gridmap/register_types.h
@@ -31,7 +31,9 @@
#ifndef GRIDMAP_REGISTER_TYPES_H
#define GRIDMAP_REGISTER_TYPES_H
-void register_gridmap_types();
-void unregister_gridmap_types();
+#include "modules/register_module_types.h"
+
+void initialize_gridmap_module(ModuleInitializationLevel p_level);
+void uninitialize_gridmap_module(ModuleInitializationLevel p_level);
#endif // GRIDMAP_REGISTER_TYPES_H
diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h
index f2d53cc206..16c0816562 100644
--- a/modules/hdr/image_loader_hdr.h
+++ b/modules/hdr/image_loader_hdr.h
@@ -40,4 +40,4 @@ public:
ImageLoaderHDR();
};
-#endif
+#endif // IMAGE_LOADER_HDR_H
diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp
index 6bfeecc927..b988bf4587 100644
--- a/modules/hdr/register_types.cpp
+++ b/modules/hdr/register_types.cpp
@@ -34,11 +34,19 @@
static ImageLoaderHDR *image_loader_hdr = nullptr;
-void register_hdr_types() {
+void initialize_hdr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
image_loader_hdr = memnew(ImageLoaderHDR);
ImageLoader::add_image_format_loader(image_loader_hdr);
}
-void unregister_hdr_types() {
+void uninitialize_hdr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_hdr);
}
diff --git a/modules/hdr/register_types.h b/modules/hdr/register_types.h
index 4224aa2ce2..0254e43b6c 100644
--- a/modules/hdr/register_types.h
+++ b/modules/hdr/register_types.h
@@ -31,7 +31,9 @@
#ifndef HDR_REGISTER_TYPES_H
#define HDR_REGISTER_TYPES_H
-void register_hdr_types();
-void unregister_hdr_types();
+#include "modules/register_module_types.h"
+
+void initialize_hdr_module(ModuleInitializationLevel p_level);
+void uninitialize_hdr_module(ModuleInitializationLevel p_level);
#endif // HDR_REGISTER_TYPES_H
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 7c6ceeea29..b840542c1b 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -13,6 +13,7 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/jpeg-compressor/"
thirdparty_sources = [
"jpgd.cpp",
+ "jpge.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 51358876a4..0e03fa65c8 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -33,7 +33,8 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include <jpgd.h>
+#include "thirdparty/jpeg-compressor/jpgd.h"
+#include "thirdparty/jpeg-compressor/jpge.h"
#include <string.h>
Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
@@ -131,6 +132,59 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
return img;
}
+static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float p_quality) {
+ return OK;
+}
+
+class ImageLoaderJPGOSFile : public jpge::output_stream {
+public:
+ Ref<FileAccess> f;
+
+ virtual bool put_buf(const void *Pbuf, int len) {
+ f->store_buffer((const uint8_t *)Pbuf, len);
+ return true;
+ }
+};
+
+class ImageLoaderJPGOSBuffer : public jpge::output_stream {
+public:
+ Vector<uint8_t> *buffer = nullptr;
+ virtual bool put_buf(const void *Pbuf, int len) {
+ uint32_t base = buffer->size();
+ buffer->resize(base + len);
+ memcpy(buffer->ptrw() + base, Pbuf, len);
+ return true;
+ }
+};
+
+static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
+ ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), Vector<uint8_t>());
+ Ref<Image> image = p_img;
+ if (image->get_format() != Image::FORMAT_RGB8) {
+ image->convert(Image::FORMAT_ETC2_RGB8);
+ }
+
+ jpge::params p;
+ p.m_quality = CLAMP(p_quality * 100, 1, 100);
+ Vector<uint8_t> output;
+ ImageLoaderJPGOSBuffer ob;
+ ob.buffer = &output;
+
+ jpge::jpeg_encoder enc;
+ enc.init(&ob, image->get_width(), image->get_height(), 3, p);
+
+ const uint8_t *src_data = image->get_data().ptr();
+ for (int i = 0; i < image->get_height(); i++) {
+ enc.process_scanline(&src_data[i * image->get_width() * 3]);
+ }
+
+ enc.process_scanline(nullptr);
+
+ return output;
+}
+
ImageLoaderJPG::ImageLoaderJPG() {
Image::_jpg_mem_loader_func = _jpegd_mem_loader_func;
+ Image::save_jpg_func = _jpgd_save_func;
+ Image::save_jpg_buffer_func = _jpgd_buffer_save_func;
}
diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h
index de9700faec..6d631446e7 100644
--- a/modules/jpg/image_loader_jpegd.h
+++ b/modules/jpg/image_loader_jpegd.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef IMAGE_LOADER_JPG_H
-#define IMAGE_LOADER_JPG_H
+#ifndef IMAGE_LOADER_JPEGD_H
+#define IMAGE_LOADER_JPEGD_H
#include "core/io/image_loader.h"
@@ -40,4 +40,4 @@ public:
ImageLoaderJPG();
};
-#endif
+#endif // IMAGE_LOADER_JPEGD_H
diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp
index 63203274f4..b8b48a550f 100644
--- a/modules/jpg/register_types.cpp
+++ b/modules/jpg/register_types.cpp
@@ -34,11 +34,19 @@
static ImageLoaderJPG *image_loader_jpg = nullptr;
-void register_jpg_types() {
+void initialize_jpg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
image_loader_jpg = memnew(ImageLoaderJPG);
ImageLoader::add_image_format_loader(image_loader_jpg);
}
-void unregister_jpg_types() {
+void uninitialize_jpg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_jpg);
}
diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h
index 97223cefda..f0a205f6a8 100644
--- a/modules/jpg/register_types.h
+++ b/modules/jpg/register_types.h
@@ -31,7 +31,9 @@
#ifndef JPG_REGISTER_TYPES_H
#define JPG_REGISTER_TYPES_H
-void register_jpg_types();
-void unregister_jpg_types();
+#include "modules/register_module_types.h"
+
+void initialize_jpg_module(ModuleInitializationLevel p_level);
+void uninitialize_jpg_module(ModuleInitializationLevel p_level);
#endif // JPG_REGISTER_TYPES_H
diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h
index 3144746f6d..1d9f2771b2 100644
--- a/modules/jsonrpc/jsonrpc.h
+++ b/modules/jsonrpc/jsonrpc.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GODOT_JSON_RPC_H
-#define GODOT_JSON_RPC_H
+#ifndef JSONRPC_H
+#define JSONRPC_H
#include "core/object/class_db.h"
#include "core/variant/variant.h"
@@ -37,7 +37,7 @@
class JSONRPC : public Object {
GDCLASS(JSONRPC, Object)
- Map<String, Object *> method_scopes;
+ HashMap<String, Object *> method_scopes;
protected:
static void _bind_methods();
@@ -67,4 +67,4 @@ public:
VARIANT_ENUM_CAST(JSONRPC::ErrorCode);
-#endif
+#endif // JSONRPC_H
diff --git a/modules/jsonrpc/register_types.cpp b/modules/jsonrpc/register_types.cpp
index d89b7e9353..6d35d6aeb8 100644
--- a/modules/jsonrpc/register_types.cpp
+++ b/modules/jsonrpc/register_types.cpp
@@ -32,9 +32,16 @@
#include "core/object/class_db.h"
#include "jsonrpc.h"
-void register_jsonrpc_types() {
+void initialize_jsonrpc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_CLASS(JSONRPC);
}
-void unregister_jsonrpc_types() {
+void uninitialize_jsonrpc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/jsonrpc/register_types.h b/modules/jsonrpc/register_types.h
index 57744e6c07..83d315a9eb 100644
--- a/modules/jsonrpc/register_types.h
+++ b/modules/jsonrpc/register_types.h
@@ -31,7 +31,9 @@
#ifndef JSONRPC_REGISTER_TYPES_H
#define JSONRPC_REGISTER_TYPES_H
-void register_jsonrpc_types();
-void unregister_jsonrpc_types();
+#include "modules/register_module_types.h"
+
+void initialize_jsonrpc_module(ModuleInitializationLevel p_level);
+void uninitialize_jsonrpc_module(ModuleInitializationLevel p_level);
#endif // JSONRPC_REGISTER_TYPES_H
diff --git a/modules/lightmapper_rd/SCsub b/modules/lightmapper_rd/SCsub
index 5cc9d8ee8b..fe9737b36f 100644
--- a/modules/lightmapper_rd/SCsub
+++ b/modules/lightmapper_rd/SCsub
@@ -7,9 +7,7 @@ env_lightmapper_rd = env_modules.Clone()
env_lightmapper_rd.GLSL_HEADER("lm_raster.glsl")
env_lightmapper_rd.GLSL_HEADER("lm_compute.glsl")
env_lightmapper_rd.GLSL_HEADER("lm_blendseams.glsl")
-env_lightmapper_rd.Depends("lm_raster.glsl.gen.h", "lm_common_inc.glsl")
-env_lightmapper_rd.Depends("lm_compute.glsl.gen.h", "lm_common_inc.glsl")
-env_lightmapper_rd.Depends("lm_blendseams.glsl.gen.h", "lm_common_inc.glsl")
+env_lightmapper_rd.Depends(Glob("*.glsl.gen.h"), ["lm_common_inc.glsl", "#glsl_builders.py"])
# Godot source files
env_lightmapper_rd.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index faa1d21490..83ac478a97 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -51,7 +51,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi);
}
-void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) {
+void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x;
@@ -62,11 +62,12 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
l.color[2] = p_color.b;
l.energy = p_energy;
l.static_bake = p_static;
- l.size = p_angular_distance;
+ l.size = Math::tan(Math::deg2rad(p_angular_distance));
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
-void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) {
+void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x;
@@ -80,10 +81,11 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy;
l.static_bake = p_static;
l.size = p_size;
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
-void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) {
+void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x;
@@ -102,6 +104,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy;
l.static_bake = p_static;
l.size = p_size;
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
@@ -268,8 +271,8 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
mi.offset.x = best_atlas_offsets[m_i].x;
mi.offset.y = best_atlas_offsets[m_i].y;
mi.slice = best_atlas_offsets[m_i].z;
- albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2(Vector2(), Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height())), mi.offset);
- emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2(), Size2i(mi.data.emission_on_uv2->get_width(), mi.data.emission_on_uv2->get_height())), mi.offset);
+ albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2i(Vector2i(), mi.data.albedo_on_uv2->get_size()), mi.offset);
+ emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2i(), mi.data.emission_on_uv2->get_size()), mi.offset);
}
return BAKE_OK;
@@ -1028,17 +1031,17 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
push_constant.atlas_slice = 0;
push_constant.region_ofs[0] = 0;
push_constant.region_ofs[1] = 0;
- push_constant.environment_xform[0] = p_environment_transform.elements[0][0];
- push_constant.environment_xform[1] = p_environment_transform.elements[1][0];
- push_constant.environment_xform[2] = p_environment_transform.elements[2][0];
+ push_constant.environment_xform[0] = p_environment_transform.rows[0][0];
+ push_constant.environment_xform[1] = p_environment_transform.rows[1][0];
+ push_constant.environment_xform[2] = p_environment_transform.rows[2][0];
push_constant.environment_xform[3] = 0;
- push_constant.environment_xform[4] = p_environment_transform.elements[0][1];
- push_constant.environment_xform[5] = p_environment_transform.elements[1][1];
- push_constant.environment_xform[6] = p_environment_transform.elements[2][1];
+ push_constant.environment_xform[4] = p_environment_transform.rows[0][1];
+ push_constant.environment_xform[5] = p_environment_transform.rows[1][1];
+ push_constant.environment_xform[6] = p_environment_transform.rows[2][1];
push_constant.environment_xform[7] = 0;
- push_constant.environment_xform[8] = p_environment_transform.elements[0][2];
- push_constant.environment_xform[9] = p_environment_transform.elements[1][2];
- push_constant.environment_xform[10] = p_environment_transform.elements[2][2];
+ push_constant.environment_xform[8] = p_environment_transform.rows[0][2];
+ push_constant.environment_xform[9] = p_environment_transform.rows[1][2];
+ push_constant.environment_xform[10] = p_environment_transform.rows[2][2];
push_constant.environment_xform[11] = 0;
}
@@ -1140,6 +1143,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
+ switch (p_quality) {
+ case BAKE_QUALITY_LOW: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_MEDIUM: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_HIGH: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_ULTRA: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
+ } break;
+ }
+
+ push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
+
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
@@ -1230,23 +1250,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
uniforms.write[1].set_id(0, light_dest_tex);
secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
- switch (p_quality) {
- case BAKE_QUALITY_LOW: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
- } break;
- case BAKE_QUALITY_MEDIUM: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
- } break;
- case BAKE_QUALITY_HIGH: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
- } break;
- case BAKE_QUALITY_ULTRA: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
- } break;
- }
-
- push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
-
int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
@@ -1417,7 +1420,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
img2.instantiate();
img2->create(2, 2, false, Image::FORMAT_RGBAF, s);
img2->convert(Image::FORMAT_RGB8);
- img->blit_rect(img2, Rect2(0, 0, 2, 2), Point2((j % 3) * 2, (j / 3) * 2));
+ img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2));
}
img->save_png("res://3_light_probe_" + itos(i) + ".png");
}
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 503f5f7009..88860ad0d4 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -57,8 +57,9 @@ class LightmapperRD : public Lightmapper {
float attenuation = 0.0;
float cos_spot_angle = 0.0;
float inv_spot_attenuation = 0.0;
+ float shadow_blur = 0.0;
uint32_t static_bake = 0;
- uint32_t pad[3] = {};
+ uint32_t pad[2] = {};
bool operator<(const Light &p_light) const {
return type < p_light.type;
@@ -110,12 +111,12 @@ class LightmapperRD : public Lightmapper {
struct EdgeHash {
_FORCE_INLINE_ static uint32_t hash(const Edge &p_edge) {
- uint32_t h = hash_djb2_one_float(p_edge.a.x);
- h = hash_djb2_one_float(p_edge.a.y, h);
- h = hash_djb2_one_float(p_edge.a.z, h);
- h = hash_djb2_one_float(p_edge.b.x, h);
- h = hash_djb2_one_float(p_edge.b.y, h);
- h = hash_djb2_one_float(p_edge.b.z, h);
+ uint32_t h = hash_murmur3_one_float(p_edge.a.x);
+ h = hash_murmur3_one_float(p_edge.a.y, h);
+ h = hash_murmur3_one_float(p_edge.a.z, h);
+ h = hash_murmur3_one_float(p_edge.b.x, h);
+ h = hash_murmur3_one_float(p_edge.b.y, h);
+ h = hash_murmur3_one_float(p_edge.b.z, h);
return h;
}
};
@@ -146,15 +147,15 @@ class LightmapperRD : public Lightmapper {
struct VertexHash {
_FORCE_INLINE_ static uint32_t hash(const Vertex &p_vtx) {
- uint32_t h = hash_djb2_one_float(p_vtx.position[0]);
- h = hash_djb2_one_float(p_vtx.position[1], h);
- h = hash_djb2_one_float(p_vtx.position[2], h);
- h = hash_djb2_one_float(p_vtx.uv[0], h);
- h = hash_djb2_one_float(p_vtx.uv[1], h);
- h = hash_djb2_one_float(p_vtx.normal_xy[0], h);
- h = hash_djb2_one_float(p_vtx.normal_xy[1], h);
- h = hash_djb2_one_float(p_vtx.normal_z, h);
- return h;
+ uint32_t h = hash_murmur3_one_float(p_vtx.position[0]);
+ h = hash_murmur3_one_float(p_vtx.position[1], h);
+ h = hash_murmur3_one_float(p_vtx.position[2], h);
+ h = hash_murmur3_one_float(p_vtx.uv[0], h);
+ h = hash_murmur3_one_float(p_vtx.uv[1], h);
+ h = hash_murmur3_one_float(p_vtx.normal_xy[0], h);
+ h = hash_murmur3_one_float(p_vtx.normal_xy[1], h);
+ h = hash_murmur3_one_float(p_vtx.normal_z, h);
+ return hash_fmix32(h);
}
};
@@ -236,9 +237,9 @@ class LightmapperRD : public Lightmapper {
public:
virtual void add_mesh(const MeshData &p_mesh) override;
- virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) override;
- virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) override;
- virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) override;
+ virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override;
+ virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override;
+ virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_probe(const Vector3 &p_position) override;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override;
@@ -255,4 +256,4 @@ public:
LightmapperRD();
};
-#endif // LIGHTMAPPER_H
+#endif // LIGHTMAPPER_RD_H
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 58523dc1f8..e0e8000f56 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -51,8 +51,9 @@ struct Light {
float cos_spot_angle;
float inv_spot_attenuation;
+ float shadow_blur;
bool static_bake;
- uint pad[3];
+ uint pad[2];
};
layout(set = 0, binding = 4, std430) restrict readonly buffer Lights {
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 0b6b72a310..efa6cd50b4 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -316,19 +316,24 @@ void main() {
for (uint i = 0; i < params.light_count; i++) {
vec3 light_pos;
+ float dist;
float attenuation;
+ float soft_shadowing_disk_size;
if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) {
vec3 light_vec = lights.data[i].direction;
light_pos = position - light_vec * length(params.world_size);
+ dist = length(params.world_size);
attenuation = 1.0;
+ soft_shadowing_disk_size = lights.data[i].size;
} else {
light_pos = lights.data[i].position;
- float d = distance(position, light_pos);
- if (d > lights.data[i].range) {
+ dist = distance(position, light_pos);
+ if (dist > lights.data[i].range) {
continue;
}
+ soft_shadowing_disk_size = lights.data[i].size / dist;
- attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation);
+ attenuation = get_omni_attenuation(dist, 1.0 / lights.data[i].range, lights.data[i].attenuation);
if (lights.data[i].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(position - light_pos);
@@ -352,27 +357,70 @@ void main() {
continue; //no need to do anything
}
- if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) {
- vec3 light = lights.data[i].color * lights.data[i].energy * attenuation;
- if (lights.data[i].static_bake) {
- static_light += light;
-#ifdef USE_SH_LIGHTMAPS
+ float penumbra = 0.0;
+ if (lights.data[i].size > 0.0) {
+ vec3 light_to_point = -light_dir;
+ vec3 aux = light_to_point.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
+ vec3 light_to_point_tan = normalize(cross(light_to_point, aux));
+ vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
+
+ const uint shadowing_rays_check_penumbra_denom = 2;
+ uint shadowing_ray_count = params.ray_count;
+
+ uint hits = 0;
+ uint noise = random_seed(ivec3(atlas_pos, 43573547 /* some prime */));
+ vec3 light_disk_to_point = light_to_point;
+ for (uint j = 0; j < shadowing_ray_count; j++) {
+ // Optimization:
+ // Once already traced an important proportion of rays, if all are hits or misses,
+ // assume we're not in the penumbra so we can infer the rest would have the same result
+ if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) {
+ if (hits == j) {
+ // Assume totally lit
+ hits = shadowing_ray_count;
+ break;
+ } else if (hits == 0) {
+ // Assume totally dark
+ hits = 0;
+ break;
+ }
+ }
- float c[4] = float[](
- 0.282095, //l0
- 0.488603 * light_dir.y, //l1n1
- 0.488603 * light_dir.z, //l1n0
- 0.488603 * light_dir.x //l1p1
- );
+ float r = randomize(noise);
+ float a = randomize(noise) * 2.0 * PI;
+ vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * lights.data[i].shadow_blur;
+ light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
- for (uint j = 0; j < 4; j++) {
- sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
+ if (trace_ray(position - light_disk_to_point * params.bias, position - light_disk_to_point * dist) == RAY_MISS) {
+ hits++;
}
-#endif
+ }
+ penumbra = float(hits) / float(shadowing_ray_count);
+ } else {
+ if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) {
+ penumbra = 1.0;
+ }
+ }
- } else {
- dynamic_light += light;
+ vec3 light = lights.data[i].color * lights.data[i].energy * attenuation * penumbra;
+ if (lights.data[i].static_bake) {
+ static_light += light;
+#ifdef USE_SH_LIGHTMAPS
+
+ float c[4] = float[](
+ 0.282095, //l0
+ 0.488603 * light_dir.y, //l1n1
+ 0.488603 * light_dir.z, //l1n0
+ 0.488603 * light_dir.x //l1p1
+ );
+
+ for (uint j = 0; j < 4; j++) {
+ sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
}
+#endif
+
+ } else {
+ dynamic_light += light;
}
}
diff --git a/modules/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp
index 0a96a86076..0e0330c1a1 100644
--- a/modules/lightmapper_rd/register_types.cpp
+++ b/modules/lightmapper_rd/register_types.cpp
@@ -40,7 +40,11 @@ static Lightmapper *create_lightmapper_rd() {
}
#endif
-void register_lightmapper_rd_types() {
+void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GLOBAL_DEF("rendering/lightmapping/bake_quality/low_quality_ray_count", 16);
GLOBAL_DEF("rendering/lightmapping/bake_quality/medium_quality_ray_count", 64);
GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_ray_count", 256);
@@ -59,5 +63,8 @@ void register_lightmapper_rd_types() {
#endif
}
-void unregister_lightmapper_rd_types() {
+void uninitialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/lightmapper_rd/register_types.h b/modules/lightmapper_rd/register_types.h
index 35a701ce01..9b72ff45d7 100644
--- a/modules/lightmapper_rd/register_types.h
+++ b/modules/lightmapper_rd/register_types.h
@@ -31,7 +31,9 @@
#ifndef LIGHTMAPPER_RD_REGISTER_TYPES_H
#define LIGHTMAPPER_RD_REGISTER_TYPES_H
-void register_lightmapper_rd_types();
-void unregister_lightmapper_rd_types();
+#include "modules/register_module_types.h"
-#endif // XATLAS_UNWRAP_REGISTER_TYPES_H
+void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level);
+void uninitialize_lightmapper_rd_module(ModuleInitializationLevel p_level);
+
+#endif // LIGHTMAPPER_RD_REGISTER_TYPES_H
diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h
index 29370062c4..a6626c9f65 100644
--- a/modules/mbedtls/dtls_server_mbedtls.h
+++ b/modules/mbedtls/dtls_server_mbedtls.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef MBED_DTLS_SERVER_H
-#define MBED_DTLS_SERVER_H
+#ifndef DTLS_SERVER_MBEDTLS_H
+#define DTLS_SERVER_MBEDTLS_H
#include "core/io/dtls_server.h"
#include "ssl_context_mbedtls.h"
@@ -54,4 +54,4 @@ public:
~DTLSServerMbedTLS();
};
-#endif // MBED_DTLS_SERVER_H
+#endif // DTLS_SERVER_MBEDTLS_H
diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp
index 1af978e70a..2d4a18b3fc 100644
--- a/modules/mbedtls/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -39,14 +39,22 @@
#include "tests/test_crypto_mbedtls.h"
#endif
-void register_mbedtls_types() {
+void initialize_mbedtls_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
CryptoMbedTLS::initialize_crypto();
StreamPeerMbedTLS::initialize_ssl();
PacketPeerMbedDTLS::initialize_dtls();
DTLSServerMbedTLS::initialize();
}
-void unregister_mbedtls_types() {
+void uninitialize_mbedtls_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
DTLSServerMbedTLS::finalize();
PacketPeerMbedDTLS::finalize_dtls();
StreamPeerMbedTLS::finalize_ssl();
diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h
index 4bc2cca118..ebe76f44f1 100644
--- a/modules/mbedtls/register_types.h
+++ b/modules/mbedtls/register_types.h
@@ -31,7 +31,9 @@
#ifndef MBEDTLS_REGISTER_TYPES_H
#define MBEDTLS_REGISTER_TYPES_H
-void register_mbedtls_types();
-void unregister_mbedtls_types();
+#include "modules/register_module_types.h"
+
+void initialize_mbedtls_module(ModuleInitializationLevel p_level);
+void uninitialize_mbedtls_module(ModuleInitializationLevel p_level);
#endif // MBEDTLS_REGISTER_TYPES_H
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h
index dd49792abd..5883388311 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/ssl_context_mbedtls.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SSL_CONTEXT_MBED_TLS_H
-#define SSL_CONTEXT_MBED_TLS_H
+#ifndef SSL_CONTEXT_MBEDTLS_H
+#define SSL_CONTEXT_MBEDTLS_H
#include "crypto_mbedtls.h"
@@ -90,4 +90,4 @@ public:
~SSLContextMbedTLS();
};
-#endif // SSL_CONTEXT_MBED_TLS_H
+#endif // SSL_CONTEXT_MBEDTLS_H
diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp
index 1818048877..92590fbcf6 100644
--- a/modules/mbedtls/stream_peer_mbedtls.cpp
+++ b/modules/mbedtls/stream_peer_mbedtls.cpp
@@ -298,6 +298,10 @@ StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
return status;
}
+Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {
+ return base;
+}
+
StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
return memnew(StreamPeerMbedTLS);
}
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index 98b91e65ab..68b07feea9 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef STREAM_PEER_OPEN_SSL_H
-#define STREAM_PEER_OPEN_SSL_H
+#ifndef STREAM_PEER_MBEDTLS_H
+#define STREAM_PEER_MBEDTLS_H
#include "core/io/stream_peer_ssl.h"
#include "ssl_context_mbedtls.h"
@@ -57,6 +57,7 @@ public:
virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>());
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>());
virtual Status get_status() const;
+ virtual Ref<StreamPeer> get_stream() const;
virtual void disconnect_from_stream();
@@ -75,4 +76,4 @@ public:
~StreamPeerMbedTLS();
};
-#endif // STREAM_PEER_SSL_H
+#endif // STREAM_PEER_MBEDTLS_H
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 597c12ed23..3e212360c0 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -32,7 +32,11 @@
#include "scene/resources/surface_tool.h"
#include "thirdparty/meshoptimizer/meshoptimizer.h"
-void register_meshoptimizer_types() {
+void initialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache;
SurfaceTool::simplify_func = meshopt_simplify;
SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
@@ -43,7 +47,11 @@ void register_meshoptimizer_types() {
SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
}
-void unregister_meshoptimizer_types() {
+void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
SurfaceTool::optimize_vertex_cache_func = nullptr;
SurfaceTool::simplify_func = nullptr;
SurfaceTool::simplify_scale_func = nullptr;
diff --git a/modules/meshoptimizer/register_types.h b/modules/meshoptimizer/register_types.h
index fdd8bed657..3a84aab7bc 100644
--- a/modules/meshoptimizer/register_types.h
+++ b/modules/meshoptimizer/register_types.h
@@ -31,7 +31,9 @@
#ifndef MESHOPTIMIZER_REGISTER_TYPES_H
#define MESHOPTIMIZER_REGISTER_TYPES_H
-void register_meshoptimizer_types();
-void unregister_meshoptimizer_types();
+#include "modules/register_module_types.h"
-#endif // PVR_REGISTER_TYPES_H
+void initialize_meshoptimizer_module(ModuleInitializationLevel p_level);
+void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level);
+
+#endif // MESHOPTIMIZER_REGISTER_TYPES_H
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index b5b51403f7..98bcdea8f4 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -38,12 +38,20 @@
#include "core/io/file_access.h"
int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- ERR_FAIL_COND_V(!active, 0);
+ if (!active) {
+ return 0;
+ }
int todo = p_frames;
int frames_mixed_this_step = p_frames;
+ int beat_length_frames = -1;
+ bool beat_loop = mp3_stream->has_loop() && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
+ if (beat_loop) {
+ beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm();
+ }
+
while (todo && active) {
mp3dec_frame_info_t frame_info;
mp3d_sample_t *buf_frame = nullptr;
@@ -52,8 +60,25 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
if (samples_mixed) {
p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ if (loop_fade_remaining < FADE_SIZE) {
+ p_buffer[p_frames - todo] += loop_fade[loop_fade_remaining] * (float(FADE_SIZE - loop_fade_remaining) / float(FADE_SIZE));
+ loop_fade_remaining++;
+ }
--todo;
++frames_mixed;
+
+ if (beat_loop && (int)frames_mixed >= beat_length_frames) {
+ for (int i = 0; i < FADE_SIZE; i++) {
+ samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+ loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ if (!samples_mixed) {
+ break;
+ }
+ }
+ loop_fade_remaining = 0;
+ seek(mp3_stream->loop_offset);
+ loops++;
+ }
}
else {
@@ -115,6 +140,10 @@ void AudioStreamPlaybackMP3::seek(float p_time) {
mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}
+void AudioStreamPlaybackMP3::tag_used_streams() {
+ mp3_stream->tag_used(get_playback_position());
+}
+
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
if (mp3d) {
mp3dec_ex_close(mp3d);
@@ -122,7 +151,7 @@ AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
}
}
-Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
+Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() {
Ref<AudioStreamPlaybackMP3> mp3s;
ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
@@ -204,6 +233,36 @@ bool AudioStreamMP3::is_monophonic() const {
return false;
}
+void AudioStreamMP3::set_bpm(double p_bpm) {
+ ERR_FAIL_COND(p_bpm < 0);
+ bpm = p_bpm;
+ emit_changed();
+}
+
+double AudioStreamMP3::get_bpm() const {
+ return bpm;
+}
+
+void AudioStreamMP3::set_beat_count(int p_beat_count) {
+ ERR_FAIL_COND(p_beat_count < 0);
+ beat_count = p_beat_count;
+ emit_changed();
+}
+
+int AudioStreamMP3::get_beat_count() const {
+ return beat_count;
+}
+
+void AudioStreamMP3::set_bar_beats(int p_bar_beats) {
+ ERR_FAIL_COND(p_bar_beats < 0);
+ bar_beats = p_bar_beats;
+ emit_changed();
+}
+
+int AudioStreamMP3::get_bar_beats() const {
+ return bar_beats;
+}
+
void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
@@ -214,7 +273,19 @@ void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);
+ ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamMP3::set_bpm);
+ ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamMP3::get_bpm);
+
+ ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamMP3::set_beat_count);
+ ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamMP3::get_beat_count);
+
+ ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamMP3::set_bar_beats);
+ ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamMP3::get_bar_beats);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index c1a60ddccb..428ac1240e 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -41,6 +41,12 @@ class AudioStreamMP3;
class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled);
+ enum {
+ FADE_SIZE = 256
+ };
+ AudioFrame loop_fade[FADE_SIZE];
+ int loop_fade_remaining = FADE_SIZE;
+
mp3dec_ex_t *mp3d = nullptr;
uint32_t frames_mixed = 0;
bool active = false;
@@ -64,6 +70,8 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
+ virtual void tag_used_streams() override;
+
AudioStreamPlaybackMP3() {}
~AudioStreamPlaybackMP3();
};
@@ -85,17 +93,30 @@ class AudioStreamMP3 : public AudioStream {
float loop_offset = 0.0;
void clear_data();
+ double bpm = 0;
+ int beat_count = 0;
+ int bar_beats = 4;
+
protected:
static void _bind_methods();
public:
void set_loop(bool p_enable);
- bool has_loop() const;
+ virtual bool has_loop() const override;
void set_loop_offset(float p_seconds);
float get_loop_offset() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ void set_bpm(double p_bpm);
+ virtual double get_bpm() const override;
+
+ void set_beat_count(int p_beat_count);
+ virtual int get_beat_count() const override;
+
+ void set_bar_beats(int p_bar_beats);
+ virtual int get_bar_beats() const override;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
void set_data(const Vector<uint8_t> &p_data);
diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml
index f5f7d3ef17..8f03681c06 100644
--- a/modules/minimp3/doc_classes/AudioStreamMP3.xml
+++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml
@@ -4,13 +4,42 @@
MP3 audio stream driver.
</brief_description>
<description>
- MP3 audio stream driver.
+ MP3 audio stream driver. See [member data] if you want to load an MP3 file at run-time.
</description>
<tutorials>
</tutorials>
<members>
+ <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
+ </member>
+ <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0">
+ </member>
+ <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
+ </member>
<member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
Contains the audio data in bytes.
+ You can load a file without having to import it beforehand using the code snippet below. Keep in mind that this snippet loads the whole file into memory and may not be ideal for huge files (hundreds of megabytes or more).
+ [codeblocks]
+ [gdscript]
+ func load_mp3(path):
+ var file = File.new()
+ file.open(path, File.READ)
+ var sound = AudioStreamMP3.new()
+ sound.data = file.get_buffer(file.get_length())
+ file.close()
+ return sound
+ [/gdscript]
+ [csharp]
+ public AudioStreamMP3 LoadMP3(string path)
+ {
+ var file = new File();
+ file.Open(path, File.READ);
+ var sound = new AudioStreamMP3();
+ sound.Data = file.GetBuffer(file.GetLength());
+ file.Close();
+ return sound;
+ }
+ [/csharp]
+ [/codeblocks]
</member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
If [code]true[/code], the stream will automatically loop when it reaches the end.
diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp
index 4d32ebf8ea..9d1b56abdf 100644
--- a/modules/minimp3/register_types.cpp
+++ b/modules/minimp3/register_types.cpp
@@ -37,7 +37,11 @@
#include "resource_importer_mp3.h"
#endif
-void register_minimp3_types() {
+void initialize_minimp3_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
Ref<ResourceImporterMP3> mp3_import;
@@ -48,5 +52,8 @@ void register_minimp3_types() {
GDREGISTER_CLASS(AudioStreamMP3);
}
-void unregister_minimp3_types() {
+void uninitialize_minimp3_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/minimp3/register_types.h b/modules/minimp3/register_types.h
index fa7f67eefc..9e5eb85abe 100644
--- a/modules/minimp3/register_types.h
+++ b/modules/minimp3/register_types.h
@@ -31,7 +31,9 @@
#ifndef MINIMP3_REGISTER_TYPES_H
#define MINIMP3_REGISTER_TYPES_H
-void register_minimp3_types();
-void unregister_minimp3_types();
+#include "modules/register_module_types.h"
+
+void initialize_minimp3_module(ModuleInitializationLevel p_level);
+void uninitialize_minimp3_module(ModuleInitializationLevel p_level);
#endif // MINIMP3_REGISTER_TYPES_H
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index 6cd710e792..7492533094 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -34,6 +34,10 @@
#include "core/io/resource_saver.h"
#include "scene/resources/texture.h"
+#ifdef TOOLS_ENABLED
+#include "editor/import/audio_stream_import_settings.h"
+#endif
+
String ResourceImporterMP3::get_importer_name() const {
return "mp3";
}
@@ -54,7 +58,7 @@ String ResourceImporterMP3::get_resource_type() const {
return "AudioStreamMP3";
}
-bool ResourceImporterMP3::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const {
+bool ResourceImporterMP3::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
@@ -69,14 +73,26 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const {
void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
-Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
+#ifdef TOOLS_ENABLED
+bool ResourceImporterMP3::has_advanced_options() const {
+ return true;
+}
+void ResourceImporterMP3::show_advanced_options(const String &p_path) {
+ Ref<AudioStreamMP3> mp3_stream = import_mp3(p_path);
+ if (mp3_stream.is_valid()) {
+ AudioStreamImportSettings::get_singleton()->edit(p_path, "mp3", mp3_stream);
+ }
+}
+#endif
- Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
- ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
+Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V(f.is_null(), Ref<AudioStreamMP3>());
uint64_t len = f->get_length();
@@ -90,11 +106,29 @@ Error ResourceImporterMP3::import(const String &p_source_file, const String &p_s
mp3_stream.instantiate();
mp3_stream->set_data(data);
- ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(!mp3_stream->get_data().size(), Ref<AudioStreamMP3>());
+
+ return mp3_stream;
+}
+
+Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+ double bpm = p_options["bpm"];
+ float beat_count = p_options["beat_count"];
+ float bar_beats = p_options["bar_beats"];
+
+ Ref<AudioStreamMP3> mp3_stream = import_mp3(p_source_file);
+ if (mp3_stream.is_null()) {
+ return ERR_CANT_OPEN;
+ }
mp3_stream->set_loop(loop);
mp3_stream->set_loop_offset(loop_offset);
+ mp3_stream->set_bpm(bpm);
+ mp3_stream->set_beat_count(beat_count);
+ mp3_stream->set_bar_beats(bar_beats);
- return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream);
+ return ResourceSaver::save(mp3_stream, p_save_path + ".mp3str");
}
ResourceImporterMP3::ResourceImporterMP3() {
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 75890228b9..38729d68f8 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -48,9 +48,15 @@ public:
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+#ifdef TOOLS_ENABLED
+ virtual bool has_advanced_options() const override;
+ virtual void show_advanced_options(const String &p_path) override;
+#endif
+ static Ref<AudioStreamMP3> import_mp3(const String &p_path);
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
ResourceImporterMP3();
};
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 8cd23ffb24..b14f5f469c 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -45,7 +45,7 @@ uint32_t MobileVRInterface::get_capabilities() const {
Vector3 MobileVRInterface::scale_magneto(const Vector3 &p_magnetometer) {
// Our magnetometer doesn't give us nice clean data.
- // Well it may on Mac OS X because we're getting a calibrated value in the current implementation but Android we're getting raw data.
+ // Well it may on macOS because we're getting a calibrated value in the current implementation but Android we're getting raw data.
// This is a fairly simple adjustment we can do to correct for the magnetometer data being elliptical
Vector3 mag_raw = p_magnetometer;
@@ -112,9 +112,9 @@ Basis MobileVRInterface::combine_acc_mag(const Vector3 &p_grav, const Vector3 &p
// We use our gravity and magnetometer vectors to construct our matrix
Basis acc_mag_m3;
- acc_mag_m3.elements[0] = -magneto_east;
- acc_mag_m3.elements[1] = up;
- acc_mag_m3.elements[2] = magneto;
+ acc_mag_m3.rows[0] = -magneto_east;
+ acc_mag_m3.rows[1] = up;
+ acc_mag_m3.rows[2] = magneto;
return acc_mag_m3;
};
@@ -175,9 +175,9 @@ void MobileVRInterface::set_position_from_sensors() {
if (has_gyro) {
// start with applying our gyro (do NOT smooth our gyro!)
Basis rotate;
- rotate.rotate(orientation.get_axis(0), gyro.x * delta_time);
- rotate.rotate(orientation.get_axis(1), gyro.y * delta_time);
- rotate.rotate(orientation.get_axis(2), gyro.z * delta_time);
+ rotate.rotate(orientation.get_column(0), gyro.x * delta_time);
+ rotate.rotate(orientation.get_column(1), gyro.y * delta_time);
+ rotate.rotate(orientation.get_column(2), gyro.z * delta_time);
orientation = rotate * orientation;
tracking_state = XRInterface::XR_NORMAL_TRACKING;
@@ -452,10 +452,10 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra
return transform_for_eye;
};
-CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+Projection MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
_THREAD_SAFE_METHOD_
- CameraMatrix eye;
+ Projection eye;
aspect = p_aspect;
eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index 8ecca3a2ae..b934a09dd3 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -150,7 +150,7 @@ public:
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 682d8bf59a..4df8af9009 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -34,7 +34,11 @@
Ref<MobileVRInterface> mobile_vr;
-void register_mobile_vr_types() {
+void initialize_mobile_vr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_CLASS(MobileVRInterface);
if (XRServer::get_singleton()) {
@@ -43,7 +47,11 @@ void register_mobile_vr_types() {
}
}
-void unregister_mobile_vr_types() {
+void uninitialize_mobile_vr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (mobile_vr.is_valid()) {
// uninitialise our interface if it is initialised
if (mobile_vr->is_initialized()) {
diff --git a/modules/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h
index 8db96ac2fa..26812af512 100644
--- a/modules/mobile_vr/register_types.h
+++ b/modules/mobile_vr/register_types.h
@@ -31,7 +31,9 @@
#ifndef MOBILE_VR_REGISTER_TYPES_H
#define MOBILE_VR_REGISTER_TYPES_H
-void register_mobile_vr_types();
-void unregister_mobile_vr_types();
+#include "modules/register_module_types.h"
+
+void initialize_mobile_vr_module(ModuleInitializationLevel p_level);
+void uninitialize_mobile_vr_module(ModuleInitializationLevel p_level);
#endif // MOBILE_VR_REGISTER_TYPES_H
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 3bafa351a9..d10ebc7b47 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -55,7 +55,7 @@ env_mono.add_source_files(env.modules_sources, "utils/*.cpp")
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
-if env["platform"] in ["osx", "iphone"]:
+if env["platform"] in ["macos", "ios"]:
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
elif env["platform"] == "android":
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index 1920ef1c1a..3459244bc2 100644
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -43,7 +43,7 @@ String get_godot_android_mono_config() {
Compression::decompress(w, config_uncompressed_size, config_compressed_data,
config_compressed_size, Compression::MODE_DEFLATE);
String s;
- if (s.parse_utf8((const char *)w, data.size())) {
+ if (s.parse_utf8((const char *)w, data.size()) != OK) {
ERR_FAIL_V(String());
}
return s;
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 8e441e7e07..e69904c54b 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -63,15 +63,15 @@ def copy_file(src_dir, dst_dir, src_name, dst_name=""):
def is_desktop(platform):
- return platform in ["windows", "osx", "linuxbsd", "server", "uwp", "haiku"]
+ return platform in ["windows", "macos", "linuxbsd", "server", "uwp", "haiku"]
def is_unix_like(platform):
- return platform in ["osx", "linuxbsd", "server", "android", "haiku", "iphone"]
+ return platform in ["macos", "linuxbsd", "server", "android", "haiku", "ios"]
def module_supports_tools_on(platform):
- return platform not in ["android", "javascript", "iphone"]
+ return platform not in ["android", "javascript", "ios"]
def find_wasm_src_dir(mono_root):
@@ -89,7 +89,7 @@ def configure(env, env_mono):
bits = env["bits"]
is_android = env["platform"] == "android"
is_javascript = env["platform"] == "javascript"
- is_ios = env["platform"] == "iphone"
+ is_ios = env["platform"] == "ios"
is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
tools_enabled = env["tools"]
@@ -206,7 +206,7 @@ def configure(env, env_mono):
copy_file(mono_bin_path, "#bin", mono_dll_file)
else:
- is_apple = env["platform"] in ["osx", "iphone"]
+ is_apple = env["platform"] in ["macos", "ios"]
is_macos = is_apple and not is_ios
sharedlib_ext = ".dylib" if is_apple else ".so"
@@ -221,7 +221,7 @@ def configure(env, env_mono):
)
if not mono_root and is_macos:
- # Try with some known directories under OSX
+ # Try with some known directories under macOS
hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"]
for hint_dir in hint_dirs:
if os.path.isdir(hint_dir):
@@ -270,7 +270,7 @@ def configure(env, env_mono):
def copy_mono_lib(libname_wo_ext):
copy_file(
- mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.iphone.%s.a" % (libname_wo_ext, arch)
+ mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.ios.%s.a" % (libname_wo_ext, arch)
)
# Copy Mono libraries to the output folder. These are meant to be bundled with
@@ -539,7 +539,7 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
os.makedirs(target_mono_lib_dir)
lib_file_names = []
- if platform == "osx":
+ if platform == "macos":
lib_file_names = [
lib_name + ".dylib"
for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"]
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index 9253f105bb..c4547b4323 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -40,17 +40,12 @@
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Dictionary classes_dict;
- List<StringName> names;
+ List<StringName> class_list;
+ ClassDB::get_class_list(&class_list);
+ // Must be alphabetically sorted for hash to compute.
+ class_list.sort_custom<StringName::AlphCompare>();
- const StringName *k = nullptr;
-
- while ((k = ClassDB::classes.next(k))) {
- names.push_back(*k);
- }
- //must be alphabetically sorted for hash to compute
- names.sort_custom<StringName::AlphCompare>();
-
- for (const StringName &E : names) {
+ for (const StringName &E : class_list) {
ClassDB::ClassInfo *t = ClassDB::classes.getptr(E);
ERR_FAIL_COND(!t);
if (t->api != p_api || !t->exposed) {
@@ -66,10 +61,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->method_map.next(k))) {
- String name = k->operator String();
+ for (const KeyValue<StringName, MethodBind *> &F : t->method_map) {
+ String name = F.key.operator String();
ERR_CONTINUE(name.is_empty());
@@ -77,7 +70,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
continue; // Ignore non-virtual methods that start with an underscore
}
- snames.push_back(*k);
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -131,10 +124,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->constant_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, int64_t> &F : t->constant_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -158,10 +149,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->signal_map.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
@@ -193,10 +182,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- k = nullptr;
-
- while ((k = t->property_setget.next(k))) {
- snames.push_back(*k);
+ for (const KeyValue<StringName, ClassDB::PropertySetGet> &F : t->property_setget) {
+ snames.push_back(F.key);
}
snames.sort_custom<StringName::AlphCompare>();
diff --git a/modules/mono/config.py b/modules/mono/config.py
index df02d9a309..3e6584590c 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,4 +1,4 @@
-supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku", "javascript", "iphone"]
+supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"]
def can_build(env, platform):
@@ -15,7 +15,7 @@ def configure(env):
from SCons.Script import BoolVariable, PathVariable, Variables, Help
- default_mono_static = platform in ["iphone", "javascript"]
+ default_mono_static = platform in ["ios", "javascript"]
default_mono_bundles_zlib = platform in ["javascript"]
envvars = Variables()
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 02aebb3805..c7279be97f 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -481,11 +481,14 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::VECTOR3,
Variant::VECTOR3I,
Variant::TRANSFORM2D,
+ Variant::VECTOR4,
+ Variant::VECTOR4I,
Variant::PLANE,
Variant::QUATERNION,
Variant::AABB,
Variant::BASIS,
Variant::TRANSFORM3D,
+ Variant::PROJECTION,
Variant::COLOR,
Variant::STRING_NAME,
Variant::NODE_PATH,
@@ -686,10 +689,10 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
#ifdef DEBUG_ENABLED
MutexLock lock(unsafe_object_references_lock);
ObjectID id = p_obj->get_instance_id();
- Map<ObjectID, int>::Element *elem = unsafe_object_references.find(id);
+ HashMap<ObjectID, int>::Iterator elem = unsafe_object_references.find(id);
ERR_FAIL_NULL(elem);
- if (--elem->value() == 0) {
- unsafe_object_references.erase(elem);
+ if (--elem->value == 0) {
+ unsafe_object_references.remove(elem);
}
#endif
}
@@ -875,7 +878,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
- for (Object *&obj : script->instances) {
+ for (Object *obj : script->instances) {
script->pending_reload_instances.insert(obj->get_instance_id());
RefCounted *rc = Object::cast_to<RefCounted>(obj);
@@ -885,7 +888,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#ifdef TOOLS_ENABLED
- for (PlaceHolderScriptInstance *&script_instance : script->placeholders) {
+ for (PlaceHolderScriptInstance *script_instance : script->placeholders) {
Object *obj = script_instance->get_owner();
script->pending_reload_instances.insert(obj->get_instance_id());
@@ -897,9 +900,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Save state and remove script from instances
- Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
+ RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
- for (Object *&obj : script->instances) {
+ for (Object *obj : script->instances) {
ERR_CONTINUE(!obj->get_script_instance());
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
@@ -922,9 +925,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// After the state of all instances is saved, clear scripts and script instances
for (Ref<CSharpScript> &script : scripts) {
- while (script->instances.front()) {
- Object *obj = script->instances.front()->get();
- obj->set_script(REF()); // Remove script and existing script instances (placeholder are not removed before domain reload)
+ while (script->instances.begin()) {
+ Object *obj = *script->instances.begin();
+ obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
}
script->_clear();
@@ -1099,14 +1102,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
const StringName &name = G.first;
const Array &serialized_data = G.second;
- Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
+ HashMap<StringName, CSharpScript::EventSignal>::Iterator match = script->event_signals.find(name);
if (!match) {
// The event or its signal attribute were removed
continue;
}
- const CSharpScript::EventSignal &event_signal = match->value();
+ const CSharpScript::EventSignal &event_signal = match->value;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
MonoDelegate *delegate = nullptr;
@@ -1428,7 +1431,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
return true;
}
-Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
+RBMap<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
return script_bindings.insert(p_object, p_script_binding);
}
@@ -1437,7 +1440,7 @@ void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance
MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
+ RBMap<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
if (match) {
return (void *)match;
}
@@ -1467,7 +1470,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
{
MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding;
+ RBMap<Object *, CSharpScriptBinding>::Element *data = (RBMap<Object *, CSharpScriptBinding>::Element *)p_binding;
CSharpScriptBinding &script_binding = data->value();
@@ -1488,7 +1491,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
CRASH_COND(!p_binding);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner);
@@ -1558,7 +1561,7 @@ void *CSharpLanguage::get_instance_binding(Object *p_object) {
// `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that.
if (binding) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)binding)->value();
if (!script_binding.inited) {
MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
@@ -1798,8 +1801,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
List<PropertyInfo> props;
- for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = script->member_info.front(); E; E = E.next()) {
- props.push_front(E.value());
+ for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
+ props.push_front(E.value);
}
// Call _get_property_list
@@ -2138,8 +2141,8 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
-const Vector<Multiplayer::RPCConfig> CSharpInstance::get_rpc_methods() const {
- return script->get_rpc_methods();
+const Variant CSharpInstance::get_rpc_config() const {
+ return script->get_rpc_config();
}
void CSharpInstance::notification(int p_notification) {
@@ -2301,7 +2304,7 @@ CSharpInstance::~CSharpInstance() {
void *data = CSharpLanguage::get_instance_binding(owner);
CRASH_COND(data == nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
CRASH_COND(!script_binding.inited);
#ifdef DEBUG_ENABLED
@@ -2315,9 +2318,9 @@ CSharpInstance::~CSharpInstance() {
#ifdef DEBUG_ENABLED
// CSharpInstance must not be created unless it's going to be added to the list for sure
- Set<Object *>::Element *match = script->instances.find(owner);
+ HashSet<Object *>::Iterator match = script->instances.find(owner);
CRASH_COND(!match);
- script->instances.erase(match);
+ script->instances.remove(match);
#else
script->instances.erase(owner);
#endif
@@ -2331,7 +2334,7 @@ void CSharpScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#endif
#ifdef TOOLS_ENABLED
-void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) {
+void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
if (base_cache.is_valid()) {
base_cache->_update_exports_values(values, propnames);
}
@@ -2567,12 +2570,12 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
if ((changed || p_instance_to_update) && placeholders.size()) {
// Update placeholders if any
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
if (changed) {
- for (PlaceHolderScriptInstance *&script_instance : placeholders) {
+ for (PlaceHolderScriptInstance *script_instance : placeholders) {
script_instance->update(propnames, values);
}
} else {
@@ -2807,7 +2810,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
GD_MONO_ASSERT_THREAD_ATTACHED;
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
- r_hint = PROPERTY_HINT_ENUM;
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
+ r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
@@ -2844,7 +2848,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
- if (val != (unsigned int)i) {
+ unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
+ if (val != expected_val) {
uses_default_values = false;
}
@@ -2864,6 +2869,12 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
+ } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(Node)->is_assignable_from(p_type.type_class)) {
+ GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
+ CRASH_COND(field_native_class == nullptr);
+
+ r_hint = PROPERTY_HINT_NODE_TYPE;
+ r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
@@ -3049,7 +3060,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
- p_script->rpc_functions.clear();
+ p_script->rpc_config.clear();
GDMonoClass *top = p_script->script_class;
while (top && top != p_script->native) {
@@ -3061,17 +3072,9 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
Vector<GDMonoMethod *> methods = top->get_all_methods();
for (int i = 0; i < methods.size(); i++) {
if (!methods[i]->is_static()) {
- Multiplayer::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
- if (Multiplayer::RPC_MODE_DISABLED != mode) {
- Multiplayer::RPCConfig nd;
- nd.name = methods[i]->get_name();
- nd.rpc_mode = mode;
- // TODO Transfer mode, channel
- nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
- nd.channel = 0;
- if (-1 == p_script->rpc_functions.find(nd)) {
- p_script->rpc_functions.push_back(nd);
- }
+ const Variant rpc_config = p_script->_member_get_rpc_config(methods[i]);
+ if (rpc_config.get_type() != Variant::NIL) {
+ p_script->rpc_config[methods[i]->get_name()] = rpc_config;
}
}
}
@@ -3080,9 +3083,6 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
top = top->get_parent_class();
}
- // Sort so we are 100% that they are always the same.
- p_script->rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
-
p_script->load_script_signals(p_script->script_class, p_script->native);
}
@@ -3144,7 +3144,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
void *data = CSharpLanguage::get_existing_instance_binding(p_owner);
CRASH_COND(data == nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited && !script_binding.gchandle.is_released()) {
MonoObject *mono_object = script_binding.gchandle.get_target();
if (mono_object) {
@@ -3221,10 +3221,10 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native));
- REF ref;
+ Ref<RefCounted> ref;
RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
- ref = REF(r);
+ ref = Ref<RefCounted>(r);
}
CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
@@ -3401,9 +3401,9 @@ ScriptLanguage *CSharpScript::get_language() const {
bool CSharpScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
- const Map<StringName, Variant>::Element *E = exported_members_defval_cache.find(p_property);
+ HashMap<StringName, Variant>::ConstIterator E = exported_members_defval_cache.find(p_property);
if (E) {
- r_value = E->get();
+ r_value = E->value;
return true;
}
@@ -3491,8 +3491,8 @@ Ref<Script> CSharpScript::get_base_script() const {
void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
List<PropertyInfo> props;
- for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = member_info.front(); E; E = E.next()) {
- props.push_front(E.value());
+ for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
+ props.push_front(E.value);
}
for (const PropertyInfo &prop : props) {
@@ -3505,19 +3505,24 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
-Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
- if (p_member->has_attribute(CACHED_CLASS(AnyPeerAttribute))) {
- return Multiplayer::RPC_MODE_ANY_PEER;
- }
- if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) {
- return Multiplayer::RPC_MODE_AUTHORITY;
+Variant CSharpScript::_member_get_rpc_config(IMonoClassMember *p_member) const {
+ Variant out;
+
+ MonoObject *rpc_attribute = p_member->get_attribute(CACHED_CLASS(RPCAttribute));
+ if (rpc_attribute != nullptr) {
+ Dictionary rpc_config;
+ rpc_config["rpc_mode"] = CACHED_PROPERTY(RPCAttribute, Mode)->get_int_value(rpc_attribute);
+ rpc_config["call_local"] = CACHED_PROPERTY(RPCAttribute, CallLocal)->get_bool_value(rpc_attribute);
+ rpc_config["transfer_mode"] = CACHED_PROPERTY(RPCAttribute, TransferMode)->get_int_value(rpc_attribute);
+ rpc_config["channel"] = CACHED_PROPERTY(RPCAttribute, TransferChannel)->get_int_value(rpc_attribute);
+ out = rpc_config;
}
- return Multiplayer::RPC_MODE_DISABLED;
+ return out;
}
-const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const {
- return rpc_functions;
+const Variant CSharpScript::get_rpc_config() const {
+ return rpc_config;
}
Error CSharpScript::load_source_code(const String &p_path) {
@@ -3574,7 +3579,7 @@ CSharpScript::~CSharpScript() {
#endif
}
-void CSharpScript::get_members(Set<StringName> *p_members) {
+void CSharpScript::get_members(HashSet<StringName> *p_members) {
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
if (p_members) {
for (const StringName &member_name : exported_members_names) {
@@ -3586,7 +3591,7 @@ void CSharpScript::get_members(Set<StringName> *p_members) {
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -3599,7 +3604,7 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
Error err = script->load_source_code(p_path);
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load C# script file '" + p_path + "'.");
+ ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + p_path + "'.");
#endif
script->set_path(p_original_path);
@@ -3625,7 +3630,7 @@ String ResourceFormatLoaderCSharpScript::get_resource_type(const String &p_path)
return p_path.get_extension().to_lower() == "cs" ? CSharpLanguage::get_singleton()->get_type() : "";
}
-Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+Error ResourceFormatSaverCSharpScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
Ref<CSharpScript> sqscr = p_resource;
ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
@@ -3662,13 +3667,13 @@ Error ResourceFormatSaverCSharpScript::save(const String &p_path, const RES &p_r
return OK;
}
-void ResourceFormatSaverCSharpScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+void ResourceFormatSaverCSharpScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
if (Object::cast_to<CSharpScript>(p_resource.ptr())) {
p_extensions->push_back("cs");
}
}
-bool ResourceFormatSaverCSharpScript::recognize(const RES &p_resource) const {
+bool ResourceFormatSaverCSharpScript::recognize(const Ref<Resource> &p_resource) const {
return Object::cast_to<CSharpScript>(p_resource.ptr()) != nullptr;
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 1e5f218c95..48129e69cb 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -110,7 +110,7 @@ private:
Ref<CSharpScript> base_cache; // TODO what's this for?
- Set<Object *> instances;
+ HashSet<Object *> instances;
#ifdef GD_MONO_HOT_RELOAD
struct StateBackup {
@@ -121,8 +121,8 @@ private:
List<Pair<StringName, Array>> event_signals;
};
- Set<ObjectID> pending_reload_instances;
- Map<ObjectID, StateBackup> pending_reload_state;
+ HashSet<ObjectID> pending_reload_instances;
+ RBMap<ObjectID, StateBackup> pending_reload_state;
StringName tied_class_name_for_reload;
StringName tied_class_namespace_for_reload;
#endif
@@ -132,29 +132,29 @@ private:
SelfList<CSharpScript> script_list = this;
- Map<StringName, Vector<SignalParameter>> _signals;
- Map<StringName, EventSignal> event_signals;
+ HashMap<StringName, Vector<SignalParameter>> _signals;
+ HashMap<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
- Vector<Multiplayer::RPCConfig> rpc_functions;
+ Dictionary rpc_config;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
- Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
- Set<PlaceHolderScriptInstance *> placeholders;
+ HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
+ HashSet<PlaceHolderScriptInstance *> placeholders;
bool source_changed_cache = false;
bool placeholder_fallback_enabled = false;
bool exports_invalidated = true;
- void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
void _update_member_info_no_exports();
void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- Set<StringName> exported_members_names;
+ HashSet<StringName> exported_members_names;
#endif
- OrderedHashMap<StringName, PropertyInfo> member_info;
+ HashMap<StringName, PropertyInfo> member_info;
void _clear();
@@ -179,7 +179,7 @@ private:
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);
- Multiplayer::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
+ Variant _member_get_rpc_config(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
@@ -218,7 +218,7 @@ public:
void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
- void get_members(Set<StringName> *p_members) override;
+ void get_members(HashSet<StringName> *p_members) override;
bool is_tool() const override { return tool; }
bool is_valid() const override { return valid; }
@@ -234,7 +234,7 @@ public:
int get_member_line(const StringName &p_member) const override;
- const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
+ const Variant get_rpc_config() const override;
#ifdef TOOLS_ENABLED
bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@@ -311,7 +311,7 @@ public:
void refcount_incremented() override;
bool refcount_decremented() override;
- const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
+ const Variant get_rpc_config() const override;
void notification(int p_notification) override;
void _call_notification(int p_notification);
@@ -356,11 +356,11 @@ class CSharpLanguage : public ScriptLanguage {
Mutex script_gchandle_release_mutex;
Mutex language_bind_mutex;
- Map<Object *, CSharpScriptBinding> script_bindings;
+ RBMap<Object *, CSharpScriptBinding> script_bindings;
#ifdef DEBUG_ENABLED
// List of unsafe object references
- Map<ObjectID, int> unsafe_object_references;
+ HashMap<ObjectID, int> unsafe_object_references;
Mutex unsafe_object_references_lock;
#endif
@@ -467,7 +467,7 @@ public:
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
/* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions,
- List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
+ List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override {
return true;
}
String validate_path(const String &p_path) const override;
@@ -502,6 +502,7 @@ public:
/* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {}
/* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {}
+ /* TODO? */ void get_public_annotations(List<MethodInfo> *p_annotations) const override {}
void reload_all_scripts() override;
void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
@@ -518,7 +519,7 @@ public:
void thread_enter() override;
void thread_exit() override;
- Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
+ RBMap<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
#ifdef DEBUG_ENABLED
@@ -534,7 +535,7 @@ public:
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
- RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
+ Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
void get_recognized_extensions(List<String> *p_extensions) const override;
bool handles_type(const String &p_type) const override;
String get_resource_type(const String &p_path) const override;
@@ -542,9 +543,9 @@ public:
class ResourceFormatSaverCSharpScript : public ResourceFormatSaver {
public:
- Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0) override;
- void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const override;
- bool recognize(const RES &p_resource) const override;
+ Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
+ void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
+ bool recognize(const Ref<Resource> &p_resource) const override;
};
#endif // CSHARP_SCRIPT_H
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 0128f5c706..5a499742e9 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
@@ -57,7 +57,7 @@
<PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
- <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">osx</GodotTargetPlatform>
+ <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">macos</GodotTargetPlatform>
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Windows))' ">windows</GodotTargetPlatform>
</PropertyGroup>
@@ -76,12 +76,12 @@
-->
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants>
- <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'osx' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'macos' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'server' ">GODOT_SERVER;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'uwp' ">GODOT_UWP;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'haiku' ">GODOT_HAIKU;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'android' ">GODOT_ANDROID;GODOT_MOBILE</GodotPlatformConstants>
- <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'iphone' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index ae78da27bc..303ca3a293 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
@@ -12,6 +12,6 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index dad6b9ae7a..02f1764f32 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -19,6 +19,6 @@ A client using this library is only compatible with servers of the same major ve
</Description>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index bfc807c01a..ebdaca0ce8 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -334,8 +334,13 @@ namespace GodotTools.Build
}
}
- private void IssuesListRmbSelected(int index, Vector2 atPosition)
+ private void IssuesListClicked(int index, Vector2 atPosition, int mouseButtonIndex)
{
+ if (mouseButtonIndex != (int)MouseButton.Right)
+ {
+ return;
+ }
+
_ = index; // Unused
_issuesListContextMenu.Clear();
@@ -375,7 +380,7 @@ namespace GodotTools.Build
};
_issuesList.ItemActivated += IssueActivated;
_issuesList.AllowRmbSelect = true;
- _issuesList.ItemRmbSelected += IssuesListRmbSelected;
+ _issuesList.ItemClicked += IssuesListClicked;
hsc.AddChild(_issuesList);
_issuesListContextMenu = new PopupMenu();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index bac7a2e6db..02e9d98647 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -63,6 +63,7 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
if (UsingMonoMsBuildOnWindows)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 9e8f7ef1b1..3c020a2589 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -126,7 +126,7 @@ namespace GodotTools.Build
{
base._Ready();
- MinimumSize = new Vector2(0, 228) * EditorScale;
+ CustomMinimumSize = new Vector2(0, 228) * EditorScale;
SizeFlagsVertical = (int)SizeFlags.ExpandFill;
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index 16dd1c8c6b..63b97e981e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -99,7 +99,7 @@ namespace GodotTools.Build
if (Utils.OS.IsWindows)
{
// %APPDATA% for both
- return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
+ return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
}
var paths = new string[2];
@@ -156,6 +156,7 @@ namespace GodotTools.Build
</packageSources>
</configuration>
";
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index e2f4d2f5fd..e9718cc82c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -336,10 +336,10 @@ MONO_AOT_MODE_LAST = 1000,
// Add the required Mono libraries to the Xcode project
- string MonoLibFile(string libFileName) => libFileName + ".iphone.fat.a";
+ string MonoLibFile(string libFileName) => libFileName + ".ios.fat.a";
string MonoLibFromTemplate(string libFileName) =>
- Path.Combine(Internal.FullTemplatesDir, "iphone-mono-libs", MonoLibFile(libFileName));
+ Path.Combine(Internal.FullExportTemplatesDir, "ios-mono-libs", MonoLibFile(libFileName));
exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmonosgen-2.0"));
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 3e46a89b7c..cca18a2a1f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -337,7 +337,7 @@ namespace GodotTools.Export
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
- string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
+ string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
bool validTemplatePathFound = true;
if (!Directory.Exists(templateDirPath))
@@ -347,7 +347,7 @@ namespace GodotTools.Export
if (isDebug)
{
target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
- templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
+ templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
validTemplatePathFound = true;
if (!Directory.Exists(templateDirPath))
@@ -380,7 +380,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.
+ // macOS 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.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
}
@@ -398,13 +398,13 @@ namespace GodotTools.Export
private static string GetBclProfileDir(string profile)
{
- string templatesDir = Internal.FullTemplatesDir;
+ string templatesDir = Internal.FullExportTemplatesDir;
return Path.Combine(templatesDir, "bcl", profile);
}
private static string DeterminePlatformBclDir(string platform)
{
- string templatesDir = Internal.FullTemplatesDir;
+ string templatesDir = Internal.FullExportTemplatesDir;
string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 69960bdbeb..b39c3d1c0d 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -263,16 +263,16 @@ namespace GodotTools
var args = new List<string>();
- bool osxAppBundleInstalled = false;
+ bool macOSAppBundleInstalled = false;
if (OS.IsMacOS)
{
// The package path is '/Applications/Visual Studio Code.app'
const string vscodeBundleId = "com.microsoft.VSCode";
- osxAppBundleInstalled = Internal.IsOsxAppBundleInstalled(vscodeBundleId);
+ macOSAppBundleInstalled = Internal.IsMacOSAppBundleInstalled(vscodeBundleId);
- if (osxAppBundleInstalled)
+ if (macOSAppBundleInstalled)
{
args.Add("-b");
args.Add(vscodeBundleId);
@@ -307,13 +307,13 @@ namespace GodotTools
if (OS.IsMacOS)
{
- if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
+ if (!macOSAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
{
GD.PushError("Cannot find code editor: VSCode");
return Error.FileNotFound;
}
- command = osxAppBundleInstalled ? "/usr/bin/open" : _vsCodePath;
+ command = macOSAppBundleInstalled ? "/usr/bin/open" : _vsCodePath;
}
else
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index b9aa760f4d..f1d45463c5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
index 3f1d5ac3ca..7a0983a8cb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -30,7 +30,7 @@ namespace GodotTools.Ides.MonoDevelop
{
string bundleId = BundleIds[_editorId];
- if (Internal.IsOsxAppBundleInstalled(bundleId))
+ if (Internal.IsMacOSAppBundleInstalled(bundleId))
{
command = "open";
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 77370090ec..12c90178c9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -12,12 +12,12 @@ namespace GodotTools.Internals
public static string UpdateApiAssembliesFromPrebuilt(string config) =>
internal_UpdateApiAssembliesFromPrebuilt(config);
- public static string FullTemplatesDir =>
- internal_FullTemplatesDir();
+ public static string FullExportTemplatesDir =>
+ internal_FullExportTemplatesDir();
public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
- public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
+ public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId);
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
@@ -57,13 +57,13 @@ namespace GodotTools.Internals
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_FullTemplatesDir();
+ private static extern string internal_FullExportTemplatesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_SimplifyGodotPath(this string path);
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
+ private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_GodotIs32Bits();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 93a1360cb6..5cef6e5c3c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -37,13 +37,13 @@ namespace GodotTools.Utils
public static class Platforms
{
public const string Windows = "windows";
- public const string MacOS = "osx";
+ public const string MacOS = "macos";
public const string LinuxBSD = "linuxbsd";
public const string Server = "server";
public const string UWP = "uwp";
public const string Haiku = "haiku";
public const string Android = "android";
- public const string iOS = "iphone";
+ public const string iOS = "ios";
public const string HTML5 = "javascript";
}
@@ -184,7 +184,8 @@ namespace GodotTools.Utils
{
RedirectStandardOutput = true,
RedirectStandardError = true,
- UseShellExecute = false
+ UseShellExecute = false,
+ CreateNoWindow = true
};
using (Process process = Process.Start(startInfo))
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 2a93c15282..2e628cb576 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -604,14 +604,14 @@ void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const Ty
void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
const StringName search_cname = !p_target_itype ? p_target_cname : StringName(p_target_itype->name + "." + (String)p_target_cname);
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname);
+ HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(search_cname);
if (!enum_match && search_cname != p_target_cname) {
enum_match = enum_types.find(p_target_cname);
}
if (enum_match) {
- const TypeInterface &target_enum_itype = enum_match->value();
+ const TypeInterface &target_enum_itype = enum_match->value;
p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
p_xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
@@ -917,6 +917,8 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
ARRAY_ALL(Vector2i);
ARRAY_ALL(Vector3);
ARRAY_ALL(Vector3i);
+ ARRAY_ALL(Vector4);
+ ARRAY_ALL(Vector4i);
#undef ARRAY_ALL
#undef ARRAY_IS_EMPTY
@@ -954,7 +956,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
}
}
- p_output.append(MEMBER_BEGIN "public const int ");
+ p_output.append(MEMBER_BEGIN "public const long ");
p_output.append(iconstant.proxy_name);
p_output.append(" = ");
p_output.append(itos(iconstant.value));
@@ -990,8 +992,13 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n" INDENT1 OPEN_BLOCK);
}
+ if (ienum.is_flags) {
+ p_output.append("\n" INDENT1 "[System.Flags]");
+ }
+
p_output.append("\n" INDENT1 "public enum ");
p_output.append(enum_proxy_name);
+ p_output.append(" : long");
p_output.append("\n" INDENT1 OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
@@ -1078,8 +1085,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- const TypeInterface &itype = E.get();
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
if (itype.api_type == ClassDB::API_EDITOR) {
continue;
@@ -1187,8 +1194,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
Vector<String> compile_items;
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- const TypeInterface &itype = E.get();
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
if (itype.api_type != ClassDB::API_EDITOR) {
continue;
@@ -1417,7 +1424,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
}
- output.append(MEMBER_BEGIN "public const int ");
+ output.append(MEMBER_BEGIN "public const long ");
output.append(iconstant.proxy_name);
output.append(" = ");
output.append(itos(iconstant.value));
@@ -1433,8 +1440,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
for (const EnumInterface &ienum : itype.enums) {
ERR_FAIL_COND_V(ienum.constants.is_empty(), ERR_BUG);
+ if (ienum.is_flags) {
+ output.append(MEMBER_BEGIN "[System.Flags]");
+ }
+
output.append(MEMBER_BEGIN "public enum ");
output.append(ienum.cname.operator String());
+ output.append(" : long");
output.append(MEMBER_BEGIN OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
@@ -1573,9 +1585,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
const TypeInterface *current_type = &p_itype;
while (!setter && current_type->base_name != StringName()) {
- OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
- current_type = &base_match.get();
+ current_type = &base_match->value;
setter = current_type->find_method_by_name(p_iprop.setter);
}
@@ -1584,9 +1596,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// Search it in base types too
current_type = &p_itype;
while (!getter && current_type->base_name != StringName()) {
- OrderedHashMap<StringName, TypeInterface>::Element base_match = obj_types.find(current_type->base_name);
+ HashMap<StringName, TypeInterface>::Iterator base_match = obj_types.find(current_type->base_name);
ERR_FAIL_COND_V_MSG(!base_match, ERR_BUG, "Type not found '" + current_type->base_name + "'. Inherited by '" + current_type->name + "'.");
- current_type = &base_match.get();
+ current_type = &base_match->value;
getter = current_type->find_method_by_name(p_iprop.getter);
}
@@ -1652,7 +1664,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append("static ");
}
- p_output.append(prop_itype->cs_type);
+ String prop_cs_type = prop_itype->cs_type + _get_generic_type_parameters(*prop_itype, proptype_name.generic_type_parameters);
+
+ p_output.append(prop_cs_type);
p_output.append(" ");
p_output.append(p_iprop.proxy_name);
p_output.append("\n" INDENT2 OPEN_BLOCK);
@@ -1762,6 +1776,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
}
+ String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
@@ -1773,7 +1789,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
arguments_sig += "Nullable<";
}
- arguments_sig += arg_type->cs_type;
+ arguments_sig += arg_cs_type;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "> ";
@@ -1800,7 +1816,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String arg_in = iarg.name;
arg_in += "_in";
- cs_in_statements += arg_type->cs_type;
+ cs_in_statements += arg_cs_type;
cs_in_statements += " ";
cs_in_statements += arg_in;
cs_in_statements += " = ";
@@ -1820,7 +1836,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements += " : ";
}
- String cs_type = arg_type->cs_type;
+ String cs_type = arg_cs_type;
if (cs_type.ends_with("[]")) {
cs_type = cs_type.substr(0, cs_type.length() - 2);
}
@@ -1837,7 +1853,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Escape < and > in the attribute default value
String param_def_arg = def_arg.replacen("<", "&lt;").replacen(">", "&gt;");
- default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + param_def_arg + "</param>");
+ default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>");
} else {
icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
@@ -1903,7 +1919,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
- p_output.append(return_type->cs_type + " ");
+ String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);
+
+ p_output.append(return_cs_type + " ");
p_output.append(p_imethod.proxy_name + "(");
p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
@@ -1914,7 +1932,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("return;\n" CLOSE_BLOCK_L2);
} else {
p_output.append("return default(");
- p_output.append(return_type->cs_type);
+ p_output.append(return_cs_type);
p_output.append(");\n" CLOSE_BLOCK_L2);
}
@@ -1938,10 +1956,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
return OK; // Won't increment method bind count
}
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
ERR_FAIL_NULL_V(match, ERR_BUG);
- const InternalCall *im_icall = match->value();
+ const InternalCall *im_icall = match->value;
String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
im_call += ".";
@@ -1956,7 +1974,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.is_empty()) {
p_output.append("return " + im_call + "(" + icall_params + ");\n");
} else {
- p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
+ p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out));
p_output.append("\n");
}
@@ -2096,8 +2114,8 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
generated_icall_funcs.clear();
- for (OrderedHashMap<StringName, TypeInterface>::Element type_elem = obj_types.front(); type_elem; type_elem = type_elem.next()) {
- const TypeInterface &itype = type_elem.get();
+ for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) {
+ const TypeInterface &itype = type_elem.value;
bool is_derived_type = itype.base_name != StringName();
@@ -2322,10 +2340,10 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
i++;
}
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
ERR_FAIL_NULL_V(match, ERR_BUG);
- const InternalCall *im_icall = match->value();
+ const InternalCall *im_icall = match->value;
String icall_method = im_icall->name;
if (!generated_icall_funcs.find(im_icall)) {
@@ -2468,29 +2486,29 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
- const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator builtin_type_match = builtin_types.find(p_typeref.cname);
if (builtin_type_match) {
- return &builtin_type_match->get();
+ return &builtin_type_match->value;
}
- const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator obj_type_match = obj_types.find(p_typeref.cname);
if (obj_type_match) {
- return &obj_type_match.get();
+ return &obj_type_match->value;
}
if (p_typeref.is_enum) {
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(p_typeref.cname);
if (enum_match) {
- return &enum_match->get();
+ return &enum_match->value;
}
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
- const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
+ HashMap<StringName, TypeInterface>::ConstIterator int_match = builtin_types.find(name_cache.type_int);
ERR_FAIL_NULL_V(int_match, nullptr);
- return &int_match->get();
+ return &int_match->value;
}
return nullptr;
@@ -2505,16 +2523,52 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol
ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
- const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname);
if (match) {
- return &match->get();
+ return &match->value;
}
TypeInterface placeholder;
TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
- return &placeholder_types.insert(placeholder.cname, placeholder)->get();
+ return &placeholder_types.insert(placeholder.cname, placeholder)->value;
+}
+
+const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
+ if (p_generic_type_parameters.is_empty()) {
+ return "";
+ }
+
+ ERR_FAIL_COND_V_MSG(p_itype.type_parameter_count != p_generic_type_parameters.size(), "",
+ "Generic type parameter count mismatch for type '" + p_itype.name + "'." +
+ " Found " + itos(p_generic_type_parameters.size()) + ", but requires " +
+ itos(p_itype.type_parameter_count) + ".");
+
+ int i = 0;
+ String params = "<";
+ for (const TypeReference &param_type : p_generic_type_parameters) {
+ const TypeInterface *param_itype = _get_type_or_placeholder(param_type);
+
+ ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
+ "Generic type parameter is a singleton: '" + param_itype->name + "'.");
+
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(param_itype->api_type == ClassDB::API_EDITOR, "",
+ "Generic type parameter '" + param_itype->name + "' has type from the editor API." +
+ " Core API cannot have dependencies on the editor API.");
+ }
+
+ params += param_itype->cs_type;
+ if (i < p_generic_type_parameters.size() - 1) {
+ params += ", ";
+ }
+
+ i++;
+ }
+ params += ">";
+
+ return params;
}
StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
@@ -2708,7 +2762,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
List<PropertyInfo> property_list;
ClassDB::get_property_list(type_cname, &property_list, true);
- Map<StringName, StringName> accessor_methods;
+ HashMap<StringName, StringName> accessor_methods;
for (const PropertyInfo &property : property_list) {
if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) {
@@ -2821,7 +2875,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
" We only expected Object.free, but found '" +
itype.name + "." + imethod.name + "'.");
}
- } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ } else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
imethod.return_type.cname = return_info.class_name;
imethod.return_type.is_enum = true;
} else if (return_info.class_name != StringName()) {
@@ -2832,6 +2886,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(bad_reference_hint, false,
String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
+ } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -2856,11 +2913,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = orig_arg_name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
iarg.type.cname = arginfo.class_name;
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -2903,9 +2963,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
imethod.proxy_name += "_";
}
- Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname);
+ HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
if (accessor) {
- const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value());
+ const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value);
// We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
// we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
@@ -2942,12 +3002,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// Populate signals
const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map;
- const StringName *k = nullptr;
- while ((k = signal_map.next(k))) {
+ for (const KeyValue<StringName, MethodInfo> &E : signal_map) {
SignalInterface isignal;
- const MethodInfo &method_info = signal_map.get(*k);
+ const MethodInfo &method_info = E.value;
isignal.name = method_info.name;
isignal.cname = method_info.name;
@@ -2962,11 +3021,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = orig_arg_name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
iarg.type.cname = arginfo.class_name;
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -3023,11 +3085,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
List<String> constants;
ClassDB::get_integer_constant_list(type_cname, &constants, true);
- const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
- k = nullptr;
+ const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map;
- while ((k = enum_map.next(k))) {
- StringName enum_proxy_cname = *k;
+ for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) {
+ StringName enum_proxy_cname = E.key;
String enum_proxy_name = enum_proxy_cname.operator String();
if (itype.find_property_by_proxy_name(enum_proxy_cname)) {
// We have several conflicts between enums and PascalCase properties,
@@ -3036,10 +3097,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_proxy_cname = StringName(enum_proxy_name);
}
EnumInterface ienum(enum_proxy_cname);
- const List<StringName> &enum_constants = enum_map.get(*k);
+ ienum.is_flags = E.value.is_bitfield;
+ const List<StringName> &enum_constants = E.value.constants;
for (const StringName &constant_cname : enum_constants) {
String constant_name = constant_cname.operator String();
- int *value = class_info->constant_map.getptr(constant_cname);
+ int64_t *value = class_info->constant_map.getptr(constant_cname);
ERR_FAIL_NULL_V(value, false);
constants.erase(constant_name);
@@ -3066,7 +3128,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
TypeInterface enum_itype;
enum_itype.is_enum = true;
- enum_itype.name = itype.name + "." + String(*k);
+ enum_itype.name = itype.name + "." + String(E.key);
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
TypeInterface::postsetup_enum_type(enum_itype);
@@ -3074,7 +3136,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
for (const String &constant_name : constants) {
- int *value = class_info->constant_map.getptr(StringName(constant_name));
+ int64_t *value = class_info->constant_map.getptr(StringName(constant_name));
ERR_FAIL_NULL_V(value, false);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
@@ -3162,6 +3224,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "new %s" + r_iarg.default_argument;
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
break;
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
+ r_iarg.default_argument = "new %s" + r_iarg.default_argument;
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ break;
case Variant::OBJECT:
ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
"Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
@@ -3202,7 +3269,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
if (transform == Transform2D()) {
r_iarg.default_argument = "Transform2D.Identity";
} else {
- r_iarg.default_argument = "new Transform2D(new Vector2" + transform.elements[0].operator String() + ", new Vector2" + transform.elements[1].operator String() + ", new Vector2" + transform.elements[2].operator String() + ")";
+ r_iarg.default_argument = "new Transform2D(new Vector2" + transform.columns[0].operator String() + ", new Vector2" + transform.columns[1].operator String() + ", new Vector2" + transform.columns[2].operator String() + ")";
}
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
} break;
@@ -3216,6 +3283,15 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
}
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
} break;
+ case Variant::PROJECTION: {
+ Projection transform = p_val.operator Projection();
+ if (transform == Projection()) {
+ r_iarg.default_argument = "Projection.Identity";
+ } else {
+ r_iarg.default_argument = "new Projection(new Vector4" + transform.matrix[0].operator String() + ", new Vector4" + transform.matrix[1].operator String() + ", new Vector4" + transform.matrix[2].operator String() + ", new Vector4" + transform.matrix[3].operator String() + ")";
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
case Variant::BASIS: {
Basis basis = p_val.operator Basis();
if (basis == Basis()) {
@@ -3551,13 +3627,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Array";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 1;
itype.c_out = "\treturn memnew(Array(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
@@ -3567,13 +3644,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Dictionary";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 2;
itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
@@ -3596,11 +3674,11 @@ void BindingsGenerator::_populate_global_constants() {
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");
+ HashMap<String, DocData::ClassDoc>::Iterator match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
CRASH_COND_MSG(!match, "Could not find '@GlobalScope' in DocData.");
- const DocData::ClassDoc &global_scope_doc = match->value();
+ const DocData::ClassDoc &global_scope_doc = match->value;
for (int i = 0; i < global_constants_count; i++) {
String constant_name = CoreConstants::get_global_constant_name(i);
@@ -3615,7 +3693,7 @@ void BindingsGenerator::_populate_global_constants() {
}
}
- int constant_value = CoreConstants::get_global_constant_value(i);
+ int64_t 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);
@@ -3623,6 +3701,7 @@ void BindingsGenerator::_populate_global_constants() {
if (enum_name != StringName()) {
EnumInterface ienum(enum_name);
+ // TODO: ienum.is_flags is always false for core constants since they don't seem to support bitfield enums
List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
if (enum_match) {
enum_match->get().constants.push_back(iconstant);
@@ -3715,8 +3794,8 @@ void BindingsGenerator::_initialize() {
core_custom_icalls.clear();
editor_custom_icalls.clear();
- for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
- _generate_method_icalls(E.get());
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ _generate_method_icalls(E.value);
}
initialized = true;
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index dec4fae8cd..ee170e4558 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -45,12 +45,12 @@ class BindingsGenerator {
struct ConstantInterface {
String name;
String proxy_name;
- int value = 0;
+ int64_t value = 0;
const DocData::ConstantDoc *const_doc;
ConstantInterface() {}
- ConstantInterface(const String &p_name, const String &p_proxy_name, int p_value) {
+ ConstantInterface(const String &p_name, const String &p_proxy_name, int64_t p_value) {
name = p_name;
proxy_name = p_proxy_name;
value = p_value;
@@ -60,6 +60,7 @@ class BindingsGenerator {
struct EnumInterface {
StringName cname;
List<ConstantInterface> constants;
+ bool is_flags = false;
_FORCE_INLINE_ bool operator==(const EnumInterface &p_ienum) const {
return p_ienum.cname == cname;
@@ -87,6 +88,8 @@ class BindingsGenerator {
StringName cname;
bool is_enum = false;
+ List<TypeReference> generic_type_parameters;
+
TypeReference() {}
TypeReference(const StringName &p_cname) :
@@ -206,6 +209,8 @@ class BindingsGenerator {
String name;
StringName cname;
+ int type_parameter_count;
+
/**
* Identifier name of the base class.
*/
@@ -533,24 +538,24 @@ class BindingsGenerator {
bool log_print_enabled = true;
bool initialized = false;
- OrderedHashMap<StringName, TypeInterface> obj_types;
+ HashMap<StringName, TypeInterface> obj_types;
- Map<StringName, TypeInterface> placeholder_types;
- Map<StringName, TypeInterface> builtin_types;
- Map<StringName, TypeInterface> enum_types;
+ HashMap<StringName, TypeInterface> placeholder_types;
+ HashMap<StringName, TypeInterface> builtin_types;
+ HashMap<StringName, TypeInterface> enum_types;
List<EnumInterface> global_enums;
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
- Map<const MethodInterface *, const InternalCall *> method_icalls_map;
+ HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
List<const InternalCall *> generated_icall_funcs;
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
- Map<StringName, List<StringName>> blacklisted_methods;
+ HashMap<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -585,6 +590,9 @@ class BindingsGenerator {
StringName type_Vector2 = StaticCString::create("Vector2");
StringName type_Rect2 = StaticCString::create("Rect2");
StringName type_Vector3 = StaticCString::create("Vector3");
+ StringName type_Vector3i = StaticCString::create("Vector3i");
+ StringName type_Vector4 = StaticCString::create("Vector4");
+ StringName type_Vector4i = StaticCString::create("Vector4i");
// Object not included as it must be checked for all derived classes
static constexpr int nullable_types_count = 17;
@@ -679,6 +687,8 @@ class BindingsGenerator {
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
+ const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
+
StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index 79015686c3..a1789412f4 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -121,10 +121,10 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
case CompletionKind::NODE_PATHS: {
{
// Autoloads.
- OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
- const ProjectSettings::AutoloadInfo &info = E.value();
+ for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
+ const ProjectSettings::AutoloadInfo &info = E.value;
suggestions.push_back(quoted("/root/" + String(info.name)));
}
}
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index f7f710f3f1..8b1b44852f 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -47,7 +47,7 @@
#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
-#include "../utils/osx_utils.h"
+#include "../utils/macos_utils.h"
#include "code_completion.h"
#include "godotsharp_export.h"
@@ -188,8 +188,8 @@ MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_c
return GDMonoMarshal::mono_string_from_godot(error_str);
}
-MonoString *godot_icall_Internal_FullTemplatesDir() {
- String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
+MonoString *godot_icall_Internal_FullExportTemplatesDir() {
+ String full_templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG);
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
}
@@ -198,10 +198,10 @@ MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
}
-MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) {
-#ifdef OSX_ENABLED
+MonoBoolean godot_icall_Internal_IsMacOSAppBundleInstalled(MonoString *p_bundle_id) {
+#ifdef MACOS_ENABLED
String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
- return (MonoBoolean)osx_is_app_bundle_installed(bundle_id);
+ return (MonoBoolean)macos_is_app_bundle_installed(bundle_id);
#else
(void)p_bundle_id; // UNUSED
return (MonoBoolean) false;
@@ -364,9 +364,9 @@ void register_editor_internal_calls() {
// Internals
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h
index a899634d57..8262ac211a 100644
--- a/modules/mono/editor/editor_internal_calls.h
+++ b/modules/mono/editor/editor_internal_calls.h
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EDITOR_INTERNAL_CALL_H
-#define EDITOR_INTERNAL_CALL_H
+#ifndef EDITOR_INTERNAL_CALLS_H
+#define EDITOR_INTERNAL_CALLS_H
void register_editor_internal_calls();
-#endif // EDITOR_INTERNAL_CALL_H
+#endif // EDITOR_INTERNAL_CALLS_H
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
new file mode 100644
index 0000000000..9e1b7ef580
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
@@ -0,0 +1,16 @@
+// meta-description: Basic import script template
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Godot.Object _PostImport(Node scene)
+ {
+ // Modify the contents of the scene upon import.
+ return scene; // Return the modified root node when you're done.
+ }
+}
+#endif
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
new file mode 100644
index 0000000000..bf2c9434e4
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
@@ -0,0 +1,15 @@
+// meta-description: Basic import script template (no comments)
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Godot.Object _PostImport(Node scene)
+ {
+ return scene;
+ }
+}
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index ef135da51a..2febf37f05 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -2,17 +2,27 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that determines if an assembly has scripts. If so, what types of scripts the assembly has.
+ /// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
private readonly bool requiresLookup;
private readonly System.Type[] scriptTypes;
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
public AssemblyHasScriptsAttribute()
{
requiresLookup = true;
}
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
+ /// <param name="scriptTypes">The specified type(s) of scripts.</param>
public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
{
requiresLookup = false;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
index e93bc89811..0b00878e8c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that disables Godot Generators.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class DisableGodotGeneratorsAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
index 6adf044886..46eb128d37 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -2,12 +2,20 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute used to export objects.
+ /// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ExportAttribute : Attribute
{
private PropertyHint hint;
private string hintString;
+ /// <summary>
+ /// Constructs a new ExportAttribute Instance.
+ /// </summary>
+ /// <param name="hint">A hint to the exported object.</param>
+ /// <param name="hintString">A string representing the exported object.</param>
public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
{
this.hint = hint;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
index 55848769d5..8d4ff0fdb7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute for a method.
+ /// </summary>
[AttributeUsage(AttributeTargets.Method)]
internal class GodotMethodAttribute : Attribute
{
@@ -9,6 +12,10 @@ namespace Godot
public string MethodName { get { return methodName; } }
+ /// <summary>
+ /// Constructs a new GodotMethodAttribute instance.
+ /// </summary>
+ /// <param name="methodName">The name of the method.</param>
public GodotMethodAttribute(string methodName)
{
this.methodName = methodName;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
new file mode 100644
index 0000000000..fb37838ffa
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Attribute that changes the RPC mode for the annotated <c>method</c> to the given <see cref="Mode"/>,
+ /// optionally specifying the <see cref="TransferMode"/> and <see cref="TransferChannel"/> (on supported peers).
+ /// See <see cref="MultiplayerAPI.RPCMode"/> and <see cref="MultiplayerPeer.TransferModeEnum"/>.
+ /// By default, methods are not exposed to networking (and RPCs).
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ public class RPCAttribute : Attribute
+ {
+ /// <summary>
+ /// RPC mode for the annotated method.
+ /// </summary>
+ public MultiplayerAPI.RPCMode Mode { get; } = MultiplayerAPI.RPCMode.Disabled;
+
+ /// <summary>
+ /// If the method will also be called locally; otherwise, it is only called remotely.
+ /// </summary>
+ public bool CallLocal { get; set; } = false;
+
+ /// <summary>
+ /// Transfer mode for the annotated method.
+ /// </summary>
+ public MultiplayerPeer.TransferModeEnum TransferMode { get; set; } = MultiplayerPeer.TransferModeEnum.Reliable;
+
+ /// <summary>
+ /// Transfer channel for the annotated mode.
+ /// </summary>
+ public int TransferChannel { get; set; } = 0;
+
+ /// <summary>
+ /// Constructs a <see cref="RPCAttribute"/> instance.
+ /// </summary>
+ /// <param name="mode">The RPC mode to use.</param>
+ public RPCAttribute(MultiplayerAPI.RPCMode mode = MultiplayerAPI.RPCMode.Authority)
+ {
+ Mode = mode;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
deleted file mode 100644
index b8b9bc660c..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Godot
-{
- [AttributeUsage(AttributeTargets.Method)]
- public class AnyPeerAttribute : Attribute { }
-
- [AttributeUsage(AttributeTargets.Method)]
- public class AuthorityAttribute : Attribute { }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 12eb1035c3..3ebb6612de 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -2,11 +2,18 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that contains the path to the object's script.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
private string path;
+ /// <summary>
+ /// Constructs a new ScriptPathAttribute instance.
+ /// </summary>
+ /// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
this.path = path;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 656796c5c7..37bdc42c2d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -526,10 +526,10 @@ namespace Godot
/// <summary>
/// Introduce an additional rotation around the given <paramref name="axis"/>
- /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector.
+ /// by <paramref name="angle"/> (in radians). The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
- /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated basis matrix.</returns>
public Basis Rotated(Vector3 axis, real_t phi)
{
@@ -770,10 +770,10 @@ namespace Godot
/// <summary>
/// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/>
- /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector.
+ /// by <paramref name="angle"/> (in radians). The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
- /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
public Basis(Vector3 axis, real_t phi)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 072e0f20ff..6475237002 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -4,9 +4,16 @@ namespace Godot
{
public static class Dispatcher
{
+ /// <summary>
+ /// Implements an external instance of GodotTaskScheduler.
+ /// </summary>
+ /// <returns>A GodotTaskScheduler instance.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
+ /// <summary>
+ /// Initializes the synchronization context as the context of the GodotTaskScheduler.
+ /// </summary>
public static GodotSynchronizationContext SynchronizationContext =>
godot_icall_DefaultGodotTaskScheduler().Context;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 74aa38386f..bb076a9633 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -239,6 +239,27 @@ namespace Godot
}
/// <summary>
+ /// Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output.
+ /// When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough.
+ ///
+ /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// to print error and warning messages instead of <see cref="Print(object[])"/> or <see cref="PrintRich(object[])"/>.
+ /// This distinguishes them from print messages used for debugging purposes,
+ /// while also displaying a stack trace when an error or warning is printed.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold.
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
+ /// </summary>
+ public static void PrintRich(params object[] what)
+ {
+ godot_icall_GD_print_rich(GetPrintParams(what));
+ }
+
+ /// <summary>
/// Prints the current stack trace information to the console.
/// </summary>
public static void PrintStack()
@@ -562,6 +583,9 @@ namespace Godot
internal static extern void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void godot_icall_GD_print_rich(object[] what);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index c01c926e82..1b599beab5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -14,6 +14,9 @@ namespace Godot
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
+ /// <summary>
+ /// Calls the Key method on each workItem object in the _queue to activate their callbacks.
+ /// </summary>
public void ExecutePendingContinuations()
{
while (_queue.TryTake(out var workItem))
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
index 8eaeea50dc..408bed71b2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
@@ -6,11 +6,25 @@ using System.Threading.Tasks;
namespace Godot
{
+ /// <summary>
+ /// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods
+ /// within the class are used to control the queue and perform the contained tasks.
+ /// </summary>
public class GodotTaskScheduler : TaskScheduler
{
+ /// <summary>
+ /// The current synchronization context.
+ /// </summary>
internal GodotSynchronizationContext Context { get; }
+
+ /// <summary>
+ /// The queue of tasks for the task scheduler.
+ /// </summary>
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
+ /// <summary>
+ /// Constructs a new GodotTaskScheduler instance.
+ /// </summary>
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
@@ -53,12 +67,19 @@ namespace Godot
}
}
+ /// <summary>
+ /// Executes all queued tasks and pending tasks from the current context.
+ /// </summary>
public void Activate()
{
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
+ /// <summary>
+ /// Loops through and attempts to execute each task in _tasks.
+ /// </summary>
+ /// <exception cref="InvalidOperationException"></exception>
private void ExecuteQueuedTasks()
{
while (true)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
index 0397957d00..e747e03c1e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
@@ -1,10 +1,17 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
public interface IAwaitable
{
IAwaiter GetAwaiter();
}
+ /// <summary>
+ /// A templated interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
index d3be9d781c..dec225eb29 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
@@ -2,6 +2,9 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a boolean for completion status and a method that gets the result of completion.
+ /// </summary>
public interface IAwaiter : INotifyCompletion
{
bool IsCompleted { get; }
@@ -9,6 +12,10 @@ namespace Godot
void GetResult();
}
+ /// <summary>
+ /// A templated interface that requires a boolean for completion status and a method that gets the result of completion and returns it.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaiter<out TResult> : INotifyCompletion
{
bool IsCompleted { get; }
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
index c3fa2f3e82..90b4d1b8d3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
@@ -1,5 +1,8 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires methods for before and after serialization.
+ /// </summary>
public interface ISerializationListener
{
void OnBeforeSerialize();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
index ee4d0eed08..50ae2eb112 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
@@ -80,6 +80,11 @@ namespace Godot
private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
/// <summary>
+ /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
+ /// </summary>
+ private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
+
+ /// <summary>
/// Returns the generic type definition of <paramref name="type"/>.
/// </summary>
/// <exception cref="InvalidOperationException">
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index ce213da6a7..36b7d0f80f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -198,6 +198,28 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by
+ /// the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="start">The start value for the interpolation.</param>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static real_t BezierInterpolate(real_t start, real_t control1, real_t control2, real_t end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return start * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Converts an angle expressed in degrees to radians.
/// </summary>
/// <param name="deg">An angle expressed in degrees.</param>
@@ -276,10 +298,14 @@ namespace Godot
/// Returns a normalized value considering the given range.
/// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>.
/// </summary>
- /// <param name="from">The interpolated value.</param>
+ /// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting value of the inverse interpolation.</returns>
+ /// <param name="weight">The interpolated value.</param>
+ /// <returns>
+ /// The resulting value of the inverse interpolation.
+ /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is
+ /// between <paramref name="from"/> and <paramref name="to"/> (inclusive).
+ /// </returns>
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
return (weight - from) / (to - from);
@@ -516,6 +542,21 @@ namespace Godot
}
/// <summary>
+ /// Maps a <paramref name="value"/> from [<paramref name="inFrom"/>, <paramref name="inTo"/>]
+ /// to [<paramref name="outFrom"/>, <paramref name="outTo"/>].
+ /// </summary>
+ /// <param name="value">The value to map.</param>
+ /// <param name="inFrom">The start value for the input interpolation.</param>
+ /// <param name="inTo">The destination value for the input interpolation.</param>
+ /// <param name="outFrom">The start value for the output interpolation.</param>
+ /// <param name="outTo">The destination value for the output interpolation.</param>
+ /// <returns>The resulting mapped value mapped.</returns>
+ public static real_t RangeLerp(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo)
+ {
+ return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value));
+ }
+
+ /// <summary>
/// Rounds <paramref name="s"/> to the nearest whole number,
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 40fb5f8788..9ae01016cb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -170,6 +170,21 @@ namespace Godot
}
/// <summary>
+ /// Returns all names concatenated with a slash character (<c>/</c>).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
+ /// GD.Print(nodepath.GetConcatenatedNames()); // Path2D/PathFollow2D/Sprite2D
+ /// </code>
+ /// </example>
+ /// <returns>The names concatenated with <c>/</c>.</returns>
+ public string GetConcatenatedNames()
+ {
+ return godot_icall_NodePath_get_concatenated_names(GetPtr(this));
+ }
+
+ /// <summary>
/// Returns all subnames concatenated with a colon character (<c>:</c>)
/// as separator, i.e. the right side of the first colon in a node path.
/// </summary>
@@ -269,6 +284,9 @@ namespace Godot
private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 63af1c5892..fd97a71e47 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -118,15 +118,15 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if point is inside the plane.
- /// Comparison uses a custom minimum epsilon threshold.
+ /// Comparison uses a custom minimum tolerance threshold.
/// </summary>
/// <param name="point">The point to check.</param>
- /// <param name="epsilon">The tolerance threshold.</param>
+ /// <param name="tolerance">The tolerance threshold.</param>
/// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns>
- public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
+ public bool HasPoint(Vector3 point, real_t tolerance = Mathf.Epsilon)
{
real_t dist = _normal.Dot(point) - D;
- return Mathf.Abs(dist) <= epsilon;
+ return Mathf.Abs(dist) <= tolerance;
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 8e253388bf..e01db7b88c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -297,9 +297,9 @@ namespace Godot
}
/// <summary>
- /// Rotates the transform by <paramref name="phi"/> (in radians), using matrix multiplication.
+ /// Rotates the transform by <paramref name="angle"/> (in radians), using matrix multiplication.
/// </summary>
- /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
public Transform2D Rotated(real_t phi)
{
@@ -347,7 +347,7 @@ namespace Godot
/// </summary>
/// <param name="offset">The offset to translate by.</param>
/// <returns>The translated matrix.</returns>
- public Transform2D Translated(Vector2 offset)
+ public Transform2D TranslatedLocal(Vector2 offset)
{
Transform2D copy = this;
copy.origin += copy.BasisXform(offset);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 5d9aabdd2f..41565bf680 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -186,11 +186,11 @@ namespace Godot
}
/// <summary>
- /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="phi"/> (in radians),
+ /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians),
/// using matrix multiplication. The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
- /// <param name="phi">The angle to rotate, in radians.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
public Transform3D Rotated(Vector3 axis, real_t phi)
{
@@ -239,7 +239,7 @@ namespace Godot
/// </summary>
/// <param name="offset">The offset to translate by.</param>
/// <returns>The translated matrix.</returns>
- public Transform3D Translated(Vector3 offset)
+ public Transform3D TranslatedLocal(Vector3 offset)
{
return new Transform3D(basis, new Vector3
(
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index d4b623b2ea..7bdbe1c28b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -221,6 +221,27 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination vector.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
+ public Vector2 BezierInterpolate(Vector2 control1, Vector2 control2, Vector2 end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
/// <param name="to">The other vector to point towards.</param>
@@ -470,9 +491,9 @@ namespace Godot
}
/// <summary>
- /// Rotates this vector by <paramref name="phi"/> radians.
+ /// Rotates this vector by <paramref name="angle"/> radians.
/// </summary>
- /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <param name="angle">The angle to rotate by, in radians.</param>
/// <returns>The rotated vector.</returns>
public Vector2 Rotated(real_t phi)
{
@@ -522,9 +543,10 @@ namespace Godot
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
- if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
- // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
- return Lerp(to, weight);
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0)
+ {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
real_t startLength = Mathf.Sqrt(startLengthSquared);
real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index 412a885daa..b61954a84c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -461,7 +461,7 @@ namespace Godot
}
/// <summary>
- /// Multiplies each component of the <see cref="Vector2i"/>
+ /// Divides each component of the <see cref="Vector2i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index eddfa76f10..480165d44a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -214,6 +214,27 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination vector.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
+ public Vector3 BezierInterpolate(Vector3 control1, Vector3 control2, Vector3 end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
/// <param name="to">The other vector to point towards.</param>
@@ -488,11 +509,11 @@ namespace Godot
}
/// <summary>
- /// Rotates this vector around a given <paramref name="axis"/> vector by <paramref name="phi"/> radians.
+ /// Rotates this vector around a given <paramref name="axis"/> vector by <paramref name="angle"/> (in radians).
/// The <paramref name="axis"/> vector must be a normalized vector.
/// </summary>
/// <param name="axis">The vector to rotate around. Must be normalized.</param>
- /// <param name="phi">The angle to rotate by, in radians.</param>
+ /// <param name="angle">The angle to rotate by, in radians.</param>
/// <returns>The rotated vector.</returns>
public Vector3 Rotated(Vector3 axis, real_t phi)
{
@@ -562,9 +583,10 @@ namespace Godot
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
- if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
- // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
- return Lerp(to, weight);
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0)
+ {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
real_t startLength = Mathf.Sqrt(startLengthSquared);
real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index abfd2ae720..0d4894f206 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -449,7 +449,7 @@ namespace Godot
}
/// <summary>
- /// Multiplies each component of the <see cref="Vector3i"/>
+ /// Divides each component of the <see cref="Vector3i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 1fcfe74c86..e59f45bbf6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -18,7 +18,7 @@
<Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
<Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
- <Compile Include="Core\Attributes\RPCAttributes.cs" />
+ <Compile Include="Core\Attributes\RPCAttribute.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index b5f2c98af5..7b9dbc87cf 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -68,7 +68,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
@@ -115,7 +115,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
@@ -148,7 +148,7 @@ MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
if (rc) {
- REF r = rc;
+ Ref<RefCounted> r = rc;
if (!r.is_valid()) {
return nullptr;
}
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 8aead217cf..8b1c2b729e 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -90,6 +90,27 @@ void godot_icall_GD_print(MonoArray *p_what) {
print_line(str);
}
+void godot_icall_GD_print_rich(MonoArray *p_what) {
+ String str;
+ int length = mono_array_length(p_what);
+
+ for (int i = 0; i < length; i++) {
+ MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
+
+ MonoException *exc = nullptr;
+ String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ return;
+ }
+
+ str += elem_str;
+ }
+
+ print_line_rich(str);
+}
+
void godot_icall_GD_printerr(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
@@ -300,6 +321,7 @@ void godot_register_gd_icalls() {
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print_rich", godot_icall_GD_print_rich);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index 9638b23410..f9ad1a9893 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef GLUE_HEADER_H
+#define GLUE_HEADER_H
+
#ifdef MONO_GLUE_ENABLED
#include "../mono_gd/gd_mono_marshal.h"
@@ -84,3 +87,5 @@ void godot_register_glue_header_icalls() {
#include "arguments_vector.h"
#endif // MONO_GLUE_ENABLED
+
+#endif // GLUE_HEADER_H
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index 0ea9814b1a..16e1509eb0 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -68,6 +68,10 @@ MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx));
}
+MonoString *godot_icall_NodePath_get_concatenated_names(NodePath *p_ptr) {
+ return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_names());
+}
+
MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames());
}
@@ -85,6 +89,7 @@ void godot_register_nodepath_icalls() {
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count);
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index cb2b60fcce..f17b24e399 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -184,7 +184,7 @@ private:
data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
#endif
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
@@ -222,7 +222,7 @@ private:
data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
#endif
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib");
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index 8ed21c323f..c159bb9eea 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -36,7 +36,7 @@
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable>::List ManagedCallable::instances;
-Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
+RBMap<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
Mutex ManagedCallable::instances_mutex;
#endif
@@ -66,9 +66,8 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust
}
uint32_t ManagedCallable::hash() const {
- // hmm
uint32_t hash = delegate_invoke->get_name().hash();
- return hash_djb2_one_64(delegate_handle.handle, hash);
+ return hash_murmur3_one_64(delegate_handle.handle, hash);
}
String ManagedCallable::get_as_text() const {
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index d50a8a7b08..11bee6cf60 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -48,7 +48,7 @@ class ManagedCallable : public CallableCustom {
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
static SelfList<ManagedCallable>::List instances;
- static Map<ManagedCallable *, Array> instances_pending_reload;
+ static RBMap<ManagedCallable *, Array> instances_pending_reload;
static Mutex instances_mutex;
#endif
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index ab9e508c99..e2aff1d19d 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CSHARP_GC_HANDLE_H
-#define CSHARP_GC_HANDLE_H
+#ifndef MONO_GC_HANDLE_H
+#define MONO_GC_HANDLE_H
#include <mono/jit/jit.h>
@@ -104,4 +104,4 @@ public:
~MonoGCHandleRef() { release(); }
};
-#endif // CSHARP_GC_HANDLE_H
+#endif // MONO_GC_HANDLE_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index e98ce8f6c1..d3d3bb2bef 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -55,7 +55,7 @@
#ifdef ANDROID_ENABLED
#include "android_mono_config.h"
#include "support/android_support.h"
-#elif defined(IPHONE_ENABLED)
+#elif defined(IOS_ENABLED)
#include "support/ios_support.h"
#endif
@@ -188,7 +188,7 @@ MonoDomain *gd_initialize_mono_runtime() {
MonoDomain *gd_initialize_mono_runtime() {
gd_mono_debug_init();
-#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
+#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED)
// I don't know whether this actually matters or not
const char *runtime_version = "mobile";
#else
@@ -263,7 +263,7 @@ void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_di
if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
r_config_dir = mono_reg_info.config_dir;
}
-#elif defined(OSX_ENABLED)
+#elif defined(MACOS_ENABLED)
const char *c_assembly_rootdir = mono_assembly_getrootdir();
const char *c_config_dir = mono_get_config_dir();
@@ -343,7 +343,7 @@ void GDMono::initialize() {
#if defined(ANDROID_ENABLED)
gdmono::android::support::initialize();
-#elif defined(IPHONE_ENABLED)
+#elif defined(IOS_ENABLED)
gdmono::ios::support::initialize();
#endif
@@ -1167,9 +1167,8 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- GDMonoAssembly *assembly = domain_assemblies.get(*k);
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ GDMonoAssembly *assembly = E.value;
if (assembly->get_image() == image) {
GDMonoClass *klass = assembly->get_class(p_raw_class);
if (klass) {
@@ -1190,9 +1189,8 @@ GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &
int32_t domain_id = mono_domain_get_id(mono_domain_get());
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- GDMonoAssembly *assembly = domain_assemblies.get(*k);
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ GDMonoAssembly *assembly = E.value;
klass = assembly->get_class(p_namespace, p_name);
if (klass) {
return klass;
@@ -1205,9 +1203,8 @@ GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &
void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
- const String *k = nullptr;
- while ((k = domain_assemblies.next(k))) {
- memdelete(domain_assemblies.get(*k));
+ for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
+ memdelete(E.value);
}
assemblies.erase(p_domain_id);
@@ -1298,13 +1295,11 @@ GDMono::~GDMono() {
// Leave the rest to 'mono_jit_cleanup'
#endif
- const int32_t *k = nullptr;
- while ((k = assemblies.next(k))) {
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k);
+ for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) {
+ const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value;
- const String *kk = nullptr;
- while ((kk = domain_assemblies.next(kk))) {
- memdelete(domain_assemblies.get(*kk));
+ for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) {
+ memdelete(F.value);
}
}
assemblies.clear();
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 3991b14612..42c6b6305f 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -412,10 +412,10 @@ GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const Stri
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_NULL_V(image, nullptr);
- Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
+ HashMap<MonoClass *, GDMonoClass *>::Iterator match = cached_raw.find(p_mono_class);
if (match) {
- return match->value();
+ return match->value;
}
StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index a96357b082..0a3ae6c4fe 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -36,7 +36,7 @@
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "gd_mono_utils.h"
class GDMonoAssembly {
@@ -79,7 +79,7 @@ class GDMonoAssembly {
#endif
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
- Map<MonoClass *, GDMonoClass *> cached_raw;
+ HashMap<MonoClass *, GDMonoClass *> cached_raw;
static Vector<String> search_dirs;
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index d8fd244067..69d8c7edc9 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -108,9 +108,12 @@ void CachedData::clear_godot_api_cache() {
class_Transform2D = nullptr;
class_Vector3 = nullptr;
class_Vector3i = nullptr;
+ class_Vector4 = nullptr;
+ class_Vector4i = nullptr;
class_Basis = nullptr;
class_Quaternion = nullptr;
class_Transform3D = nullptr;
+ class_Projection = nullptr;
class_AABB = nullptr;
class_Color = nullptr;
class_Plane = nullptr;
@@ -140,8 +143,11 @@ void CachedData::clear_godot_api_cache() {
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
- class_AnyPeerAttribute = nullptr;
- class_AuthorityAttribute = nullptr;
+ class_RPCAttribute = nullptr;
+ property_RPCAttribute_Mode = nullptr;
+ property_RPCAttribute_CallLocal = nullptr;
+ property_RPCAttribute_TransferMode = nullptr;
+ property_RPCAttribute_TransferChannel = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
class_ScriptPathAttribute = nullptr;
@@ -176,6 +182,7 @@ void CachedData::clear_godot_api_cache() {
methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
+ methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
@@ -235,9 +242,12 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Transform2D, GODOT_API_CLASS(Transform2D));
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i));
+ CACHE_CLASS_AND_CHECK(Vector4, GODOT_API_CLASS(Vector4));
+ CACHE_CLASS_AND_CHECK(Vector4i, GODOT_API_CLASS(Vector4i));
CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion));
CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D));
+ CACHE_CLASS_AND_CHECK(Projection, GODOT_API_CLASS(Projection));
CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
@@ -267,8 +277,11 @@ void update_godot_api_cache() {
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
- CACHE_CLASS_AND_CHECK(AnyPeerAttribute, GODOT_API_CLASS(AnyPeerAttribute));
- CACHE_CLASS_AND_CHECK(AuthorityAttribute, GODOT_API_CLASS(AuthorityAttribute));
+ CACHE_CLASS_AND_CHECK(RPCAttribute, GODOT_API_CLASS(RPCAttribute));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, Mode, CACHED_CLASS(RPCAttribute)->get_property("Mode"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, CallLocal, CACHED_CLASS(RPCAttribute)->get_property("CallLocal"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferMode, CACHED_CLASS(RPCAttribute)->get_property("TransferMode"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferChannel, CACHED_CLASS(RPCAttribute)->get_property("TransferChannel"));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute));
@@ -300,6 +313,7 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 4000342c94..e9cc26899e 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -79,9 +79,12 @@ struct CachedData {
GDMonoClass *class_Transform2D = nullptr;
GDMonoClass *class_Vector3 = nullptr;
GDMonoClass *class_Vector3i = nullptr;
+ GDMonoClass *class_Vector4 = nullptr;
+ GDMonoClass *class_Vector4i = nullptr;
GDMonoClass *class_Basis = nullptr;
GDMonoClass *class_Quaternion = nullptr;
GDMonoClass *class_Transform3D = nullptr;
+ GDMonoClass *class_Projection = nullptr;
GDMonoClass *class_AABB = nullptr;
GDMonoClass *class_Color = nullptr;
GDMonoClass *class_Plane = nullptr;
@@ -111,8 +114,11 @@ struct CachedData {
GDMonoField *field_ExportAttribute_hintString = nullptr;
GDMonoClass *class_SignalAttribute = nullptr;
GDMonoClass *class_ToolAttribute = nullptr;
- GDMonoClass *class_AnyPeerAttribute = nullptr;
- GDMonoClass *class_AuthorityAttribute = nullptr;
+ GDMonoClass *class_RPCAttribute = nullptr;
+ GDMonoProperty *property_RPCAttribute_Mode = nullptr;
+ GDMonoProperty *property_RPCAttribute_CallLocal = nullptr;
+ GDMonoProperty *property_RPCAttribute_TransferMode = nullptr;
+ GDMonoProperty *property_RPCAttribute_TransferChannel = nullptr;
GDMonoClass *class_GodotMethodAttribute = nullptr;
GDMonoField *field_GodotMethodAttribute_methodName = nullptr;
GDMonoClass *class_ScriptPathAttribute = nullptr;
@@ -147,6 +153,7 @@ struct CachedData {
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
+ GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 3fc0f16e05..51c5aa3542 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -247,7 +247,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
if (existing_method) {
memdelete(*existing_method); // Must delete old one
}
- methods.set(key, method);
+ methods.insert(key, method);
break;
}
@@ -266,11 +266,9 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, nullptr);
- const MethodKey *k = nullptr;
-
- while ((k = methods.next(k))) {
- if (k->name == p_name) {
- return methods.get(*k);
+ for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
+ if (E.key.name == p_name) {
+ return E.value;
}
}
@@ -307,7 +305,7 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_param
if (raw_method) {
GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
- methods.set(key, method);
+ methods.insert(key, method);
return method;
}
@@ -342,7 +340,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
}
GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
- methods.set(key, method);
+ methods.insert(key, method);
return method;
}
@@ -362,10 +360,10 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
}
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
- Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
+ HashMap<StringName, GDMonoField *>::Iterator result = fields.find(p_name);
if (result) {
- return result->value();
+ return result->value;
}
if (fields_fetched) {
@@ -394,10 +392,10 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_field_get_name(raw_field));
- Map<StringName, GDMonoField *>::Element *match = fields.find(name);
+ HashMap<StringName, GDMonoField *>::Iterator match = fields.find(name);
if (match) {
- fields_list.push_back(match->get());
+ fields_list.push_back(match->value);
} else {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(name, field);
@@ -411,10 +409,10 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
}
GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
- Map<StringName, GDMonoProperty *>::Element *result = properties.find(p_name);
+ HashMap<StringName, GDMonoProperty *>::Iterator result = properties.find(p_name);
if (result) {
- return result->value();
+ return result->value;
}
if (properties_fetched) {
@@ -443,10 +441,10 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_property_get_name(raw_property));
- Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
+ HashMap<StringName, GDMonoProperty *>::Iterator match = properties.find(name);
if (match) {
- properties_list.push_back(match->get());
+ properties_list.push_back(match->value);
} else {
GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
properties.insert(name, property);
@@ -479,10 +477,10 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
if (mono_class_is_delegate(raw_class)) {
StringName name = String::utf8(mono_class_get_name(raw_class));
- Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
+ HashMap<StringName, GDMonoClass *>::Iterator match = delegates.find(name);
if (match) {
- delegates_list.push_back(match->get());
+ delegates_list.push_back(match->value);
} else {
GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly));
delegates.insert(name, delegate);
@@ -549,9 +547,8 @@ GDMonoClass::~GDMonoClass() {
Vector<GDMonoMethod *> deleted_methods;
deleted_methods.resize(methods.size());
- const MethodKey *k = nullptr;
- while ((k = methods.next(k))) {
- GDMonoMethod *method = methods.get(*k);
+ for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
+ GDMonoMethod *method = E.value;
if (method) {
for (int i = 0; i < offset; i++) {
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index b32d561f61..6b35da30f9 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -32,7 +32,7 @@
#define GD_MONO_CLASS_H
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
@@ -85,15 +85,15 @@ class GDMonoClass {
Vector<GDMonoMethod *> method_list;
bool fields_fetched;
- Map<StringName, GDMonoField *> fields;
+ HashMap<StringName, GDMonoField *> fields;
Vector<GDMonoField *> fields_list;
bool properties_fetched;
- Map<StringName, GDMonoProperty *> properties;
+ HashMap<StringName, GDMonoProperty *> properties;
Vector<GDMonoProperty *> properties_list;
bool delegates_fetched;
- Map<StringName, GDMonoClass *> delegates;
+ HashMap<StringName, GDMonoClass *> delegates;
Vector<GDMonoClass *> delegates_list;
friend class GDMonoAssembly;
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 333a06c94a..cb025fc67a 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -140,6 +140,18 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (tclass == CACHED_CLASS(Vector4)) {
+ GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4());
+ mono_field_set_value(p_object, mono_field, &from);
+ break;
+ }
+
+ if (tclass == CACHED_CLASS(Vector4i)) {
+ GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i());
+ mono_field_set_value(p_object, mono_field, &from);
+ break;
+ }
+
if (tclass == CACHED_CLASS(Basis)) {
GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
mono_field_set_value(p_object, mono_field, &from);
@@ -158,6 +170,12 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
break;
}
+ if (tclass == CACHED_CLASS(Projection)) {
+ GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection());
+ mono_field_set_value(p_object, mono_field, &from);
+ break;
+ }
+
if (tclass == CACHED_CLASS(AABB)) {
GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
mono_field_set_value(p_object, mono_field, &from);
@@ -328,6 +346,14 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
mono_field_set_value(p_object, mono_field, &from);
} break;
+ case Variant::VECTOR4: {
+ GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_value.operator ::Vector4());
+ mono_field_set_value(p_object, mono_field, &from);
+ } break;
+ case Variant::VECTOR4I: {
+ GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_value.operator ::Vector4i());
+ mono_field_set_value(p_object, mono_field, &from);
+ } break;
case Variant::TRANSFORM2D: {
GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
mono_field_set_value(p_object, mono_field, &from);
@@ -352,6 +378,10 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D());
mono_field_set_value(p_object, mono_field, &from);
} break;
+ case Variant::PROJECTION: {
+ GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_value.operator ::Projection());
+ mono_field_set_value(p_object, mono_field, &from);
+ } break;
case Variant::COLOR: {
GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
mono_field_set_value(p_object, mono_field, &from);
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
index 87ef245f3f..1d30f7a369 100644
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDMONOFIELD_H
-#define GDMONOFIELD_H
+#ifndef GD_MONO_FIELD_H
+#define GD_MONO_FIELD_H
#include "gd_mono.h"
#include "gd_mono_header.h"
@@ -75,4 +75,4 @@ public:
~GDMonoField();
};
-#endif // GDMONOFIELD_H
+#endif // GD_MONO_FIELD_H
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
index 9fc35f8e31..93ba6a410e 100644
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -35,7 +35,7 @@
#include "core/typedefs.h"
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
+#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
// We have custom mono log callbacks for WASM and iOS
#define GD_MONO_LOG_ENABLED
#endif
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index 957abca37b..a860442764 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -99,6 +99,13 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
if (vtclass == CACHED_CLASS(Vector3i)) {
return Variant::VECTOR3I;
}
+ if (vtclass == CACHED_CLASS(Vector4)) {
+ return Variant::VECTOR4;
+ }
+
+ if (vtclass == CACHED_CLASS(Vector4i)) {
+ return Variant::VECTOR4I;
+ }
if (vtclass == CACHED_CLASS(Basis)) {
return Variant::BASIS;
@@ -111,7 +118,9 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
if (vtclass == CACHED_CLASS(Transform3D)) {
return Variant::TRANSFORM3D;
}
-
+ if (vtclass == CACHED_CLASS(Projection)) {
+ return Variant::PROJECTION;
+ }
if (vtclass == CACHED_CLASS(AABB)) {
return Variant::AABB;
}
@@ -539,6 +548,14 @@ MonoObject *variant_to_mono_object(const Variant &p_var) {
GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
}
+ case Variant::VECTOR4: {
+ GDMonoMarshal::M_Vector4 from = MARSHALLED_OUT(Vector4, p_var.operator ::Vector4());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4), &from);
+ }
+ case Variant::VECTOR4I: {
+ GDMonoMarshal::M_Vector4i from = MARSHALLED_OUT(Vector4i, p_var.operator ::Vector4i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector4i), &from);
+ }
case Variant::PLANE: {
GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
@@ -559,6 +576,10 @@ MonoObject *variant_to_mono_object(const Variant &p_var) {
GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from);
}
+ case Variant::PROJECTION: {
+ GDMonoMarshal::M_Projection from = MARSHALLED_OUT(Projection, p_var.operator ::Projection());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Projection), &from);
+ }
case Variant::COLOR: {
GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index eb33c6119e..3b6fd25d71 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDMONOMARSHAL_H
-#define GDMONOMARSHAL_H
+#ifndef GD_MONO_MARSHAL_H
+#define GD_MONO_MARSHAL_H
#include "core/variant/variant.h"
@@ -256,6 +256,18 @@ enum {
offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
offsetof(Vector3, z) == (sizeof(real_t) * 2)),
+ MATCHES_Vector4 = (MATCHES_real_t && (sizeof(Vector4) == (sizeof(real_t) * 4)) &&
+ offsetof(Vector4, x) == (sizeof(real_t) * 0) &&
+ offsetof(Vector4, y) == (sizeof(real_t) * 1) &&
+ offsetof(Vector4, z) == (sizeof(real_t) * 2) &&
+ offsetof(Vector4, w) == (sizeof(real_t) * 3)),
+
+ MATCHES_Vector4i = (MATCHES_int && (sizeof(Vector4i) == (sizeof(int32_t) * 4i)) &&
+ offsetof(Vector4i, x) == (sizeof(int32_t) * 0) &&
+ offsetof(Vector4i, y) == (sizeof(int32_t) * 1) &&
+ offsetof(Vector4i, z) == (sizeof(int32_t) * 2) &&
+ offsetof(Vector4i, w) == (sizeof(int32_t) * 3)),
+
MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) &&
offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
@@ -273,6 +285,8 @@ enum {
offsetof(Transform3D, basis) == 0 &&
offsetof(Transform3D, origin) == sizeof(Basis)),
+ MATCHES_Projection = (MATCHES_Vector4 && (sizeof(Projection) == (sizeof(Vector4) * 4))),
+
MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) &&
offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
offsetof(AABB, size) == (sizeof(Vector3) * 1)),
@@ -291,9 +305,9 @@ enum {
// In the future we may force this if we want to ref return these structs
#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
/* clang-format off */
-static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
- MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_AABB && MATCHES_Color &&
- MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i);
+static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 && MATCHES_Vector4 &&
+ MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_Projection && MATCHES_AABB && MATCHES_Color &&
+ MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i && MATCHES_Vector4i);
/* clang-format on */
#endif
} // namespace InteropLayout
@@ -367,9 +381,9 @@ struct M_Transform2D {
static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) {
M_Transform2D ret = {
- M_Vector2::convert_from(p_from.elements[0]),
- M_Vector2::convert_from(p_from.elements[1]),
- M_Vector2::convert_from(p_from.elements[2])
+ M_Vector2::convert_from(p_from.columns[0]),
+ M_Vector2::convert_from(p_from.columns[1]),
+ M_Vector2::convert_from(p_from.columns[2])
};
return ret;
}
@@ -401,6 +415,32 @@ struct M_Vector3i {
}
};
+struct M_Vector4 {
+ real_t x, y, z, w;
+
+ static _FORCE_INLINE_ Vector4 convert_to(const M_Vector4 &p_from) {
+ return Vector4(p_from.x, p_from.y, p_from.z, p_from.w);
+ }
+
+ static _FORCE_INLINE_ M_Vector4 convert_from(const Vector4 &p_from) {
+ M_Vector4 ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+ return ret;
+ }
+};
+
+struct M_Vector4i {
+ int32_t x, y, z, w;
+
+ static _FORCE_INLINE_ Vector4i convert_to(const M_Vector4i &p_from) {
+ return Vector4i(p_from.x, p_from.y, p_from.z, p_from.w);
+ }
+
+ static _FORCE_INLINE_ M_Vector4i convert_from(const Vector4i &p_from) {
+ M_Vector4i ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+ return ret;
+ }
+};
+
struct M_Basis {
M_Vector3 elements[3];
@@ -412,9 +452,9 @@ struct M_Basis {
static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) {
M_Basis ret = {
- M_Vector3::convert_from(p_from.elements[0]),
- M_Vector3::convert_from(p_from.elements[1]),
- M_Vector3::convert_from(p_from.elements[2])
+ M_Vector3::convert_from(p_from.rows[0]),
+ M_Vector3::convert_from(p_from.rows[1]),
+ M_Vector3::convert_from(p_from.rows[2])
};
return ret;
}
@@ -447,6 +487,22 @@ struct M_Transform3D {
}
};
+struct M_Projection {
+ M_Vector4 vec1;
+ M_Vector4 vec2;
+ M_Vector4 vec3;
+ M_Vector4 vec4;
+
+ static _FORCE_INLINE_ Projection convert_to(const M_Projection &p_from) {
+ return Projection(M_Vector4::convert_to(p_from.vec1), M_Vector4::convert_to(p_from.vec2), M_Vector4::convert_to(p_from.vec3), M_Vector4::convert_to(p_from.vec4));
+ }
+
+ static _FORCE_INLINE_ M_Projection convert_from(const Projection &p_from) {
+ M_Projection ret = { M_Vector4::convert_from(p_from.matrix[0]), M_Vector4::convert_from(p_from.matrix[1]), M_Vector4::convert_from(p_from.matrix[2]), M_Vector4::convert_from(p_from.matrix[3]) };
+ return ret;
+ }
+};
+
struct M_AABB {
M_Vector3 position;
M_Vector3 size;
@@ -533,8 +589,11 @@ DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
DECL_TYPE_MARSHAL_TEMPLATES(Vector3i)
DECL_TYPE_MARSHAL_TEMPLATES(Basis)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector4)
+DECL_TYPE_MARSHAL_TEMPLATES(Vector4i)
DECL_TYPE_MARSHAL_TEMPLATES(Quaternion)
DECL_TYPE_MARSHAL_TEMPLATES(Transform3D)
+DECL_TYPE_MARSHAL_TEMPLATES(Projection)
DECL_TYPE_MARSHAL_TEMPLATES(AABB)
DECL_TYPE_MARSHAL_TEMPLATES(Color)
DECL_TYPE_MARSHAL_TEMPLATES(Plane)
@@ -543,4 +602,4 @@ DECL_TYPE_MARSHAL_TEMPLATES(Plane)
#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from))
} // namespace GDMonoMarshal
-#endif // GDMONOMARSHAL_H
+#endif // GD_MONO_MARSHAL_H
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
index bb163b89bc..0180dee3ea 100644
--- a/modules/mono/mono_gd/gd_mono_method_thunk.h
+++ b/modules/mono/mono_gd/gd_mono_method_thunk.h
@@ -39,7 +39,7 @@
#include "gd_mono_method.h"
#include "gd_mono_utils.h"
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
+#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
#define HAVE_METHOD_THUNKS
#endif
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index a884bf4da0..1983d6ebe2 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -70,7 +70,7 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
void *data = CSharpLanguage::get_instance_binding(unmanaged);
ERR_FAIL_NULL_V(data, nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, nullptr);
MonoGCHandleData &gchandle = script_binding.gchandle;
@@ -614,6 +614,14 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
return (bool)res;
}
+bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
+ NO_GLUE_RET(false);
+ MonoException *exc = nullptr;
+ MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
+ UNHANDLED_EXCEPTION(exc);
+ return (bool)res;
+}
+
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 4c2c2c93c2..300cacfa4b 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_MONOUTILS_H
-#define GD_MONOUTILS_H
+#ifndef GD_MONO_UTILS_H
+#define GD_MONO_UTILS_H
#include <mono/metadata/threads.h>
@@ -61,6 +61,7 @@ bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype);
bool type_is_generic_ienumerable(MonoReflectionType *p_reftype);
bool type_is_generic_icollection(MonoReflectionType *p_reftype);
bool type_is_generic_idictionary(MonoReflectionType *p_reftype);
+bool type_has_flags_attribute(MonoReflectionType *p_reftype);
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype);
@@ -201,4 +202,4 @@ void add_internal_call(const char *p_name, R (*p_func)(P...)) {
#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0)
#endif
-#endif // GD_MONOUTILS_H
+#endif // GD_MONO_UTILS_H
diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h
index 2f444d5089..03e86df698 100644
--- a/modules/mono/mono_gd/support/ios_support.h
+++ b/modules/mono/mono_gd/support/ios_support.h
@@ -31,7 +31,7 @@
#ifndef IOS_SUPPORT_H
#define IOS_SUPPORT_H
-#if defined(IPHONE_ENABLED)
+#if defined(IOS_ENABLED)
#include "core/string/ustring.h"
@@ -45,6 +45,6 @@ void cleanup();
} // namespace ios
} // namespace gdmono
-#endif // IPHONE_ENABLED
+#endif // IOS_ENABLED
#endif // IOS_SUPPORT_H
diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm
index df97dfba49..7c941b9d1e 100644
--- a/modules/mono/mono_gd/support/ios_support.mm
+++ b/modules/mono/mono_gd/support/ios_support.mm
@@ -30,7 +30,7 @@
#include "ios_support.h"
-#if defined(IPHONE_ENABLED)
+#if defined(IOS_ENABLED)
#import <Foundation/Foundation.h>
#include <os/log.h>
@@ -147,4 +147,4 @@ GD_PINVOKE_EXPORT void xamarin_start_wwan(const char *p_uri) {
os_log_error(OS_LOG_DEFAULT, "Not implemented: 'xamarin_start_wwan'");
}
-#endif // IPHONE_ENABLED
+#endif // IOS_ENABLED
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 531a4bb11f..755e1f7a30 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -40,7 +40,11 @@ Ref<ResourceFormatSaverCSharpScript> resource_saver_cs;
mono_bind::GodotSharp *_godotsharp = nullptr;
-void register_mono_types() {
+void initialize_mono_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_CLASS(CSharpScript);
_godotsharp = memnew(mono_bind::GodotSharp);
@@ -59,7 +63,11 @@ void register_mono_types() {
ResourceSaver::add_resource_format_saver(resource_saver_cs);
}
-void unregister_mono_types() {
+void uninitialize_mono_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
ScriptServer::unregister_language(script_language_cs);
if (script_language_cs) {
diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h
index 12f7e36f02..bc2690c277 100644
--- a/modules/mono/register_types.h
+++ b/modules/mono/register_types.h
@@ -31,7 +31,9 @@
#ifndef MONO_REGISTER_TYPES_H
#define MONO_REGISTER_TYPES_H
-void register_mono_types();
-void unregister_mono_types();
+#include "modules/register_module_types.h"
+
+void initialize_mono_module(ModuleInitializationLevel p_level);
+void uninitialize_mono_module(ModuleInitializationLevel p_level);
#endif // MONO_REGISTER_TYPES_H
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 315a9c29f6..618e1b58e0 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -63,7 +63,7 @@ bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const Callab
uint32_t SignalAwaiterCallable::hash() const {
uint32_t hash = signal.hash();
- return hash_djb2_one_64(target_id, hash);
+ return hash_murmur3_one_64(target_id, hash);
}
String SignalAwaiterCallable::get_as_text() const {
@@ -164,7 +164,7 @@ bool EventSignalCallable::compare_less(const CallableCustom *p_a, const Callable
uint32_t EventSignalCallable::hash() const {
uint32_t hash = event_signal->field->get_name().hash();
- return hash_djb2_one_64(owner->get_instance_id(), hash);
+ return hash_murmur3_one_64(owner->get_instance_id(), hash);
}
String EventSignalCallable::get_as_text() const {
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/macos_utils.cpp
index 835c611709..cd4f7a827e 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/macos_utils.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* osx_utils.cpp */
+/* macos_utils.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "osx_utils.h"
+#include "macos_utils.h"
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
#include "core/string/print_string.h"
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
+#import <CoreFoundation/CoreFoundation.h>
+#import <CoreServices/CoreServices.h>
-bool osx_is_app_bundle_installed(const String &p_bundle_id) {
+bool macos_is_app_bundle_installed(const String &p_bundle_id) {
CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8);
CFArrayRef result = LSCopyApplicationURLsForBundleIdentifier(bundle_id, nullptr);
CFRelease(bundle_id);
diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/macos_utils.h
index 2f6c6dad51..ca4957f5a7 100644
--- a/modules/mono/utils/osx_utils.h
+++ b/modules/mono/utils/macos_utils.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* osx_utils.h */
+/* macos_utils.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,13 +30,13 @@
#include "core/string/ustring.h"
-#ifndef OSX_UTILS_H
-#define OSX_UTILS_H
+#ifndef MONO_MACOS_UTILS_H
+#define MONO_MACOS_UTILS_H
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
-bool osx_is_app_bundle_installed(const String &p_bundle_id);
+bool macos_is_app_bundle_installed(const String &p_bundle_id);
#endif
-#endif // OSX_UTILS_H
+#endif // MONO_MACOS_UTILS_H
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index 2ca1a4cbf1..b7bd9a2495 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef UTIL_MACROS_H
-#define UTIL_MACROS_H
+#ifndef MONO_MACROS_H
+#define MONO_MACROS_H
#define _GD_VARNAME_CONCAT_B_(m_ignore, m_name) m_name
#define _GD_VARNAME_CONCAT_A_(m_a, m_b, m_c) _GD_VARNAME_CONCAT_B_(hello there, m_a##m_b##m_c)
@@ -69,4 +69,4 @@ public:
#define SCOPE_EXIT \
auto GD_UNIQUE_NAME(gd_scope_exit) = gdmono::ScopeExitAux() + [=]() -> void
-#endif // UTIL_MACROS_H
+#endif // MONO_MACROS_H
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index 15a0b28181..a1905dfcfe 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -62,7 +62,8 @@ String cwd() {
}
String result;
- if (result.parse_utf16(buffer.ptr())) {
+ result.parse_utf16(buffer.ptr());
+ if (result.is_empty()) {
return ".";
}
return result.simplify_path();
@@ -73,7 +74,7 @@ String cwd() {
}
String result;
- if (result.parse_utf8(buffer)) {
+ if (result.parse_utf8(buffer) != OK) {
return ".";
}
@@ -114,7 +115,8 @@ String realpath(const String &p_path) {
::CloseHandle(hFile);
String result;
- if (result.parse_utf16(buffer.ptr())) {
+ result.parse_utf16(buffer.ptr());
+ if (result.is_empty()) {
return p_path;
}
@@ -127,10 +129,10 @@ String realpath(const String &p_path) {
}
String result;
- bool parse_ok = result.parse_utf8(resolved_path);
+ Error parse_ok = result.parse_utf8(resolved_path);
::free(resolved_path);
- if (parse_ok) {
+ if (parse_ok != OK) {
return p_path;
}
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index a8cd8daf04..9a2c757361 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PATH_UTILS_H
-#define PATH_UTILS_H
+#ifndef MONO_PATH_UTILS_H
+#define MONO_PATH_UTILS_H
#include "core/string/string_builder.h"
#include "core/string/ustring.h"
@@ -58,4 +58,4 @@ String realpath(const String &p_path);
String relative_to(const String &p_path, const String &p_relative_to);
} // namespace path
-#endif // PATH_UTILS_H
+#endif // MONO_PATH_UTILS_H
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index e6975611d2..975f2d8332 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -145,7 +145,7 @@ bool is_csharp_keyword(const String &p_name) {
p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
- p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
+ p_name == "foreach" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
@@ -178,7 +178,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
w[len] = 0;
String source;
- if (source.parse_utf8((const char *)w)) {
+ if (source.parse_utf8((const char *)w) != OK) {
ERR_FAIL_V(ERR_INVALID_DATA);
}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index d79888716a..fa4c5e89f4 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef STRING_FORMAT_H
-#define STRING_FORMAT_H
+#ifndef MONO_STRING_UTILS_H
+#define MONO_STRING_UTILS_H
#include "core/string/ustring.h"
#include "core/variant/variant.h"
@@ -59,4 +59,4 @@ String str_format(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE
char *str_format_new(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_1_2;
char *str_format_new(const char *p_format, va_list p_list) _PRINTF_FORMAT_ATTRIBUTE_1_0;
-#endif // STRING_FORMAT_H
+#endif // MONO_STRING_UTILS_H
diff --git a/modules/msdfgen/register_types.cpp b/modules/msdfgen/register_types.cpp
index 69855d93fe..2d3a2a0c69 100644
--- a/modules/msdfgen/register_types.cpp
+++ b/modules/msdfgen/register_types.cpp
@@ -30,6 +30,14 @@
#include "register_types.h"
-void register_msdfgen_types() {}
+void initialize_msdfgen_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
-void unregister_msdfgen_types() {}
+void uninitialize_msdfgen_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/msdfgen/register_types.h b/modules/msdfgen/register_types.h
index 0e2fed2ce8..749204f390 100644
--- a/modules/msdfgen/register_types.h
+++ b/modules/msdfgen/register_types.h
@@ -31,7 +31,9 @@
#ifndef MSDFGEN_REGISTER_TYPES_H
#define MSDFGEN_REGISTER_TYPES_H
-void register_msdfgen_types();
-void unregister_msdfgen_types();
+#include "modules/register_module_types.h"
+
+void initialize_msdfgen_module(ModuleInitializationLevel p_level);
+void uninitialize_msdfgen_module(ModuleInitializationLevel p_level);
#endif // MSDFGEN_REGISTER_TYPES_H
diff --git a/modules/multiplayer/SCsub b/modules/multiplayer/SCsub
new file mode 100644
index 0000000000..ff33655537
--- /dev/null
+++ b/modules/multiplayer/SCsub
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_mp = env_modules.Clone()
+
+module_obj = []
+env_mp.add_source_files(module_obj, "*.cpp")
+
+if env["tools"]:
+ env_mp.add_source_files(module_obj, "editor/*.cpp")
+
+env.modules_sources += module_obj
diff --git a/modules/multiplayer/config.py b/modules/multiplayer/config.py
new file mode 100644
index 0000000000..414bf0afcf
--- /dev/null
+++ b/modules/multiplayer/config.py
@@ -0,0 +1,19 @@
+def can_build(env, platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "SceneReplicationConfig",
+ "SceneMultiplayer",
+ "MultiplayerSpawner",
+ "MultiplayerSynchronizer",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
new file mode 100644
index 0000000000..44ab34f52c
--- /dev/null
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerSpawner" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ </brief_description>
+ <description>
+ This node uses [method MultiplayerAPI.object_configuration_add] to notify spawns passing the spawned node as the [code]object[/code] and itself as the [code]configuration[/code], and [method MultiplayerAPI.object_configuration_remove] to notify despawns in a similar way.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_spawn_custom" qualifiers="virtual">
+ <return type="Object" />
+ <argument index="0" name="data" type="Variant" />
+ <description>
+ </description>
+ </method>
+ <method name="add_spawnable_scene">
+ <return type="void" />
+ <argument index="0" name="path" type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="clear_spawnable_scenes">
+ <return type="void" />
+ <description>
+ </description>
+ </method>
+ <method name="get_spawnable_scene" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="path" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_spawnable_scene_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="spawn">
+ <return type="Node" />
+ <argument index="0" name="data" type="Variant" default="null" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
+ </member>
+ <member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
+ </member>
+ </members>
+ <signals>
+ <signal name="despawned">
+ <argument index="0" name="scene_id" type="int" />
+ <argument index="1" name="node" type="Node" />
+ <description>
+ </description>
+ </signal>
+ <signal name="spawned">
+ <argument index="0" name="scene_id" type="int" />
+ <argument index="1" name="node" type="Node" />
+ <description>
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
new file mode 100644
index 0000000000..ebd1b50201
--- /dev/null
+++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerSynchronizer" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ </brief_description>
+ <description>
+ The [MultiplayerSynchronizer] uses [method MultiplayerAPI.object_configuration_add] to notify synchronization start passing the [Node] at [member root_path] as the [code]object[/code] and itself as the [code]configuration[/code], and uses [method MultiplayerAPI.object_configuration_remove] to notify synchronization end in a similar way.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_visibility_filter">
+ <return type="void" />
+ <argument index="0" name="filter" type="Callable" />
+ <description>
+ </description>
+ </method>
+ <method name="get_visibility_for" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="peer" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="remove_visibility_filter">
+ <return type="void" />
+ <argument index="0" name="filter" type="Callable" />
+ <description>
+ </description>
+ </method>
+ <method name="set_visibility_for">
+ <return type="void" />
+ <argument index="0" name="peer" type="int" />
+ <argument index="1" name="visible" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="update_visibility">
+ <return type="void" />
+ <argument index="0" name="for_peer" type="int" default="0" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true">
+ </member>
+ <member name="replication_config" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config">
+ </member>
+ <member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
+ </member>
+ <member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;..&quot;)">
+ </member>
+ <member name="visibility_update_mode" type="int" setter="set_visibility_update_mode" getter="get_visibility_update_mode" enum="MultiplayerSynchronizer.VisibilityUpdateMode" default="0">
+ </member>
+ </members>
+ <signals>
+ <signal name="visibility_changed">
+ <argument index="0" name="for_peer" type="int" />
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ <constant name="VISIBILITY_PROCESS_IDLE" value="0" enum="VisibilityUpdateMode">
+ </constant>
+ <constant name="VISIBILITY_PROCESS_PHYSICS" value="1" enum="VisibilityUpdateMode">
+ </constant>
+ <constant name="VISIBILITY_PROCESS_NONE" value="2" enum="VisibilityUpdateMode">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
new file mode 100644
index 0000000000..0c3ed2d784
--- /dev/null
+++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SceneMultiplayer" inherits="MultiplayerAPI" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ High-level multiplayer API implementation.
+ </brief_description>
+ <description>
+ This class is the default implementation of [MultiplayerAPI], used to provide multiplayer functionalities in Godot Engine.
+ This implementation supports RPCs via [method Node.rpc] and [method Node.rpc_id] and requires [method MultiplayerAPI.rpc] to be passed a [Node] (it will fail for other object types).
+ This implementation additionally provide [SceneTree] replication via the [MultiplayerSpawner] and [MultiplayerSynchronizer] nodes, and the [SceneReplicationConfig] resource.
+ [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice.
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="clear">
+ <return type="void" />
+ <description>
+ Clears the current SceneMultiplayer network state (you shouldn't call this unless you know what you are doing).
+ </description>
+ </method>
+ <method name="send_bytes">
+ <return type="int" enum="Error" />
+ <argument index="0" name="bytes" type="PackedByteArray" />
+ <argument index="1" name="id" type="int" default="0" />
+ <argument index="2" name="mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
+ <argument index="3" name="channel" type="int" default="0" />
+ <description>
+ Sends the given raw [code]bytes[/code] to a specific peer identified by [code]id[/code] (see [method MultiplayerPeer.set_target_peer]). Default ID is [code]0[/code], i.e. broadcast to all peers.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="allow_object_decoding" type="bool" setter="set_allow_object_decoding" getter="is_object_decoding_allowed" default="false">
+ If [code]true[/code], the MultiplayerAPI will allow encoding and decoding of object during RPCs.
+ [b]Warning:[/b] Deserialized objects can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threat such as remote code execution.
+ </member>
+ <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false">
+ If [code]true[/code], the MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] refuses new incoming connections.
+ </member>
+ <member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;&quot;)">
+ The root path to use for RPCs and replication. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
+ This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene.
+ </member>
+ </members>
+ <signals>
+ <signal name="peer_packet">
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="packet" type="PackedByteArray" />
+ <description>
+ Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] receives a [code]packet[/code] with custom data (see [method send_bytes]). ID is the peer ID of the peer that sent the packet.
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
new file mode 100644
index 0000000000..1d6dec2f92
--- /dev/null
+++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SceneReplicationConfig" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_property">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="index" type="int" default="-1" />
+ <description>
+ </description>
+ </method>
+ <method name="get_properties" qualifiers="const">
+ <return type="NodePath[]" />
+ <description>
+ </description>
+ </method>
+ <method name="has_property" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_index" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_spawn">
+ <return type="bool" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_sync">
+ <return type="bool" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_set_spawn">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="property_set_sync">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="remove_property">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor_plugin.cpp
new file mode 100644
index 0000000000..c6484264d0
--- /dev/null
+++ b/modules/multiplayer/editor/replication_editor_plugin.cpp
@@ -0,0 +1,645 @@
+/*************************************************************************/
+/* replication_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "replication_editor_plugin.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/inspector_dock.h"
+#include "modules/multiplayer/multiplayer_synchronizer.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/tree.h"
+
+void ReplicationEditor::_pick_node_filter_text_changed(const String &p_newtext) {
+ TreeItem *root_item = pick_node->get_scene_tree()->get_scene_tree()->get_root();
+
+ Vector<Node *> select_candidates;
+ Node *to_select = nullptr;
+
+ String filter = pick_node->get_filter_line_edit()->get_text();
+
+ _pick_node_select_recursive(root_item, filter, select_candidates);
+
+ if (!select_candidates.is_empty()) {
+ for (int i = 0; i < select_candidates.size(); ++i) {
+ Node *candidate = select_candidates[i];
+
+ if (((String)candidate->get_name()).to_lower().begins_with(filter.to_lower())) {
+ to_select = candidate;
+ break;
+ }
+ }
+
+ if (!to_select) {
+ to_select = select_candidates[0];
+ }
+ }
+
+ pick_node->get_scene_tree()->set_selected(to_select);
+}
+
+void ReplicationEditor::_pick_node_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates) {
+ if (!p_item) {
+ return;
+ }
+
+ NodePath np = p_item->get_metadata(0);
+ Node *node = get_node(np);
+
+ if (!p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+ p_select_candidates.push_back(node);
+ }
+
+ TreeItem *c = p_item->get_first_child();
+
+ while (c) {
+ _pick_node_select_recursive(c, p_filter, p_select_candidates);
+ c = c->get_next();
+ }
+}
+
+void ReplicationEditor::_pick_node_filter_input(const Ref<InputEvent> &p_ie) {
+ Ref<InputEventKey> k = p_ie;
+
+ if (k.is_valid()) {
+ switch (k->get_keycode()) {
+ case Key::UP:
+ case Key::DOWN:
+ case Key::PAGEUP:
+ case Key::PAGEDOWN: {
+ pick_node->get_scene_tree()->get_scene_tree()->gui_input(k);
+ pick_node->get_filter_line_edit()->accept_event();
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+void ReplicationEditor::_pick_node_selected(NodePath p_path) {
+ Node *root = current->get_node(current->get_root_path());
+ ERR_FAIL_COND(!root);
+ Node *node = get_node(p_path);
+ ERR_FAIL_COND(!node);
+ NodePath path_to = root->get_path_to(node);
+ adding_node_path = path_to;
+ prop_selector->select_property_from_instance(node);
+}
+
+void ReplicationEditor::_pick_new_property() {
+ if (current == nullptr) {
+ EditorNode::get_singleton()->show_warning(TTR("Select a replicator node in order to pick a property to add to it."));
+ return;
+ }
+ Node *root = current->get_node(current->get_root_path());
+ if (!root) {
+ EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new property to synchronize without a root."));
+ return;
+ }
+ pick_node->popup_scenetree_dialog();
+ pick_node->get_filter_line_edit()->clear();
+ pick_node->get_filter_line_edit()->grab_focus();
+}
+
+void ReplicationEditor::_add_sync_property(String p_path) {
+ config = current->get_replication_config();
+
+ if (config.is_valid() && config->has_property(p_path)) {
+ EditorNode::get_singleton()->show_warning(TTR("Property is already being synchronized."));
+ return;
+ }
+
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Add property to synchronizer"));
+
+ if (config.is_null()) {
+ config.instantiate();
+ current->set_replication_config(config);
+ undo_redo->add_do_method(current, "set_replication_config", config);
+ undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>());
+ _update_config();
+ }
+
+ undo_redo->add_do_method(config.ptr(), "add_property", p_path);
+ undo_redo->add_undo_method(config.ptr(), "remove_property", p_path);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+}
+
+void ReplicationEditor::_pick_node_property_selected(String p_name) {
+ String adding_prop_path = String(adding_node_path) + ":" + p_name;
+
+ _add_sync_property(adding_prop_path);
+}
+
+/// ReplicationEditor
+ReplicationEditor::ReplicationEditor() {
+ set_v_size_flags(SIZE_EXPAND_FILL);
+ set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+
+ delete_dialog = memnew(ConfirmationDialog);
+ delete_dialog->connect("cancelled", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(false));
+ delete_dialog->connect("confirmed", callable_mp(this, &ReplicationEditor::_dialog_closed).bind(true));
+ add_child(delete_dialog);
+
+ error_dialog = memnew(AcceptDialog);
+ error_dialog->set_ok_button_text(TTR("Close"));
+ error_dialog->set_title(TTR("Error!"));
+ add_child(error_dialog);
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(vb);
+
+ pick_node = memnew(SceneTreeDialog);
+ add_child(pick_node);
+ pick_node->register_text_enter(pick_node->get_filter_line_edit());
+ pick_node->set_title(TTR("Pick a node to synchronize:"));
+ pick_node->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_selected));
+ pick_node->get_filter_line_edit()->connect("text_changed", callable_mp(this, &ReplicationEditor::_pick_node_filter_text_changed));
+ pick_node->get_filter_line_edit()->connect("gui_input", callable_mp(this, &ReplicationEditor::_pick_node_filter_input));
+
+ prop_selector = memnew(PropertySelector);
+ add_child(prop_selector);
+ prop_selector->connect("selected", callable_mp(this, &ReplicationEditor::_pick_node_property_selected));
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ vb->add_child(hb);
+
+ add_pick_button = memnew(Button);
+ add_pick_button->connect("pressed", callable_mp(this, &ReplicationEditor::_pick_new_property));
+ add_pick_button->set_text(TTR("Add property to sync.."));
+ hb->add_child(add_pick_button);
+ VSeparator *vs = memnew(VSeparator);
+ vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0));
+ hb->add_child(vs);
+ hb->add_child(memnew(Label(TTR("Path:"))));
+ np_line_edit = memnew(LineEdit);
+ np_line_edit->set_placeholder(":property");
+ np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(np_line_edit);
+ add_from_path_button = memnew(Button);
+ add_from_path_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed));
+ add_from_path_button->set_text(TTR("Add from path"));
+ hb->add_child(add_from_path_button);
+ vs = memnew(VSeparator);
+ vs->set_custom_minimum_size(Size2(30 * EDSCALE, 0));
+ hb->add_child(vs);
+ pin = memnew(Button);
+ pin->set_flat(true);
+ pin->set_toggle_mode(true);
+ hb->add_child(pin);
+
+ tree = memnew(Tree);
+ tree->set_hide_root(true);
+ tree->set_columns(4);
+ tree->set_column_titles_visible(true);
+ tree->set_column_title(0, TTR("Properties"));
+ tree->set_column_expand(0, true);
+ tree->set_column_title(1, TTR("Spawn"));
+ tree->set_column_expand(1, false);
+ tree->set_column_custom_minimum_width(1, 100);
+ tree->set_column_title(2, TTR("Sync"));
+ tree->set_column_custom_minimum_width(2, 100);
+ tree->set_column_expand(2, false);
+ tree->set_column_expand(3, false);
+ tree->create_item();
+ tree->connect("button_clicked", callable_mp(this, &ReplicationEditor::_tree_button_pressed));
+ tree->connect("item_edited", callable_mp(this, &ReplicationEditor::_tree_item_edited));
+ tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ vb->add_child(tree);
+
+ drop_label = memnew(Label);
+ drop_label->set_text(TTR("Add properties using the buttons above or\ndrag them them from the inspector and drop them here."));
+ drop_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ drop_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
+ tree->add_child(drop_label);
+ drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+
+ tree->set_drag_forwarding(this);
+}
+
+void ReplicationEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_config"), &ReplicationEditor::_update_config);
+ ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked);
+ ClassDB::bind_method("_can_drop_data_fw", &ReplicationEditor::_can_drop_data_fw);
+ ClassDB::bind_method("_drop_data_fw", &ReplicationEditor::_drop_data_fw);
+
+ ADD_SIGNAL(MethodInfo("keying_changed"));
+}
+
+bool ReplicationEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return false;
+ }
+ String t = d["type"];
+ if (t != "obj_property") {
+ return false;
+ }
+ Object *obj = d["object"];
+ if (!obj) {
+ return false;
+ }
+ Node *node = Object::cast_to<Node>(obj);
+ if (!node) {
+ return false;
+ }
+
+ return true;
+}
+
+void ReplicationEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ if (current == nullptr) {
+ EditorNode::get_singleton()->show_warning(TTR("Select a replicator node in order to pick a property to add to it."));
+ return;
+ }
+ Node *root = current->get_node(current->get_root_path());
+ if (!root) {
+ EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new property to synchronize without a root."));
+ return;
+ }
+
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return;
+ }
+ String t = d["type"];
+ if (t != "obj_property") {
+ return;
+ }
+ Object *obj = d["object"];
+ if (!obj) {
+ return;
+ }
+ Node *node = Object::cast_to<Node>(obj);
+ if (!node) {
+ return;
+ }
+
+ String path = root->get_path_to(node);
+ path += ":" + String(d["property"]);
+
+ _add_sync_property(path);
+}
+
+void ReplicationEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ add_theme_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_theme_stylebox(SNAME("panel"), SNAME("Panel")));
+ add_pick_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ pin->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ update_keying();
+ } break;
+ }
+}
+
+void ReplicationEditor::_add_pressed() {
+ if (!current) {
+ error_dialog->set_text(TTR("Please select a MultiplayerSynchronizer first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ if (current->get_root_path().is_empty()) {
+ error_dialog->set_text(TTR("The MultiplayerSynchronizer needs a root path."));
+ error_dialog->popup_centered();
+ return;
+ }
+ String np_text = np_line_edit->get_text();
+ if (np_text.find(":") == -1) {
+ np_text = ":" + np_text;
+ }
+ NodePath prop = NodePath(np_text);
+ if (prop.is_empty()) {
+ return;
+ }
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Add property"));
+ config = current->get_replication_config();
+ if (config.is_null()) {
+ config.instantiate();
+ current->set_replication_config(config);
+ undo_redo->add_do_method(current, "set_replication_config", config);
+ undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>());
+ _update_config();
+ }
+ undo_redo->add_do_method(config.ptr(), "add_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+}
+
+void ReplicationEditor::_tree_item_edited() {
+ TreeItem *ti = tree->get_edited();
+ if (!ti || config.is_null()) {
+ return;
+ }
+ int column = tree->get_edited_column();
+ ERR_FAIL_COND(column < 1 || column > 2);
+ const NodePath prop = ti->get_metadata(0);
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ bool value = ti->is_checked(column);
+ String method;
+ if (column == 1) {
+ undo_redo->create_action(TTR("Set spawn property"));
+ method = "property_set_spawn";
+ } else {
+ undo_redo->create_action(TTR("Set sync property"));
+ method = "property_set_sync";
+ }
+ undo_redo->add_do_method(config.ptr(), method, prop, value);
+ undo_redo->add_undo_method(config.ptr(), method, prop, !value);
+ undo_redo->add_do_method(this, "_update_checked", prop, column, value);
+ undo_redo->add_undo_method(this, "_update_checked", prop, column, !value);
+ undo_redo->commit_action();
+}
+
+void ReplicationEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
+ if (p_button != MouseButton::LEFT) {
+ return;
+ }
+
+ TreeItem *ti = Object::cast_to<TreeItem>(p_item);
+ if (!ti) {
+ return;
+ }
+ deleting = ti->get_metadata(0);
+ delete_dialog->set_text(TTR("Delete Property?") + "\n\"" + ti->get_text(0) + "\"");
+ delete_dialog->popup_centered();
+}
+
+void ReplicationEditor::_dialog_closed(bool p_confirmed) {
+ if (deleting.is_empty() || config.is_null()) {
+ return;
+ }
+ if (p_confirmed) {
+ const NodePath prop = deleting;
+ int idx = config->property_get_index(prop);
+ bool spawn = config->property_get_spawn(prop);
+ bool sync = config->property_get_sync(prop);
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Remove Property"));
+ undo_redo->add_do_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx);
+ undo_redo->add_undo_method(config.ptr(), "property_set_spawn", prop, spawn);
+ undo_redo->add_undo_method(config.ptr(), "property_set_sync", prop, sync);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+ }
+ deleting = NodePath();
+}
+
+void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bool p_checked) {
+ if (!tree->get_root()) {
+ return;
+ }
+ TreeItem *ti = tree->get_root()->get_first_child();
+ while (ti) {
+ if (ti->get_metadata(0).operator NodePath() == p_prop) {
+ ti->set_checked(p_column, p_checked);
+ return;
+ }
+ ti = ti->get_next();
+ }
+}
+
+void ReplicationEditor::update_keying() {
+ /// TODO make keying usable.
+#if 0
+ bool keying_enabled = false;
+ EditorSelectionHistory *editor_history = EditorNode::get_singleton()->get_editor_selection_history();
+ if (is_visible_in_tree() && config.is_valid() && editor_history->get_path_size() > 0) {
+ Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
+ keying_enabled = Object::cast_to<Node>(obj) != nullptr;
+ }
+
+ if (keying_enabled == keying) {
+ return;
+ }
+
+ keying = keying_enabled;
+ emit_signal(SNAME("keying_changed"));
+#endif
+}
+
+void ReplicationEditor::_update_config() {
+ deleting = NodePath();
+ tree->clear();
+ tree->create_item();
+ drop_label->set_visible(true);
+ if (!config.is_valid()) {
+ update_keying();
+ return;
+ }
+ TypedArray<NodePath> props = config->get_properties();
+ if (props.size()) {
+ drop_label->set_visible(false);
+ }
+ for (int i = 0; i < props.size(); i++) {
+ const NodePath path = props[i];
+ _add_property(path, config->property_get_spawn(path), config->property_get_sync(path));
+ }
+ update_keying();
+}
+
+void ReplicationEditor::edit(MultiplayerSynchronizer *p_sync) {
+ if (current == p_sync) {
+ return;
+ }
+ current = p_sync;
+ if (current) {
+ config = current->get_replication_config();
+ } else {
+ config.unref();
+ }
+ _update_config();
+}
+
+Ref<Texture2D> ReplicationEditor::_get_class_icon(const Node *p_node) {
+ if (!p_node || !has_theme_icon(p_node->get_class(), "EditorIcons")) {
+ return get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"));
+ }
+ return get_theme_icon(p_node->get_class(), "EditorIcons");
+}
+
+void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, bool p_sync) {
+ String prop = String(p_property);
+ TreeItem *item = tree->create_item();
+ item->set_selectable(0, false);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_selectable(3, false);
+ item->set_text(0, prop);
+ item->set_metadata(0, prop);
+ Node *root_node = current && !current->get_root_path().is_empty() ? current->get_node(current->get_root_path()) : nullptr;
+ Ref<Texture2D> icon = _get_class_icon(root_node);
+ if (root_node) {
+ String path = prop.substr(0, prop.find(":"));
+ String subpath = prop.substr(path.size());
+ Node *node = root_node->get_node_or_null(path);
+ if (!node) {
+ node = root_node;
+ }
+ item->set_text(0, String(node->get_name()) + ":" + subpath);
+ icon = _get_class_icon(node);
+ }
+ item->set_icon(0, icon);
+ item->add_button(3, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ item->set_text_alignment(1, HORIZONTAL_ALIGNMENT_CENTER);
+ item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK);
+ item->set_checked(1, p_spawn);
+ item->set_editable(1, true);
+ item->set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER);
+ item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK);
+ item->set_checked(2, p_sync);
+ item->set_editable(2, true);
+}
+
+void ReplicationEditor::property_keyed(const String &p_property) {
+ ERR_FAIL_COND(!current || config.is_null());
+ Node *root = current->get_node(current->get_root_path());
+ ERR_FAIL_COND(!root);
+ EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
+ ERR_FAIL_COND(history->get_path_size() == 0);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(history->get_path_object(0)));
+ ERR_FAIL_COND(!node);
+ if (node->is_class("MultiplayerSynchronizer")) {
+ error_dialog->set_text(TTR("Properties of 'MultiplayerSynchronizer' cannot be configured for replication."));
+ error_dialog->popup_centered();
+ return;
+ }
+ if (history->get_path_size() > 1 || p_property.get_slice_count(":") > 1) {
+ error_dialog->set_text(TTR("Subresources cannot yet be configured for replication."));
+ error_dialog->popup_centered();
+ return;
+ }
+
+ String path = root->get_path_to(node);
+ for (int i = 1; i < history->get_path_size(); i++) {
+ String prop = history->get_path_property(i);
+ ERR_FAIL_COND(prop == "");
+ path += ":" + prop;
+ }
+ path += ":" + p_property;
+
+ NodePath prop = path;
+ UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ undo_redo->create_action(TTR("Add property"));
+ undo_redo->add_do_method(config.ptr(), "add_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+}
+
+/// ReplicationEditorPlugin
+ReplicationEditorPlugin::ReplicationEditorPlugin() {
+ repl_editor = memnew(ReplicationEditor);
+ button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor);
+ button->hide();
+ repl_editor->get_pin()->connect("pressed", callable_mp(this, &ReplicationEditorPlugin::_pinned));
+}
+
+ReplicationEditorPlugin::~ReplicationEditorPlugin() {
+}
+
+void ReplicationEditorPlugin::_keying_changed() {
+ // TODO make lock usable.
+ //InspectorDock::get_inspector_singleton()->set_keying(repl_editor->has_keying(), this);
+}
+
+void ReplicationEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
+ if (!repl_editor->has_keying()) {
+ return;
+ }
+ repl_editor->property_keyed(p_keyed);
+}
+
+void ReplicationEditorPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ //Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request));
+ InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &ReplicationEditorPlugin::_property_keyed));
+ repl_editor->connect("keying_changed", callable_mp(this, &ReplicationEditorPlugin::_keying_changed));
+ // TODO make lock usable.
+ //InspectorDock::get_inspector_singleton()->connect("object_inspected", callable_mp(repl_editor, &ReplicationEditor::update_keying));
+ get_tree()->connect("node_removed", callable_mp(this, &ReplicationEditorPlugin::_node_removed));
+ } break;
+ }
+}
+
+void ReplicationEditorPlugin::_node_removed(Node *p_node) {
+ if (p_node && p_node == repl_editor->get_current()) {
+ repl_editor->edit(nullptr);
+ if (repl_editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+ button->hide();
+ repl_editor->get_pin()->set_pressed(false);
+ }
+}
+
+void ReplicationEditorPlugin::_pinned() {
+ if (!repl_editor->get_pin()->is_pressed()) {
+ if (repl_editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+ button->hide();
+ }
+}
+
+void ReplicationEditorPlugin::edit(Object *p_object) {
+ repl_editor->edit(Object::cast_to<MultiplayerSynchronizer>(p_object));
+}
+
+bool ReplicationEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("MultiplayerSynchronizer");
+}
+
+void ReplicationEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ //editor->hide_animation_player_editors();
+ //editor->animation_panel_make_visible(true);
+ button->show();
+ EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor);
+ } else if (!repl_editor->get_pin()->is_pressed()) {
+ if (repl_editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+ button->hide();
+ }
+}
diff --git a/modules/multiplayer/editor/replication_editor_plugin.h b/modules/multiplayer/editor/replication_editor_plugin.h
new file mode 100644
index 0000000000..57fa4c82fa
--- /dev/null
+++ b/modules/multiplayer/editor/replication_editor_plugin.h
@@ -0,0 +1,148 @@
+/*************************************************************************/
+/* replication_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REPLICATION_EDITOR_PLUGIN_H
+#define REPLICATION_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+
+#include "editor/editor_spin_slider.h"
+#include "editor/property_editor.h"
+#include "editor/property_selector.h"
+
+#include "../scene_replication_config.h"
+
+class ConfirmationDialog;
+class MultiplayerSynchronizer;
+class Tree;
+
+class ReplicationEditor : public VBoxContainer {
+ GDCLASS(ReplicationEditor, VBoxContainer);
+
+private:
+ MultiplayerSynchronizer *current = nullptr;
+
+ AcceptDialog *error_dialog = nullptr;
+ ConfirmationDialog *delete_dialog = nullptr;
+ Button *add_pick_button = nullptr;
+ Button *add_from_path_button = nullptr;
+ LineEdit *np_line_edit = nullptr;
+
+ Label *drop_label = nullptr;
+
+ Ref<SceneReplicationConfig> config;
+ NodePath deleting;
+ Tree *tree = nullptr;
+ bool keying = false;
+
+ PropertySelector *prop_selector = nullptr;
+ SceneTreeDialog *pick_node = nullptr;
+ NodePath adding_node_path;
+
+ Button *pin = nullptr;
+
+ Ref<Texture2D> _get_class_icon(const Node *p_node);
+
+ void _add_pressed();
+ void _tree_item_edited();
+ void _tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
+ void _update_checked(const NodePath &p_prop, int p_column, bool p_checked);
+ void _update_config();
+ void _dialog_closed(bool p_confirmed);
+ void _add_property(const NodePath &p_property, bool p_spawn = true, bool p_sync = true);
+
+ void _pick_node_filter_text_changed(const String &p_newtext);
+ void _pick_node_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates);
+ void _pick_node_filter_input(const Ref<InputEvent> &p_ie);
+ void _pick_node_selected(NodePath p_path);
+
+ void _pick_new_property();
+ void _pick_node_property_selected(String p_name);
+
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
+ void _add_sync_property(String p_path);
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+public:
+ void update_keying();
+ void edit(MultiplayerSynchronizer *p_object);
+ bool has_keying() const { return keying; }
+ MultiplayerSynchronizer *get_current() const { return current; }
+ void property_keyed(const String &p_property);
+
+ Button *get_pin() { return pin; }
+ ReplicationEditor();
+ ~ReplicationEditor() {}
+};
+
+class ReplicationEditorPlugin : public EditorPlugin {
+ GDCLASS(ReplicationEditorPlugin, EditorPlugin);
+
+private:
+ Button *button = nullptr;
+ ReplicationEditor *repl_editor = nullptr;
+
+ void _node_removed(Node *p_node);
+ void _keying_changed();
+ void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
+
+ void _pinned();
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ ReplicationEditorPlugin();
+ ~ReplicationEditorPlugin();
+};
+#else
+class ReplicationEditorPlugin : public EditorPlugin {
+ GDCLASS(ReplicationEditorPlugin, EditorPlugin);
+
+public:
+ virtual void edit(Object *p_object) override {}
+ virtual bool handles(Object *p_object) const override { return false; }
+ virtual void make_visible(bool p_visible) override {}
+
+ ReplicationEditorPlugin() {}
+ ~ReplicationEditorPlugin() {}
+};
+
+#endif // REPLICATION_EDITOR_PLUGIN_H
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
new file mode 100644
index 0000000000..f5edffbb0c
--- /dev/null
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -0,0 +1,302 @@
+/*************************************************************************/
+/* multiplayer_spawner.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_spawner.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/multiplayer_api.h"
+#include "scene/main/window.h"
+#include "scene/scene_string_names.h"
+
+#ifdef TOOLS_ENABLED
+/* This is editor only */
+bool MultiplayerSpawner::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "_spawnable_scene_count") {
+ spawnable_scenes.resize(p_value);
+ notify_property_list_changed();
+ return true;
+ } else {
+ String ns = p_name;
+ if (ns.begins_with("scenes/")) {
+ uint32_t index = ns.get_slicec('/', 1).to_int();
+ ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false);
+ spawnable_scenes[index].path = p_value;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "_spawnable_scene_count") {
+ r_ret = spawnable_scenes.size();
+ return true;
+ } else {
+ String ns = p_name;
+ if (ns.begins_with("scenes/")) {
+ uint32_t index = ns.get_slicec('/', 1).to_int();
+ ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false);
+ r_ret = spawnable_scenes[index].path;
+ return true;
+ }
+ }
+ return false;
+}
+
+void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/"));
+ List<String> exts;
+ ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts);
+ String ext_hint;
+ for (const String &E : exts) {
+ if (!ext_hint.is_empty()) {
+ ext_hint += ",";
+ }
+ ext_hint += "*." + E;
+ }
+ for (uint32_t i = 0; i < spawnable_scenes.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, "scenes/" + itos(i), PROPERTY_HINT_FILE, ext_hint, PROPERTY_USAGE_EDITOR));
+ }
+}
+#endif
+void MultiplayerSpawner::add_spawnable_scene(const String &p_path) {
+ SpawnableScene sc;
+ sc.path = p_path;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ ERR_FAIL_COND(!FileAccess::exists(p_path));
+ } else {
+ sc.cache = ResourceLoader::load(p_path);
+ ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path);
+ }
+ spawnable_scenes.push_back(sc);
+}
+int MultiplayerSpawner::get_spawnable_scene_count() const {
+ return spawnable_scenes.size();
+}
+String MultiplayerSpawner::get_spawnable_scene(int p_idx) const {
+ return spawnable_scenes[p_idx].path;
+}
+void MultiplayerSpawner::clear_spawnable_scenes() {
+ spawnable_scenes.clear();
+}
+
+Vector<String> MultiplayerSpawner::_get_spawnable_scenes() const {
+ Vector<String> ss;
+ ss.resize(spawnable_scenes.size());
+ for (int i = 0; i < ss.size(); i++) {
+ ss.write[i] = spawnable_scenes[i].path;
+ }
+ return ss;
+}
+
+void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) {
+ clear_spawnable_scenes();
+ for (int i = 0; i < p_scenes.size(); i++) {
+ add_spawnable_scene(p_scenes[i]);
+ }
+}
+
+void MultiplayerSpawner::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_spawnable_scene", "path"), &MultiplayerSpawner::add_spawnable_scene);
+ ClassDB::bind_method(D_METHOD("get_spawnable_scene_count"), &MultiplayerSpawner::get_spawnable_scene_count);
+ ClassDB::bind_method(D_METHOD("get_spawnable_scene", "path"), &MultiplayerSpawner::get_spawnable_scene);
+ ClassDB::bind_method(D_METHOD("clear_spawnable_scenes"), &MultiplayerSpawner::clear_spawnable_scenes);
+
+ ClassDB::bind_method(D_METHOD("_get_spawnable_scenes"), &MultiplayerSpawner::_get_spawnable_scenes);
+ ClassDB::bind_method(D_METHOD("_set_spawnable_scenes", "scenes"), &MultiplayerSpawner::_set_spawnable_scenes);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_spawnable_scenes", PROPERTY_HINT_NONE, "", (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL)), "_set_spawnable_scenes", "_get_spawnable_scenes");
+
+ ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant()));
+
+ ClassDB::bind_method(D_METHOD("get_spawn_path"), &MultiplayerSpawner::get_spawn_path);
+ ClassDB::bind_method(D_METHOD("set_spawn_path", "path"), &MultiplayerSpawner::set_spawn_path);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "spawn_path", PROPERTY_HINT_NONE, ""), "set_spawn_path", "get_spawn_path");
+
+ ClassDB::bind_method(D_METHOD("get_spawn_limit"), &MultiplayerSpawner::get_spawn_limit);
+ ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
+
+ GDVIRTUAL_BIND(_spawn_custom, "data");
+
+ ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+}
+
+void MultiplayerSpawner::_update_spawn_node() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ if (spawn_node.is_valid()) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node));
+ if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
+ node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
+ }
+ Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
+ if (node) {
+ spawn_node = node->get_instance_id();
+ if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) {
+ node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
+ } else {
+ spawn_node = ObjectID();
+ }
+}
+
+void MultiplayerSpawner::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_POST_ENTER_TREE: {
+ _update_spawn_node();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _update_spawn_node();
+
+ for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key));
+ ERR_CONTINUE(!node);
+ node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
+ // This is unlikely, but might still crash the engine.
+ if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
+ node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
+ }
+ get_multiplayer()->object_configuration_remove(node, this);
+ }
+ tracked_nodes.clear();
+ } break;
+ }
+}
+
+void MultiplayerSpawner::_node_added(Node *p_node) {
+ if (!get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority()) {
+ return;
+ }
+ if (tracked_nodes.has(p_node->get_instance_id())) {
+ return;
+ }
+ const Node *parent = get_spawn_node();
+ if (!parent || p_node->get_parent() != parent) {
+ return;
+ }
+ int id = find_spawnable_scene_index_from_path(p_node->get_scene_file_path());
+ if (id == INVALID_ID) {
+ return;
+ }
+ const String name = p_node->get_name();
+ ERR_FAIL_COND_MSG(name.validate_node_name() != name, vformat("Unable to auto-spawn node with reserved name: %s. Make sure to add your replicated scenes via 'add_child(node, true)' to produce valid names.", name));
+ _track(p_node, Variant(), id);
+}
+
+NodePath MultiplayerSpawner::get_spawn_path() const {
+ return spawn_path;
+}
+
+void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) {
+ spawn_path = p_path;
+ _update_spawn_node();
+}
+
+void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) {
+ ObjectID oid = p_node->get_instance_id();
+ if (!tracked_nodes.has(oid)) {
+ tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
+ p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONESHOT);
+ p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready).bind(p_node->get_instance_id()), CONNECT_ONESHOT);
+ }
+}
+
+void MultiplayerSpawner::_node_ready(ObjectID p_id) {
+ get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this);
+}
+
+void MultiplayerSpawner::_node_exit(ObjectID p_id) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
+ ERR_FAIL_COND(!node);
+ if (tracked_nodes.has(p_id)) {
+ tracked_nodes.erase(p_id);
+ get_multiplayer()->object_configuration_remove(node, this);
+ }
+}
+
+int MultiplayerSpawner::find_spawnable_scene_index_from_path(const String &p_scene) const {
+ for (uint32_t i = 0; i < spawnable_scenes.size(); i++) {
+ if (spawnable_scenes[i].path == p_scene) {
+ return i;
+ }
+ }
+ return INVALID_ID;
+}
+
+int MultiplayerSpawner::find_spawnable_scene_index_from_object(const ObjectID &p_id) const {
+ const SpawnInfo *info = tracked_nodes.getptr(p_id);
+ return info ? info->id : INVALID_ID;
+}
+
+const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const {
+ const SpawnInfo *info = tracked_nodes.getptr(p_id);
+ return info ? info->args : Variant();
+}
+
+Node *MultiplayerSpawner::instantiate_scene(int p_id) {
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr);
+ Ref<PackedScene> scene = spawnable_scenes[p_id].cache;
+ ERR_FAIL_COND_V(scene.is_null(), nullptr);
+ return scene->instantiate();
+}
+
+Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ Object *obj = nullptr;
+ Node *node = nullptr;
+ if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) {
+ node = Object::cast_to<Node>(obj);
+ }
+ return node;
+}
+
+Node *MultiplayerSpawner::spawn(const Variant &p_data) {
+ ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script.");
+
+ Node *parent = get_spawn_node();
+ ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
+
+ Node *node = instantiate_custom(p_data);
+ ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node.");
+
+ _track(node, p_data);
+ parent->add_child(node, true);
+ return node;
+}
diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
new file mode 100644
index 0000000000..80bb878a74
--- /dev/null
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* multiplayer_spawner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_SPAWNER_H
+#define MULTIPLAYER_SPAWNER_H
+
+#include "scene/main/node.h"
+
+#include "core/templates/local_vector.h"
+#include "core/variant/typed_array.h"
+#include "scene/resources/packed_scene.h"
+
+#include "scene_replication_config.h"
+
+class MultiplayerSpawner : public Node {
+ GDCLASS(MultiplayerSpawner, Node);
+
+public:
+ enum {
+ INVALID_ID = 0xFF,
+ };
+
+private:
+ struct SpawnableScene {
+ String path;
+ Ref<PackedScene> cache;
+ };
+
+ LocalVector<SpawnableScene> spawnable_scenes;
+
+ HashSet<ResourceUID::ID> spawnable_ids;
+ NodePath spawn_path;
+
+ struct SpawnInfo {
+ Variant args;
+ int id = INVALID_ID;
+ SpawnInfo(Variant p_args, int p_id) {
+ id = p_id;
+ args = p_args;
+ }
+ SpawnInfo() {}
+ };
+
+ ObjectID spawn_node;
+ HashMap<ObjectID, SpawnInfo> tracked_nodes;
+ uint32_t spawn_limit = 0;
+
+ void _update_spawn_node();
+ void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
+ void _node_added(Node *p_node);
+ void _node_exit(ObjectID p_id);
+ void _node_ready(ObjectID p_id);
+
+ Vector<String> _get_spawnable_scenes() const;
+ void _set_spawnable_scenes(const Vector<String> &p_scenes);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+#ifdef TOOLS_ENABLED
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+#endif
+public:
+ Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; }
+
+ void add_spawnable_scene(const String &p_path);
+ int get_spawnable_scene_count() const;
+ String get_spawnable_scene(int p_idx) const;
+ void clear_spawnable_scenes();
+
+ NodePath get_spawn_path() const;
+ void set_spawn_path(const NodePath &p_path);
+ uint32_t get_spawn_limit() const { return spawn_limit; }
+ void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
+
+ const Variant get_spawn_argument(const ObjectID &p_id) const;
+ int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
+ int find_spawnable_scene_index_from_path(const String &p_path) const;
+ Node *spawn(const Variant &p_data = Variant());
+ Node *instantiate_custom(const Variant &p_data);
+ Node *instantiate_scene(int p_idx);
+
+ GDVIRTUAL1R(Object *, _spawn_custom, const Variant &);
+
+ MultiplayerSpawner() {}
+};
+
+#endif // MULTIPLAYER_SPAWNER_H
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
new file mode 100644
index 0000000000..eee1495c14
--- /dev/null
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -0,0 +1,304 @@
+/*************************************************************************/
+/* multiplayer_synchronizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_synchronizer.h"
+
+#include "core/config/engine.h"
+#include "scene/main/multiplayer_api.h"
+
+Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) {
+ if (p_path.get_name_count() == 0) {
+ return p_obj;
+ }
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path));
+ return node->get_node(p_path);
+}
+
+void MultiplayerSynchronizer::_stop() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->object_configuration_remove(node, this);
+ }
+}
+
+void MultiplayerSynchronizer::_start() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->object_configuration_add(node, this);
+ _update_process();
+ }
+}
+
+void MultiplayerSynchronizer::_update_process() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (!node) {
+ return;
+ }
+ set_process_internal(false);
+ set_physics_process_internal(false);
+ if (!visibility_filters.size()) {
+ return;
+ }
+ switch (visibility_update_mode) {
+ case VISIBILITY_PROCESS_IDLE:
+ set_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_PHYSICS:
+ set_physics_process_internal(true);
+ break;
+ case VISIBILITY_PROCESS_NONE:
+ break;
+ }
+}
+
+Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
+ ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ r_variant.resize(p_properties.size());
+ r_variant_ptrs.resize(r_variant.size());
+ int i = 0;
+ for (const NodePath &prop : p_properties) {
+ bool valid = false;
+ const Object *obj = _get_prop_target(p_obj, prop);
+ ERR_FAIL_COND_V(!obj, FAILED);
+ r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid);
+ r_variant_ptrs.write[i] = &r_variant[i];
+ ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
+ i++;
+ }
+ return OK;
+}
+
+Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) {
+ ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ int i = 0;
+ for (const NodePath &prop : p_properties) {
+ Object *obj = _get_prop_target(p_obj, prop);
+ ERR_FAIL_COND_V(!obj, FAILED);
+ obj->set(prop.get_concatenated_subnames(), p_state[i]);
+ i += 1;
+ }
+ return OK;
+}
+
+bool MultiplayerSynchronizer::is_visibility_public() const {
+ return peer_visibility.has(0);
+}
+
+void MultiplayerSynchronizer::set_visibility_public(bool p_visible) {
+ set_visibility_for(0, p_visible);
+}
+
+bool MultiplayerSynchronizer::is_visible_to(int p_peer) {
+ if (visibility_filters.size()) {
+ Variant arg = p_peer;
+ const Variant *argv[1] = { &arg };
+ for (Callable filter : visibility_filters) {
+ Variant ret;
+ Callable::CallError err;
+ filter.callp(argv, 1, ret, err);
+ ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false);
+ if (!ret.operator bool()) {
+ return false;
+ }
+ }
+ }
+ return peer_visibility.has(0) || peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) {
+ visibility_filters.insert(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) {
+ visibility_filters.erase(p_callback);
+ _update_process();
+}
+
+void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) {
+ if (peer_visibility.has(p_peer) == p_visible) {
+ return;
+ }
+ if (p_visible) {
+ peer_visibility.insert(p_peer);
+ } else {
+ peer_visibility.erase(p_peer);
+ }
+ update_visibility(p_peer);
+}
+
+bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const {
+ return peer_visibility.has(p_peer);
+}
+
+void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) {
+ visibility_update_mode = p_mode;
+ _update_process();
+}
+
+MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const {
+ return visibility_update_mode;
+}
+
+void MultiplayerSynchronizer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
+ ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
+
+ ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval);
+ ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval);
+
+ ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
+ ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
+
+ ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode);
+ ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public);
+ ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public);
+
+ ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter);
+ ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter);
+ ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for);
+ ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public");
+
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS);
+ BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE);
+
+ ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer")));
+}
+
+void MultiplayerSynchronizer::_notification(int p_what) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ if (root_path.is_empty()) {
+ return;
+ }
+
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _start();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _stop();
+ } break;
+
+ case NOTIFICATION_INTERNAL_PROCESS:
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ update_visibility(0);
+ } break;
+ }
+}
+
+void MultiplayerSynchronizer::set_replication_interval(double p_interval) {
+ ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
+ interval_msec = uint64_t(p_interval * 1000);
+}
+
+double MultiplayerSynchronizer::get_replication_interval() const {
+ return double(interval_msec) / 1000.0;
+}
+
+uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const {
+ return interval_msec;
+}
+
+void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
+ replication_config = p_config;
+}
+
+Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
+ return replication_config;
+}
+
+void MultiplayerSynchronizer::update_visibility(int p_for_peer) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) {
+ emit_signal(SNAME("visibility_changed"), p_for_peer);
+ }
+}
+
+void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
+ _stop();
+ root_path = p_path;
+ _start();
+}
+
+NodePath MultiplayerSynchronizer::get_root_path() const {
+ return root_path;
+}
+
+void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (!node) {
+ Node::set_multiplayer_authority(p_peer_id, p_recursive);
+ return;
+ }
+ get_multiplayer()->object_configuration_remove(node, this);
+ Node::set_multiplayer_authority(p_peer_id, p_recursive);
+ get_multiplayer()->object_configuration_add(node, this);
+}
+
+MultiplayerSynchronizer::MultiplayerSynchronizer() {
+ // Publicly visible by default.
+ peer_visibility.insert(0);
+}
diff --git a/modules/multiplayer/multiplayer_synchronizer.h b/modules/multiplayer/multiplayer_synchronizer.h
new file mode 100644
index 0000000000..e84d41db86
--- /dev/null
+++ b/modules/multiplayer/multiplayer_synchronizer.h
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* multiplayer_synchronizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_SYNCHRONIZER_H
+#define MULTIPLAYER_SYNCHRONIZER_H
+
+#include "scene/main/node.h"
+
+#include "scene_replication_config.h"
+
+class MultiplayerSynchronizer : public Node {
+ GDCLASS(MultiplayerSynchronizer, Node);
+
+public:
+ enum VisibilityUpdateMode {
+ VISIBILITY_PROCESS_IDLE,
+ VISIBILITY_PROCESS_PHYSICS,
+ VISIBILITY_PROCESS_NONE,
+ };
+
+private:
+ Ref<SceneReplicationConfig> replication_config;
+ NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer.
+ uint64_t interval_msec = 0;
+ VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE;
+ HashSet<Callable> visibility_filters;
+ HashSet<int> peer_visibility;
+
+ static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
+ void _start();
+ void _stop();
+ void _update_process();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs);
+ static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state);
+
+ void set_replication_interval(double p_interval);
+ double get_replication_interval() const;
+ uint64_t get_replication_interval_msec() const;
+
+ void set_replication_config(Ref<SceneReplicationConfig> p_config);
+ Ref<SceneReplicationConfig> get_replication_config();
+
+ void set_root_path(const NodePath &p_path);
+ NodePath get_root_path() const;
+ virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
+
+ bool is_visibility_public() const;
+ void set_visibility_public(bool p_public);
+ bool is_visible_to(int p_peer);
+ void set_visibility_for(int p_peer, bool p_visible);
+ bool get_visibility_for(int p_peer) const;
+ void update_visibility(int p_for_peer);
+ void set_visibility_update_mode(VisibilityUpdateMode p_mode);
+ void add_visibility_filter(Callable p_callback);
+ void remove_visibility_filter(Callable p_callback);
+ VisibilityUpdateMode get_visibility_update_mode() const;
+
+ MultiplayerSynchronizer();
+};
+
+VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode);
+
+#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/modules/multiplayer/register_types.cpp b/modules/multiplayer/register_types.cpp
new file mode 100644
index 0000000000..a2c524da80
--- /dev/null
+++ b/modules/multiplayer/register_types.cpp
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
+#include "scene_multiplayer.h"
+#include "scene_replication_interface.h"
+#include "scene_rpc_interface.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_plugin.h"
+#include "editor/replication_editor_plugin.h"
+#endif
+
+void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(SceneReplicationConfig);
+ GDREGISTER_CLASS(MultiplayerSpawner);
+ GDREGISTER_CLASS(MultiplayerSynchronizer);
+ GDREGISTER_CLASS(SceneMultiplayer);
+ MultiplayerAPI::set_default_interface("SceneMultiplayer");
+ }
+#ifdef TOOLS_ENABLED
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<ReplicationEditorPlugin>();
+ }
+#endif
+}
+
+void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) {
+}
diff --git a/modules/multiplayer/register_types.h b/modules/multiplayer/register_types.h
new file mode 100644
index 0000000000..aca6cf46ed
--- /dev/null
+++ b/modules/multiplayer/register_types.h
@@ -0,0 +1,39 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_REGISTER_TYPES_H
+#define MULTIPLAYER_REGISTER_TYPES_H
+
+#include "modules/register_module_types.h"
+
+void initialize_multiplayer_module(ModuleInitializationLevel p_level);
+void uninitialize_multiplayer_module(ModuleInitializationLevel p_level);
+
+#endif // MULTIPLAYER_REGISTER_TYPES_H
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
new file mode 100644
index 0000000000..b3b642f815
--- /dev/null
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -0,0 +1,265 @@
+/*************************************************************************/
+/* scene_cache_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_cache_interface.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+#include "scene/main/window.h"
+
+#include "scene_multiplayer.h"
+
+void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
+ if (p_connected) {
+ path_get_cache.insert(p_id, PathGetCache());
+ } else {
+ // Cleanup get cache.
+ path_get_cache.erase(p_id);
+ // Cleanup sent cache.
+ // Some refactoring is needed to make this faster and do paths GC.
+ for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) {
+ PathSentCache *psc = path_send_cache.getptr(E.key);
+ psc->confirmed_peers.erase(p_id);
+ }
+ }
+}
+
+void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND(!root_node);
+ ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
+ int ofs = 1;
+
+ String methods_md5;
+ methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
+ ofs += 33;
+
+ int id = decode_uint32(&p_packet[ofs]);
+ ofs += 4;
+
+ String paths;
+ paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from] = PathGetCache();
+ }
+
+ Node *node = root_node->get_node(path);
+ ERR_FAIL_COND(node == nullptr);
+ const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path = path;
+
+ path_get_cache[p_from].nodes[id] = ni;
+
+ // Encode path to send ack.
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(), nullptr);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + 1 + len);
+ packet.write[0] = SceneMultiplayer::NETWORK_COMMAND_CONFIRM_PATH;
+ packet.write[1] = valid_rpc_checksum;
+ encode_cstring(pname.get_data(), &packet.write[2]);
+
+ Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND(multiplayer_peer.is_null());
+
+#ifdef DEBUG_ENABLED
+ multiplayer->profile_bandwidth("out", packet.size());
+#endif
+
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ multiplayer_peer->set_target_peer(p_from);
+ multiplayer_peer->put_packet(packet.ptr(), packet.size());
+}
+
+void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
+
+ const bool valid_rpc_checksum = p_packet[1];
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
+
+ NodePath path = paths;
+
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
+
+ HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
+ E->value = true;
+}
+
+Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) {
+ // Encode function name.
+ const CharString path = String(p_path).utf8();
+ const int path_len = encode_cstring(path.get_data(), nullptr);
+
+ // Extract MD5 from rpc methods list.
+ const String methods_md5 = multiplayer->get_rpc_md5(p_node);
+ const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
+
+ Vector<uint8_t> packet;
+ packet.resize(1 + 4 + path_len + methods_md5_len);
+ int ofs = 0;
+
+ packet.write[ofs] = SceneMultiplayer::NETWORK_COMMAND_SIMPLIFY_PATH;
+ ofs += 1;
+
+ ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
+
+ ofs += encode_uint32(psc->id, &packet.write[ofs]);
+
+ ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
+
+ Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG);
+
+#ifdef DEBUG_ENABLED
+ multiplayer->profile_bandwidth("out", packet.size() * p_peers.size());
+#endif
+
+ Error err = OK;
+ for (int peer_id : p_peers) {
+ multiplayer_peer->set_target_peer(peer_id);
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ err = multiplayer_peer->put_packet(packet.ptr(), packet.size());
+ ERR_FAIL_COND_V(err != OK, err);
+ // Insert into confirmed, but as false since it was not confirmed.
+ psc->confirmed_peers.insert(peer_id, false);
+ }
+ return err;
+}
+
+bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
+ const PathSentCache *psc = path_send_cache.getptr(p_path);
+ ERR_FAIL_COND_V(!psc, false);
+ HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer);
+ ERR_FAIL_COND_V(!F, false); // Should never happen.
+ return F->value;
+}
+
+int SceneCacheInterface::make_object_cache(Object *p_obj) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, -1);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
+ // See if the path is cached.
+ PathSentCache *psc = path_send_cache.getptr(for_path);
+ if (!psc) {
+ // Path is not cached, create.
+ path_send_cache[for_path] = PathSentCache();
+ psc = path_send_cache.getptr(for_path);
+ psc->id = last_send_cache_id++;
+ }
+ return psc->id;
+}
+
+bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, false);
+
+ r_id = make_object_cache(p_obj);
+ ERR_FAIL_COND_V(r_id < 0, false);
+ NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path());
+ PathSentCache *psc = path_send_cache.getptr(for_path);
+
+ bool has_all_peers = true;
+ List<int> peers_to_add; // If one is missing, take note to add it.
+
+ if (p_peer_id > 0) {
+ // Fast single peer check.
+ HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id);
+ if (!F) {
+ peers_to_add.push_back(p_peer_id); // Need to also be notified.
+ has_all_peers = false;
+ } else if (!F->value) {
+ has_all_peers = false;
+ }
+ } else {
+ // Long and painful.
+ for (const int &E : multiplayer->get_connected_peers()) {
+ if (p_peer_id < 0 && E == -p_peer_id) {
+ continue; // Continue, excluded.
+ }
+
+ HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E);
+ if (!F) {
+ peers_to_add.push_back(E); // Need to also be notified.
+ has_all_peers = false;
+ } else if (!F->value) {
+ has_all_peers = false;
+ }
+ }
+ }
+
+ if (peers_to_add.size()) {
+ _send_confirm_path(node, for_path, psc, peers_to_add);
+ }
+
+ return has_all_peers;
+}
+
+Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND_V(!root_node, nullptr);
+ HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from);
+ ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
+
+ HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id);
+ ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from));
+
+ PathGetCache::NodeInfo *ni = &F->value;
+ Node *node = root_node->get_node(ni->path);
+ if (!node) {
+ ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
+ }
+ return node;
+}
+
+void SceneCacheInterface::clear() {
+ path_get_cache.clear();
+ path_send_cache.clear();
+ last_send_cache_id = 1;
+}
diff --git a/modules/multiplayer/scene_cache_interface.h b/modules/multiplayer/scene_cache_interface.h
new file mode 100644
index 0000000000..1e80792fe7
--- /dev/null
+++ b/modules/multiplayer/scene_cache_interface.h
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* scene_cache_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_CACHE_INTERFACE_H
+#define SCENE_CACHE_INTERFACE_H
+
+#include "scene/main/multiplayer_api.h"
+
+class SceneMultiplayer;
+
+class SceneCacheInterface : public RefCounted {
+ GDCLASS(SceneCacheInterface, RefCounted);
+
+private:
+ SceneMultiplayer *multiplayer = nullptr;
+
+ //path sent caches
+ struct PathSentCache {
+ HashMap<int, bool> confirmed_peers;
+ int id;
+ };
+
+ //path get caches
+ struct PathGetCache {
+ struct NodeInfo {
+ NodePath path;
+ ObjectID instance;
+ };
+
+ HashMap<int, NodeInfo> nodes;
+ };
+
+ HashMap<NodePath, PathSentCache> path_send_cache;
+ HashMap<int, PathGetCache> path_get_cache;
+ int last_send_cache_id = 1;
+
+protected:
+ Error _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers);
+
+public:
+ void clear();
+ void on_peer_change(int p_id, bool p_connected);
+ void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+
+ // Returns true if all peers have cached path.
+ bool send_object_cache(Object *p_obj, int p_target, int &p_id);
+ int make_object_cache(Object *p_obj);
+ Object *get_cached_object(int p_from, uint32_t p_cache_id);
+ bool is_cache_confirmed(NodePath p_path, int p_peer);
+
+ SceneCacheInterface(SceneMultiplayer *p_multiplayer) { multiplayer = p_multiplayer; }
+};
+
+#endif // SCENE_CACHE_INTERFACE_H
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
new file mode 100644
index 0000000000..3fc1eef366
--- /dev/null
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -0,0 +1,332 @@
+/*************************************************************************/
+/* scene_multiplayer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_multiplayer.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+
+#include <stdint.h>
+
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#endif
+
+#ifdef DEBUG_ENABLED
+void SceneMultiplayer::profile_bandwidth(const String &p_inout, int p_size) {
+ if (EngineDebugger::is_profiling("multiplayer")) {
+ Array values;
+ values.push_back(p_inout);
+ values.push_back(OS::get_singleton()->get_ticks_msec());
+ values.push_back(p_size);
+ EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ }
+}
+#endif
+
+Error SceneMultiplayer::poll() {
+ if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) {
+ return ERR_UNCONFIGURED;
+ }
+
+ multiplayer_peer->poll();
+
+ if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here.
+ return OK;
+ }
+
+ while (multiplayer_peer->get_available_packet_count()) {
+ int sender = multiplayer_peer->get_packet_peer();
+ const uint8_t *packet;
+ int len;
+
+ Error err = multiplayer_peer->get_packet(&packet, len);
+ ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err));
+
+ remote_sender_id = sender;
+ _process_packet(sender, packet, len);
+ remote_sender_id = 0;
+
+ if (!multiplayer_peer.is_valid()) {
+ return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here.
+ }
+ }
+ replicator->on_network_process();
+ return OK;
+}
+
+void SceneMultiplayer::clear() {
+ connected_peers.clear();
+ packet_cache.clear();
+ cache->clear();
+}
+
+void SceneMultiplayer::set_root_path(const NodePath &p_path) {
+ ERR_FAIL_COND_MSG(!p_path.is_absolute() && !p_path.is_empty(), "SceneMultiplayer root path must be absolute.");
+ root_path = p_path;
+}
+
+NodePath SceneMultiplayer::get_root_path() const {
+ return root_path;
+}
+
+void SceneMultiplayer::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
+ if (p_peer == multiplayer_peer) {
+ return; // Nothing to do
+ }
+
+ ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED,
+ "Supplied MultiplayerPeer must be connecting or connected.");
+
+ if (multiplayer_peer.is_valid()) {
+ multiplayer_peer->disconnect("peer_connected", callable_mp(this, &SceneMultiplayer::_add_peer));
+ multiplayer_peer->disconnect("peer_disconnected", callable_mp(this, &SceneMultiplayer::_del_peer));
+ multiplayer_peer->disconnect("connection_succeeded", callable_mp(this, &SceneMultiplayer::_connected_to_server));
+ multiplayer_peer->disconnect("connection_failed", callable_mp(this, &SceneMultiplayer::_connection_failed));
+ multiplayer_peer->disconnect("server_disconnected", callable_mp(this, &SceneMultiplayer::_server_disconnected));
+ clear();
+ }
+
+ multiplayer_peer = p_peer;
+
+ if (multiplayer_peer.is_valid()) {
+ multiplayer_peer->connect("peer_connected", callable_mp(this, &SceneMultiplayer::_add_peer));
+ multiplayer_peer->connect("peer_disconnected", callable_mp(this, &SceneMultiplayer::_del_peer));
+ multiplayer_peer->connect("connection_succeeded", callable_mp(this, &SceneMultiplayer::_connected_to_server));
+ multiplayer_peer->connect("connection_failed", callable_mp(this, &SceneMultiplayer::_connection_failed));
+ multiplayer_peer->connect("server_disconnected", callable_mp(this, &SceneMultiplayer::_server_disconnected));
+ }
+ replicator->on_reset();
+}
+
+Ref<MultiplayerPeer> SceneMultiplayer::get_multiplayer_peer() {
+ return multiplayer_peer;
+}
+
+void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(root_path.is_empty(), "Multiplayer root was not initialized. If you are using custom multiplayer, remember to set the root path via SceneMultiplayer.set_root_path before using it.");
+ ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
+
+#ifdef DEBUG_ENABLED
+ profile_bandwidth("in", p_packet_len);
+#endif
+
+ // Extract the `packet_type` from the LSB three bits:
+ uint8_t packet_type = p_packet[0] & CMD_MASK;
+
+ switch (packet_type) {
+ case NETWORK_COMMAND_SIMPLIFY_PATH: {
+ cache->process_simplify_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_CONFIRM_PATH: {
+ cache->process_confirm_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_REMOTE_CALL: {
+ rpc->process_rpc(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_RAW: {
+ _process_raw(p_from, p_packet, p_packet_len);
+ } break;
+ case NETWORK_COMMAND_SPAWN: {
+ replicator->on_spawn_receive(p_from, p_packet, p_packet_len);
+ } break;
+ case NETWORK_COMMAND_DESPAWN: {
+ replicator->on_despawn_receive(p_from, p_packet, p_packet_len);
+ } break;
+ case NETWORK_COMMAND_SYNC: {
+ replicator->on_sync_receive(p_from, p_packet, p_packet_len);
+ } break;
+ }
+}
+
+void SceneMultiplayer::_add_peer(int p_id) {
+ connected_peers.insert(p_id);
+ cache->on_peer_change(p_id, true);
+ replicator->on_peer_change(p_id, true);
+ emit_signal(SNAME("peer_connected"), p_id);
+}
+
+void SceneMultiplayer::_del_peer(int p_id) {
+ replicator->on_peer_change(p_id, false);
+ cache->on_peer_change(p_id, false);
+ connected_peers.erase(p_id);
+ emit_signal(SNAME("peer_disconnected"), p_id);
+}
+
+void SceneMultiplayer::_connected_to_server() {
+ emit_signal(SNAME("connected_to_server"));
+}
+
+void SceneMultiplayer::_connection_failed() {
+ emit_signal(SNAME("connection_failed"));
+}
+
+void SceneMultiplayer::_server_disconnected() {
+ replicator->on_reset();
+ emit_signal(SNAME("server_disconnected"));
+}
+
+Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) {
+ ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet.");
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no multiplayer peer is active.");
+ ERR_FAIL_COND_V_MSG(multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a multiplayer peer which is not connected.");
+
+ if (packet_cache.size() < p_data.size() + 1) {
+ packet_cache.resize(p_data.size() + 1);
+ }
+
+ const uint8_t *r = p_data.ptr();
+ packet_cache.write[0] = NETWORK_COMMAND_RAW;
+ memcpy(&packet_cache.write[1], &r[0], p_data.size());
+
+ multiplayer_peer->set_target_peer(p_to);
+ multiplayer_peer->set_transfer_channel(p_channel);
+ multiplayer_peer->set_transfer_mode(p_mode);
+
+ return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
+}
+
+void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
+
+ Vector<uint8_t> out;
+ int len = p_packet_len - 1;
+ out.resize(len);
+ {
+ uint8_t *w = out.ptrw();
+ memcpy(&w[0], &p_packet[1], len);
+ }
+ emit_signal(SNAME("peer_packet"), p_from, out);
+}
+
+int SceneMultiplayer::get_unique_id() {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), 0, "No multiplayer peer is assigned. Unable to get unique ID.");
+ return multiplayer_peer->get_unique_id();
+}
+
+void SceneMultiplayer::set_refuse_new_connections(bool p_refuse) {
+ ERR_FAIL_COND_MSG(!multiplayer_peer.is_valid(), "No multiplayer peer is assigned. Unable to set 'refuse_new_connections'.");
+ multiplayer_peer->set_refuse_new_connections(p_refuse);
+}
+
+bool SceneMultiplayer::is_refusing_new_connections() const {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), false, "No multiplayer peer is assigned. Unable to get 'refuse_new_connections'.");
+ return multiplayer_peer->is_refusing_new_connections();
+}
+
+Vector<int> SceneMultiplayer::get_peer_ids() {
+ ERR_FAIL_COND_V_MSG(!multiplayer_peer.is_valid(), Vector<int>(), "No multiplayer peer is assigned. Assume no peers are connected.");
+
+ Vector<int> ret;
+ for (const int &E : connected_peers) {
+ ret.push_back(E);
+ }
+
+ return ret;
+}
+
+void SceneMultiplayer::set_allow_object_decoding(bool p_enable) {
+ allow_object_decoding = p_enable;
+}
+
+bool SceneMultiplayer::is_object_decoding_allowed() const {
+ return allow_object_decoding;
+}
+
+String SceneMultiplayer::get_rpc_md5(const Object *p_obj) {
+ return rpc->get_rpc_md5(p_obj);
+}
+
+Error SceneMultiplayer::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ return rpc->rpcp(p_obj, p_peer_id, p_method, p_arg, p_argcount);
+}
+
+Error SceneMultiplayer::object_configuration_add(Object *p_obj, Variant p_config) {
+ if (p_obj == nullptr && p_config.get_type() == Variant::NODE_PATH) {
+ set_root_path(p_config);
+ return OK;
+ }
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ if (spawner) {
+ return replicator->on_spawn(p_obj, p_config);
+ } else if (sync) {
+ return replicator->on_replication_start(p_obj, p_config);
+ }
+ return ERR_INVALID_PARAMETER;
+}
+
+Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_config) {
+ if (p_obj == nullptr && p_config.get_type() == Variant::NODE_PATH) {
+ ERR_FAIL_COND_V(root_path != p_config.operator NodePath(), ERR_INVALID_PARAMETER);
+ set_root_path(NodePath());
+ return OK;
+ }
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ if (spawner) {
+ return replicator->on_despawn(p_obj, p_config);
+ }
+ if (sync) {
+ return replicator->on_replication_stop(p_obj, p_config);
+ }
+ return ERR_INVALID_PARAMETER;
+}
+
+void SceneMultiplayer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path);
+ ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path);
+ ClassDB::bind_method(D_METHOD("clear"), &SceneMultiplayer::clear);
+ ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "refuse"), &SceneMultiplayer::set_refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections);
+ ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding);
+ ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &SceneMultiplayer::is_object_decoding_allowed);
+ ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
+ ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
+
+ ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
+}
+
+SceneMultiplayer::SceneMultiplayer() {
+ replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this)));
+ rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this)));
+ cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));
+}
+
+SceneMultiplayer::~SceneMultiplayer() {
+ clear();
+}
diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h
new file mode 100644
index 0000000000..a99cca7b21
--- /dev/null
+++ b/modules/multiplayer/scene_multiplayer.h
@@ -0,0 +1,136 @@
+/*************************************************************************/
+/* scene_multiplayer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_MULTIPLAYER_H
+#define SCENE_MULTIPLAYER_H
+
+#include "scene/main/multiplayer_api.h"
+
+#include "scene_cache_interface.h"
+#include "scene_replication_interface.h"
+#include "scene_rpc_interface.h"
+
+class SceneMultiplayer : public MultiplayerAPI {
+ GDCLASS(SceneMultiplayer, MultiplayerAPI);
+
+public:
+ enum NetworkCommands {
+ NETWORK_COMMAND_REMOTE_CALL = 0,
+ NETWORK_COMMAND_SIMPLIFY_PATH,
+ NETWORK_COMMAND_CONFIRM_PATH,
+ NETWORK_COMMAND_RAW,
+ NETWORK_COMMAND_SPAWN,
+ NETWORK_COMMAND_DESPAWN,
+ NETWORK_COMMAND_SYNC,
+ };
+
+ // For each command, the 4 MSB can contain custom flags, as defined by subsystems.
+ enum {
+ CMD_FLAG_0_SHIFT = 4,
+ CMD_FLAG_1_SHIFT = 5,
+ CMD_FLAG_2_SHIFT = 6,
+ CMD_FLAG_3_SHIFT = 7,
+ };
+
+ // This is the mask that will be used to extract the command.
+ enum {
+ CMD_MASK = 7, // 0x7 -> 0b00001111
+ };
+
+private:
+ Ref<MultiplayerPeer> multiplayer_peer;
+ HashSet<int> connected_peers;
+ int remote_sender_id = 0;
+ int remote_sender_override = 0;
+
+ Vector<uint8_t> packet_cache;
+
+ NodePath root_path;
+ bool allow_object_decoding = false;
+
+ Ref<SceneCacheInterface> cache;
+ Ref<SceneReplicationInterface> replicator;
+ Ref<SceneRPCInterface> rpc;
+
+protected:
+ static void _bind_methods();
+
+ void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
+
+ void _add_peer(int p_id);
+ void _del_peer(int p_id);
+ void _connected_to_server();
+ void _connection_failed();
+ void _server_disconnected();
+
+public:
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
+
+ virtual Error poll() override;
+ virtual int get_unique_id() override;
+ virtual Vector<int> get_peer_ids() override;
+ virtual int get_remote_sender_id() override { return remote_sender_override ? remote_sender_override : remote_sender_id; }
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
+
+ virtual Error object_configuration_add(Object *p_obj, Variant p_config) override;
+ virtual Error object_configuration_remove(Object *p_obj, Variant p_config) override;
+
+ void clear();
+
+ // Usually from object_configuration_add/remove
+ void set_root_path(const NodePath &p_path);
+ NodePath get_root_path() const;
+
+ Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
+ String get_rpc_md5(const Object *p_obj);
+
+ const HashSet<int> get_connected_peers() const { return connected_peers; }
+
+ void set_remote_sender_override(int p_id) { remote_sender_override = p_id; }
+ void set_refuse_new_connections(bool p_refuse);
+ bool is_refusing_new_connections() const;
+
+ void set_allow_object_decoding(bool p_enable);
+ bool is_object_decoding_allowed() const;
+
+ Ref<SceneCacheInterface> get_path_cache() { return cache; }
+
+#ifdef DEBUG_ENABLED
+ void profile_bandwidth(const String &p_inout, int p_size);
+#endif
+
+ SceneMultiplayer();
+ ~SceneMultiplayer();
+};
+
+#endif // SCENE_MULTIPLAYER_H
diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp
new file mode 100644
index 0000000000..ae06516b7b
--- /dev/null
+++ b/modules/multiplayer/scene_replication_config.cpp
@@ -0,0 +1,205 @@
+/*************************************************************************/
+/* scene_replication_config.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_replication_config.h"
+
+#include "scene/main/multiplayer_api.h"
+#include "scene/main/node.h"
+
+bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+
+ if (name.begins_with("properties/")) {
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ if (properties.size() == idx && what == "path") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false);
+ NodePath path = p_value;
+ ERR_FAIL_COND_V(path.is_empty() || path.get_subname_count() == 0, false);
+ add_property(path);
+ return true;
+ }
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
+ ERR_FAIL_INDEX_V(idx, properties.size(), false);
+ ReplicationProperty &prop = properties[idx];
+ if (what == "sync") {
+ prop.sync = p_value;
+ if (prop.sync) {
+ sync_props.push_back(prop.name);
+ } else {
+ sync_props.erase(prop.name);
+ }
+ return true;
+ } else if (what == "spawn") {
+ prop.spawn = p_value;
+ if (prop.spawn) {
+ spawn_props.push_back(prop.name);
+ } else {
+ spawn_props.erase(prop.name);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const {
+ String name = p_name;
+
+ if (name.begins_with("properties/")) {
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(idx, properties.size(), false);
+ const ReplicationProperty &prop = properties[idx];
+ if (what == "path") {
+ r_ret = prop.name;
+ return true;
+ } else if (what == "sync") {
+ r_ret = prop.sync;
+ return true;
+ } else if (what == "spawn") {
+ r_ret = prop.spawn;
+ return true;
+ }
+ }
+ return false;
+}
+
+void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < properties.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ }
+}
+
+TypedArray<NodePath> SceneReplicationConfig::get_properties() const {
+ TypedArray<NodePath> paths;
+ for (const ReplicationProperty &prop : properties) {
+ paths.push_back(prop.name);
+ }
+ return paths;
+}
+
+void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) {
+ ERR_FAIL_COND(properties.find(p_path));
+
+ if (p_index < 0 || p_index == properties.size()) {
+ properties.push_back(ReplicationProperty(p_path));
+ return;
+ }
+
+ ERR_FAIL_INDEX(p_index, properties.size());
+
+ List<ReplicationProperty>::Element *I = properties.front();
+ int c = 0;
+ while (c < p_index) {
+ I = I->next();
+ c++;
+ }
+ properties.insert_before(I, ReplicationProperty(p_path));
+}
+
+void SceneReplicationConfig::remove_property(const NodePath &p_path) {
+ properties.erase(p_path);
+}
+
+bool SceneReplicationConfig::has_property(const NodePath &p_path) const {
+ for (int i = 0; i < properties.size(); i++) {
+ if (properties[i].name == p_path) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int SceneReplicationConfig::property_get_index(const NodePath &p_path) const {
+ for (int i = 0; i < properties.size(); i++) {
+ if (properties[i].name == p_path) {
+ return i;
+ }
+ }
+ ERR_FAIL_V(-1);
+}
+
+bool SceneReplicationConfig::property_get_spawn(const NodePath &p_path) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND_V(!E, false);
+ return E->get().spawn;
+}
+
+void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_enabled) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND(!E);
+ if (E->get().spawn == p_enabled) {
+ return;
+ }
+ E->get().spawn = p_enabled;
+ spawn_props.clear();
+ for (const ReplicationProperty &prop : properties) {
+ if (prop.spawn) {
+ spawn_props.push_back(p_path);
+ }
+ }
+}
+
+bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND_V(!E, false);
+ return E->get().sync;
+}
+
+void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND(!E);
+ if (E->get().sync == p_enabled) {
+ return;
+ }
+ E->get().sync = p_enabled;
+ sync_props.clear();
+ for (const ReplicationProperty &prop : properties) {
+ if (prop.sync) {
+ sync_props.push_back(p_path);
+ }
+ }
+}
+
+void SceneReplicationConfig::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties);
+ ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("has_property", "path"), &SceneReplicationConfig::has_property);
+ ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property);
+ ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index);
+ ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn);
+ ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn);
+ ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync);
+ ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync);
+}
diff --git a/modules/multiplayer/scene_replication_config.h b/modules/multiplayer/scene_replication_config.h
new file mode 100644
index 0000000000..ab3658d2a7
--- /dev/null
+++ b/modules/multiplayer/scene_replication_config.h
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* scene_replication_config.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_REPLICATION_CONFIG_H
+#define SCENE_REPLICATION_CONFIG_H
+
+#include "core/io/resource.h"
+
+#include "core/variant/typed_array.h"
+
+class SceneReplicationConfig : public Resource {
+ GDCLASS(SceneReplicationConfig, Resource);
+ OBJ_SAVE_TYPE(SceneReplicationConfig);
+ RES_BASE_EXTENSION("repl");
+
+private:
+ struct ReplicationProperty {
+ NodePath name;
+ bool spawn = true;
+ bool sync = true;
+
+ bool operator==(const ReplicationProperty &p_to) {
+ return name == p_to.name;
+ }
+
+ ReplicationProperty() {}
+
+ ReplicationProperty(const NodePath &p_name) {
+ name = p_name;
+ }
+ };
+
+ List<ReplicationProperty> properties;
+ List<NodePath> spawn_props;
+ List<NodePath> sync_props;
+
+protected:
+ static void _bind_methods();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ TypedArray<NodePath> get_properties() const;
+
+ void add_property(const NodePath &p_path, int p_index = -1);
+ void remove_property(const NodePath &p_path);
+ bool has_property(const NodePath &p_path) const;
+
+ int property_get_index(const NodePath &p_path) const;
+ bool property_get_spawn(const NodePath &p_path);
+ void property_set_spawn(const NodePath &p_path, bool p_enabled);
+
+ bool property_get_sync(const NodePath &p_path);
+ void property_set_sync(const NodePath &p_path, bool p_enabled);
+
+ const List<NodePath> &get_spawn_properties() { return spawn_props; }
+ const List<NodePath> &get_sync_properties() { return sync_props; }
+
+ SceneReplicationConfig() {}
+};
+
+#endif // SCENE_REPLICATION_CONFIG_H
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
new file mode 100644
index 0000000000..c89270fbe8
--- /dev/null
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -0,0 +1,527 @@
+/*************************************************************************/
+/* scene_replication_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_replication_interface.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
+#include "scene_multiplayer.h"
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) \
+ packet_cache.resize(m_amount);
+
+void SceneReplicationInterface::_free_remotes(int p_id) {
+ const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id);
+ for (const KeyValue<uint32_t, ObjectID> &E : remotes) {
+ Node *node = rep_state->get_node(E.value);
+ ERR_CONTINUE(!node);
+ node->queue_delete();
+ }
+}
+
+void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
+ if (p_connected) {
+ rep_state->on_peer_change(p_id, p_connected);
+ for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
+ _update_spawn_visibility(p_id, oid);
+ }
+ for (const ObjectID &oid : rep_state->get_synced_nodes()) {
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!sync); // ERR_BUG
+ if (sync->is_multiplayer_authority()) {
+ _update_sync_visibility(p_id, oid);
+ }
+ }
+ } else {
+ _free_remotes(p_id);
+ rep_state->on_peer_change(p_id, p_connected);
+ }
+}
+
+void SceneReplicationInterface::on_reset() {
+ for (int pid : rep_state->get_peers()) {
+ _free_remotes(pid);
+ }
+ rep_state->reset();
+}
+
+void SceneReplicationInterface::on_network_process() {
+ uint64_t msec = OS::get_singleton()->get_ticks_msec();
+ for (int peer : rep_state->get_peers()) {
+ _send_sync(peer, msec);
+ }
+}
+
+Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
+ Error err = rep_state->config_add_spawn(node, spawner);
+ ERR_FAIL_COND_V(err != OK, err);
+ const ObjectID oid = node->get_instance_id();
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ rep_state->ensure_net_id(oid);
+ _update_spawn_visibility(0, oid);
+ }
+ ERR_FAIL_COND_V(err != OK, err);
+ return OK;
+}
+
+Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER);
+ // Forcibly despawn to all peers that knowns me.
+ int len = 0;
+ Error err = _make_despawn_packet(node, len);
+ ERR_FAIL_COND_V(err != OK, ERR_BUG);
+ const ObjectID oid = p_obj->get_instance_id();
+ for (int pid : rep_state->get_peers()) {
+ if (!rep_state->is_peer_spawn(pid, oid)) {
+ continue;
+ }
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ // Also remove spawner tracking from the replication state.
+ return rep_state->config_del_spawn(node, spawner);
+}
+
+Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+
+ // Add to synchronizer list and setup visibility.
+ rep_state->config_add_sync(node, sync);
+ const ObjectID oid = node->get_instance_id();
+ sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(oid));
+ if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
+ _update_sync_visibility(0, oid);
+ }
+
+ // Try to apply initial state if spawning (hack to apply if before ready).
+ if (pending_spawn == p_obj->get_instance_id()) {
+ pending_spawn = ObjectID(); // Make sure this only happens once.
+ const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
+ Vector<Variant> vars;
+ vars.resize(props.size());
+ int consumed;
+ Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
+ ERR_FAIL_COND_V(err, err);
+ err = MultiplayerSynchronizer::set_state(props, node, vars);
+ ERR_FAIL_COND_V(err, err);
+ }
+ return OK;
+}
+
+Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+ sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed));
+ return rep_state->config_del_sync(node, sync);
+}
+
+void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) {
+ if (rep_state->is_spawned_node(p_oid)) {
+ _update_spawn_visibility(p_peer, p_oid);
+ }
+ if (rep_state->is_synced_node(p_oid)) {
+ _update_sync_visibility(p_peer, p_oid);
+ }
+}
+
+Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = sync->is_visible_to(p_peer);
+ if (p_peer == 0) {
+ for (int pid : rep_state->get_peers()) {
+ // Might be visible to this specific peer.
+ is_visible = is_visible || sync->is_visible_to(pid);
+ if (rep_state->is_peer_sync(pid, p_oid) == is_visible) {
+ continue;
+ }
+ if (is_visible) {
+ rep_state->peer_add_sync(pid, p_oid);
+ } else {
+ rep_state->peer_del_sync(pid, p_oid);
+ }
+ }
+ return OK;
+ } else {
+ if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ return rep_state->peer_add_sync(p_peer, p_oid);
+ } else {
+ return rep_state->peer_del_sync(p_peer, p_oid);
+ }
+ }
+}
+
+Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) {
+ MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid);
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid));
+ ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
+ bool is_visible = !sync || sync->is_visible_to(p_peer);
+ // Spawn (and despawn) when needed.
+ HashSet<int> to_spawn;
+ HashSet<int> to_despawn;
+ if (p_peer) {
+ if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) {
+ return OK;
+ }
+ if (is_visible) {
+ to_spawn.insert(p_peer);
+ } else {
+ to_despawn.insert(p_peer);
+ }
+ } else {
+ // Check visibility for each peers.
+ for (int pid : rep_state->get_peers()) {
+ bool peer_visible = is_visible || sync->is_visible_to(pid);
+ if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) {
+ continue;
+ }
+ if (peer_visible) {
+ to_spawn.insert(pid);
+ } else {
+ to_despawn.insert(pid);
+ }
+ }
+ }
+ if (to_spawn.size()) {
+ int len = 0;
+ _make_spawn_packet(node, len);
+ for (int pid : to_spawn) {
+ int path_id;
+ multiplayer->get_path_cache()->send_object_cache(spawner, pid, path_id);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ rep_state->peer_add_spawn(pid, p_oid);
+ }
+ }
+ if (to_despawn.size()) {
+ int len = 0;
+ _make_despawn_packet(node, len);
+ for (int pid : to_despawn) {
+ rep_state->peer_del_spawn(pid, p_oid);
+ _send_raw(packet_cache.ptr(), len, pid, true);
+ }
+ }
+ return OK;
+}
+
+Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
+ ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+
+#ifdef DEBUG_ENABLED
+ multiplayer->profile_bandwidth("out", p_size);
+#endif
+
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer);
+ peer->set_transfer_channel(0);
+ peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE);
+ return peer->put_packet(p_buffer, p_size);
+}
+
+Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
+ ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
+
+ const ObjectID oid = p_node->get_instance_id();
+ MultiplayerSpawner *spawner = rep_state->get_spawner(oid);
+ ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG);
+
+ uint32_t nid = rep_state->get_net_id(oid);
+ ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED);
+
+ // Prepare custom arg and scene_id
+ uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid);
+ bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
+ Variant spawn_arg = spawner->get_spawn_argument(oid);
+ int spawn_arg_size = 0;
+ if (is_custom) {
+ Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
+ ERR_FAIL_COND_V(err, err);
+ }
+
+ // Prepare spawn state.
+ int state_size = 0;
+ Vector<Variant> state_vars;
+ Vector<const Variant *> state_varp;
+ MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
+ if (synchronizer) {
+ ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG);
+ const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
+ Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
+ err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
+ }
+
+ // Encode scene ID, path ID, net ID, node name.
+ int path_id = multiplayer->get_path_cache()->make_object_cache(spawner);
+ CharString cname = p_node->get_name().operator String().utf8();
+ int nlen = encode_cstring(cname.get_data(), nullptr);
+ MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_SPAWN;
+ ptr[1] = scene_id;
+ int ofs = 2;
+ ofs += encode_uint32(path_id, &ptr[ofs]);
+ ofs += encode_uint32(nid, &ptr[ofs]);
+ ofs += encode_uint32(nlen, &ptr[ofs]);
+ ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
+ // Write args
+ if (is_custom) {
+ ofs += encode_uint32(spawn_arg_size, &ptr[ofs]);
+ Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false);
+ ERR_FAIL_COND_V(err, err);
+ ofs += spawn_arg_size;
+ }
+ // Write state.
+ if (state_size) {
+ Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size);
+ ERR_FAIL_COND_V(err, err);
+ ofs += state_size;
+ }
+ r_len = ofs;
+ return OK;
+}
+
+Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
+ const ObjectID oid = p_node->get_instance_id();
+ MAKE_ROOM(5);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (uint8_t)SceneMultiplayer::NETWORK_COMMAND_DESPAWN;
+ int ofs = 1;
+ uint32_t nid = rep_state->get_net_id(oid);
+ ofs += encode_uint32(nid, &ptr[ofs]);
+ r_len = ofs;
+ return OK;
+}
+
+Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received");
+ int ofs = 1; // The spawn/despawn command.
+ uint8_t scene_id = p_buffer[ofs];
+ ofs += 1;
+ uint32_t node_target = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_path_cache()->get_cached_object(p_from, node_target));
+ ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
+ ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
+
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ uint32_t name_len = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len));
+ ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size.");
+
+ // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names.
+ const String name = String::utf8((const char *)&p_buffer[ofs], name_len);
+ ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely.", name));
+ ofs += name_len;
+
+ // Check that we can spawn.
+ Node *parent = spawner->get_node_or_null(spawner->get_spawn_path());
+ ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA);
+
+ Node *node = nullptr;
+ if (scene_id == MultiplayerSpawner::INVALID_ID) {
+ // Custom spawn.
+ ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA);
+ uint32_t arg_size = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
+ Variant v;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false);
+ ERR_FAIL_COND_V(err != OK, err);
+ ofs += arg_size;
+ node = spawner->instantiate_custom(v);
+ } else {
+ // Scene based spawn.
+ node = spawner->instantiate_scene(scene_id);
+ }
+ ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
+ node->set_name(name);
+ rep_state->peer_add_remote(p_from, net_id, node, spawner);
+ // The initial state will be applied during the sync config (i.e. before _ready).
+ int state_len = p_buffer_len - ofs;
+ if (state_len) {
+ pending_spawn = node->get_instance_id();
+ pending_buffer = &p_buffer[ofs];
+ pending_buffer_size = state_len;
+ }
+ parent->add_child(node);
+ pending_spawn = ObjectID();
+ pending_buffer = nullptr;
+ pending_buffer_size = 0;
+ return OK;
+}
+
+Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received");
+ int ofs = 1; // The spawn/despawn command.
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ Node *node = nullptr;
+ Error err = rep_state->peer_del_remote(p_from, net_id, &node);
+ ERR_FAIL_COND_V(err != OK, err);
+ ERR_FAIL_COND_V(!node, ERR_BUG);
+ if (node->get_parent() != nullptr) {
+ node->get_parent()->remove_child(node);
+ }
+ node->queue_delete();
+ return OK;
+}
+
+void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
+ const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer);
+ if (to_sync.is_empty()) {
+ return;
+ }
+ MAKE_ROOM(sync_mtu);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC;
+ int ofs = 1;
+ ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
+ // Can only send updates for already notified nodes.
+ // This is a lazy implementation, we could optimize much more here with by grouping by replication config.
+ for (const ObjectID &oid : to_sync) {
+ if (!rep_state->update_sync_time(oid, p_msec)) {
+ continue; // nothing to sync.
+ }
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid());
+ Node *node = rep_state->get_node(oid);
+ ERR_CONTINUE(!node);
+ uint32_t net_id = rep_state->get_net_id(oid);
+ if (net_id == 0 || (net_id & 0x80000000)) {
+ int path_id = 0;
+ bool verified = multiplayer->get_path_cache()->send_object_cache(sync, p_peer, path_id);
+ ERR_CONTINUE_MSG(path_id < 0, "This should never happen!");
+ if (net_id == 0) {
+ // First time path based ID.
+ net_id = path_id | 0x80000000;
+ rep_state->set_net_id(oid, net_id | 0x80000000);
+ }
+ if (!verified) {
+ // The path based sync is not yet confirmed, skipping.
+ continue;
+ }
+ }
+ int size;
+ Vector<Variant> vars;
+ Vector<const Variant *> varp;
+ const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
+ Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp);
+ ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state.");
+ err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size);
+ ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state.");
+ // TODO Handle single state above MTU.
+ ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path()));
+ if (ofs + 4 + 4 + size > sync_mtu) {
+ // Send what we got, and reset write.
+ _send_raw(packet_cache.ptr(), ofs, p_peer, false);
+ ofs = 3;
+ }
+ if (size) {
+ ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
+ ofs += encode_uint32(size, &ptr[ofs]);
+ MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
+ ofs += size;
+ }
+ }
+ if (ofs > 3) {
+ // Got some left over to send.
+ _send_raw(packet_cache.ptr(), ofs, p_peer, false);
+ }
+}
+
+Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received");
+ uint16_t time = decode_uint16(&p_buffer[1]);
+ int ofs = 3;
+ rep_state->peer_sync_recv(p_from, time);
+ while (ofs + 8 < p_buffer_len) {
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ uint32_t size = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ Node *node = nullptr;
+ if (net_id & 0x80000000) {
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
+ ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED);
+ node = sync->get_node(sync->get_root_path());
+ } else {
+ node = rep_state->peer_get_remote(p_from, net_id);
+ }
+ if (!node) {
+ // Not received yet.
+ ofs += size;
+ continue;
+ }
+ const ObjectID oid = node->get_instance_id();
+ if (!rep_state->update_last_node_sync(oid, time)) {
+ // State is too old.
+ ofs += size;
+ continue;
+ }
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_FAIL_COND_V(!sync, ERR_BUG);
+ ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
+ const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
+ Vector<Variant> vars;
+ vars.resize(props.size());
+ int consumed;
+ Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed);
+ ERR_FAIL_COND_V(err, err);
+ err = MultiplayerSynchronizer::set_state(props, node, vars);
+ ERR_FAIL_COND_V(err, err);
+ ofs += size;
+ }
+ return OK;
+}
diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h
new file mode 100644
index 0000000000..8981647429
--- /dev/null
+++ b/modules/multiplayer/scene_replication_interface.h
@@ -0,0 +1,86 @@
+/*************************************************************************/
+/* scene_replication_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_REPLICATION_INTERFACE_H
+#define SCENE_REPLICATION_INTERFACE_H
+
+#include "scene/main/multiplayer_api.h"
+
+#include "scene_replication_state.h"
+
+class SceneMultiplayer;
+
+class SceneReplicationInterface : public RefCounted {
+ GDCLASS(SceneReplicationInterface, RefCounted);
+
+private:
+ void _send_sync(int p_peer, uint64_t p_msec);
+ Error _make_spawn_packet(Node *p_node, int &r_len);
+ Error _make_despawn_packet(Node *p_node, int &r_len);
+ Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+
+ void _visibility_changed(int p_peer, ObjectID p_oid);
+ Error _update_sync_visibility(int p_peer, const ObjectID &p_oid);
+ Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
+ void _free_remotes(int p_peer);
+
+ Ref<SceneReplicationState> rep_state;
+ SceneMultiplayer *multiplayer = nullptr;
+ PackedByteArray packet_cache;
+ int sync_mtu = 1350; // Highly dependent on underlying protocol.
+
+ // An hack to apply the initial state before ready.
+ ObjectID pending_spawn;
+ const uint8_t *pending_buffer = nullptr;
+ int pending_buffer_size = 0;
+
+public:
+ static void make_default();
+
+ void on_reset();
+ void on_peer_change(int p_id, bool p_connected);
+
+ Error on_spawn(Object *p_obj, Variant p_config);
+ Error on_despawn(Object *p_obj, Variant p_config);
+ Error on_replication_start(Object *p_obj, Variant p_config);
+ Error on_replication_stop(Object *p_obj, Variant p_config);
+ void on_network_process();
+
+ Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
+ Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
+ Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len);
+
+ SceneReplicationInterface(SceneMultiplayer *p_multiplayer) {
+ rep_state.instantiate();
+ multiplayer = p_multiplayer;
+ }
+};
+
+#endif // SCENE_REPLICATION_INTERFACE_H
diff --git a/modules/multiplayer/scene_replication_state.cpp b/modules/multiplayer/scene_replication_state.cpp
new file mode 100644
index 0000000000..25442bb7fa
--- /dev/null
+++ b/modules/multiplayer/scene_replication_state.cpp
@@ -0,0 +1,267 @@
+/*************************************************************************/
+/* scene_replication_state.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_replication_state.h"
+
+#include "scene/scene_string_names.h"
+
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
+
+SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) {
+ if (!tracked_nodes.has(p_id)) {
+ tracked_nodes[p_id] = TrackedNode(p_id);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
+ node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack).bind(p_id), Node::CONNECT_ONESHOT);
+ }
+ return tracked_nodes[p_id];
+}
+
+void SceneReplicationState::_untrack(const ObjectID &p_id) {
+ if (tracked_nodes.has(p_id)) {
+ uint32_t net_id = tracked_nodes[p_id].net_id;
+ uint32_t peer = tracked_nodes[p_id].remote_peer;
+ tracked_nodes.erase(p_id);
+ // If it was spawned by a remote, remove it from the received nodes.
+ if (peer && peers_info.has(peer)) {
+ peers_info[peer].recv_nodes.erase(net_id);
+ }
+ // If we spawned or synced it, we need to remove it from any peer it was sent to.
+ if (net_id || peer == 0) {
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.sync_nodes.erase(p_id);
+ E.value.spawn_nodes.erase(p_id);
+ }
+ }
+ }
+}
+
+const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const {
+ return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>();
+}
+
+bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, false);
+ if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) {
+ return false;
+ }
+ tnode->last_sync = p_time;
+ return true;
+}
+
+bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, false);
+ MultiplayerSynchronizer *sync = get_synchronizer(p_id);
+ if (!sync) {
+ return false;
+ }
+ if (tnode->last_sync_msec == p_msec) {
+ return true;
+ }
+ if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) {
+ tnode->last_sync_msec = p_msec;
+ return true;
+ }
+ return false;
+}
+
+uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
+ const TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, 0);
+ return tnode->net_id;
+}
+
+void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND(!tnode);
+ tnode->net_id = p_net_id;
+}
+
+uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, 0);
+ if (tnode->net_id == 0) {
+ tnode->net_id = ++last_net_id;
+ }
+ return tnode->net_id;
+}
+
+void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) {
+ if (p_connected) {
+ peers_info[p_peer] = PeerInfo();
+ known_peers.insert(p_peer);
+ } else {
+ peers_info.erase(p_peer);
+ known_peers.erase(p_peer);
+ }
+}
+
+void SceneReplicationState::reset() {
+ peers_info.clear();
+ known_peers.clear();
+ // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
+ for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) {
+ TrackedNode &tobj = E.value;
+ tobj.net_id = 0;
+ tobj.remote_peer = 0;
+ tobj.last_sync = 0;
+ }
+}
+
+Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
+ const ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
+ tobj.spawner = p_spawner->get_instance_id();
+ spawned_nodes.insert(oid);
+ return OK;
+}
+
+Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
+ const ObjectID oid = p_node->get_instance_id();
+ ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
+ tobj.spawner = ObjectID();
+ spawned_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.spawn_nodes.erase(oid);
+ }
+ return OK;
+}
+
+Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
+ const ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
+ tobj.synchronizer = p_sync->get_instance_id();
+ synced_nodes.insert(oid);
+ return OK;
+}
+
+Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
+ const ObjectID oid = p_node->get_instance_id();
+ ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
+ tobj.synchronizer = ObjectID();
+ synced_nodes.erase(oid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.sync_nodes.erase(oid);
+ }
+ return OK;
+}
+
+Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.insert(p_id);
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].sync_nodes.erase(p_id);
+ return OK;
+}
+
+const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].sync_nodes;
+}
+
+bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].sync_nodes.has(p_id);
+}
+
+Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.insert(p_id);
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].spawn_nodes.erase(p_id);
+ return OK;
+}
+
+const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>());
+ return peers_info[p_peer].spawn_nodes;
+}
+
+bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), false);
+ return peers_info[p_peer].spawn_nodes.has(p_id);
+}
+
+Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
+ PeerInfo *info = peers_info.getptr(p_peer);
+ return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
+}
+
+Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) {
+ ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE);
+ PeerInfo &pinfo = peers_info[p_peer];
+ ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ tobj.spawner = p_spawner->get_instance_id();
+ tobj.net_id = p_net_id;
+ tobj.remote_peer = p_peer;
+ tobj.last_sync = pinfo.last_recv_sync;
+ // Also track as a remote.
+ ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE);
+ pinfo.recv_nodes[p_net_id] = oid;
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED);
+ PeerInfo &info = peers_info[p_peer];
+ ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED);
+ *r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id]));
+ info.recv_nodes.erase(p_net_id);
+ return OK;
+}
+
+uint16_t SceneReplicationState::peer_sync_next(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), 0);
+ PeerInfo &info = peers_info[p_peer];
+ return ++info.last_sent_sync;
+}
+
+void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) {
+ ERR_FAIL_COND(!peers_info.has(p_peer));
+ peers_info[p_peer].last_recv_sync = p_time;
+}
diff --git a/modules/multiplayer/scene_replication_state.h b/modules/multiplayer/scene_replication_state.h
new file mode 100644
index 0000000000..bdff6ae3b7
--- /dev/null
+++ b/modules/multiplayer/scene_replication_state.h
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* scene_replication_state.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_REPLICATION_STATE_H
+#define SCENE_REPLICATION_STATE_H
+
+#include "core/object/ref_counted.h"
+
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
+
+class MultiplayerSpawner;
+class MultiplayerSynchronizer;
+class Node;
+
+class SceneReplicationState : public RefCounted {
+private:
+ struct TrackedNode {
+ ObjectID id;
+ uint32_t net_id = 0;
+ uint32_t remote_peer = 0;
+ ObjectID spawner;
+ ObjectID synchronizer;
+ uint16_t last_sync = 0;
+ uint64_t last_sync_msec = 0;
+
+ bool operator==(const ObjectID &p_other) { return id == p_other; }
+
+ Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; }
+ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
+ MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; }
+ TrackedNode() {}
+ TrackedNode(const ObjectID &p_id) { id = p_id; }
+ TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
+ id = p_id;
+ net_id = p_net_id;
+ }
+ };
+
+ struct PeerInfo {
+ HashSet<ObjectID> sync_nodes;
+ HashSet<ObjectID> spawn_nodes;
+ HashMap<uint32_t, ObjectID> recv_nodes;
+ uint16_t last_sent_sync = 0;
+ uint16_t last_recv_sync = 0;
+ };
+
+ HashSet<int> known_peers;
+ uint32_t last_net_id = 0;
+ HashMap<ObjectID, TrackedNode> tracked_nodes;
+ HashMap<int, PeerInfo> peers_info;
+ HashSet<ObjectID> spawned_nodes;
+ HashSet<ObjectID> synced_nodes;
+
+ TrackedNode &_track(const ObjectID &p_id);
+ void _untrack(const ObjectID &p_id);
+ bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); }
+
+public:
+ const HashSet<int> get_peers() const { return known_peers; }
+ const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; }
+ bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); }
+ const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; }
+ bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); }
+
+ MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
+ MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
+ Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; }
+ bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
+ bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
+
+ uint32_t get_net_id(const ObjectID &p_id) const;
+ void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
+ uint32_t ensure_net_id(const ObjectID &p_id);
+
+ void reset();
+ void on_peer_change(int p_peer, bool p_connected);
+
+ Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
+ Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
+
+ Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
+ Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
+
+ Error peer_add_sync(int p_peer, const ObjectID &p_id);
+ Error peer_del_sync(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_sync_nodes(int p_peer);
+ bool is_peer_sync(int p_peer, const ObjectID &p_id) const;
+
+ Error peer_add_spawn(int p_peer, const ObjectID &p_id);
+ Error peer_del_spawn(int p_peer, const ObjectID &p_id);
+
+ const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer);
+ bool is_peer_spawn(int p_peer, const ObjectID &p_id) const;
+
+ const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
+ Node *peer_get_remote(int p_peer, uint32_t p_net_id);
+ Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner);
+ Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node);
+
+ uint16_t peer_sync_next(int p_peer);
+ void peer_sync_recv(int p_peer, uint16_t p_time);
+
+ SceneReplicationState() {}
+};
+
+#endif // SCENE_REPLICATION_STATE_H
diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp
new file mode 100644
index 0000000000..65090b9316
--- /dev/null
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -0,0 +1,521 @@
+/*************************************************************************/
+/* scene_rpc_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_rpc_interface.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+#include "scene/main/multiplayer_api.h"
+#include "scene/main/node.h"
+#include "scene/main/window.h"
+
+#include "scene_multiplayer.h"
+
+// The RPC meta is composed by a single byte that contains (starting from the least significant bit):
+// - `NetworkCommands` in the first four bits.
+// - `NetworkNodeIdCompression` in the next 2 bits.
+// - `NetworkNameIdCompression` in the next 1 bit.
+// - `byte_only_or_no_args` in the next 1 bit.
+#define NODE_ID_COMPRESSION_SHIFT SceneMultiplayer::CMD_FLAG_0_SHIFT
+#define NAME_ID_COMPRESSION_SHIFT SceneMultiplayer::CMD_FLAG_2_SHIFT
+#define BYTE_ONLY_OR_NO_ARGS_SHIFT SceneMultiplayer::CMD_FLAG_3_SHIFT
+
+#define NODE_ID_COMPRESSION_FLAG ((1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)))
+#define NAME_ID_COMPRESSION_FLAG (1 << NAME_ID_COMPRESSION_SHIFT)
+#define BYTE_ONLY_OR_NO_ARGS_FLAG (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT)
+
+#ifdef DEBUG_ENABLED
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {
+ if (EngineDebugger::is_profiling("rpc")) {
+ Array values;
+ values.push_back(p_id);
+ values.push_back(p_what);
+ EngineDebugger::profiler_add_frame_data("rpc", values);
+ }
+}
+#else
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {}
+#endif
+
+// Returns the packet size stripping the node path added when the node is not yet cached.
+int get_packet_len(uint32_t p_node_target, int p_packet_len) {
+ if (p_node_target & 0x80000000) {
+ int ofs = p_node_target & 0x7FFFFFFF;
+ return p_packet_len - (p_packet_len - ofs);
+ } else {
+ return p_packet_len;
+ }
+}
+
+void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache) {
+ if (p_config.get_type() == Variant::NIL) {
+ return;
+ }
+ ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
+ const Dictionary config = p_config;
+ Array names = config.keys();
+ names.sort(); // Ensure ID order
+ for (int i = 0; i < names.size(); i++) {
+ ERR_CONTINUE(names[i].get_type() != Variant::STRING);
+ String name = names[i].operator String();
+ ERR_CONTINUE(config[name].get_type() != Variant::DICTIONARY);
+ ERR_CONTINUE(!config[name].operator Dictionary().has("rpc_mode"));
+ Dictionary dict = config[name];
+ RPCConfig cfg;
+ cfg.name = name;
+ cfg.rpc_mode = ((MultiplayerAPI::RPCMode)dict.get("rpc_mode", MultiplayerAPI::RPC_MODE_AUTHORITY).operator int());
+ cfg.transfer_mode = ((MultiplayerPeer::TransferMode)dict.get("transfer_mode", MultiplayerPeer::TRANSFER_MODE_RELIABLE).operator int());
+ cfg.call_local = dict.get("call_local", false).operator bool();
+ cfg.channel = dict.get("channel", 0).operator int();
+ uint16_t id = ((uint16_t)i);
+ if (p_for_node) {
+ id |= (1 << 15);
+ }
+ r_cache.configs[id] = cfg;
+ r_cache.ids[name] = id;
+ }
+}
+
+const SceneRPCInterface::RPCConfigCache &SceneRPCInterface::_get_node_config(const Node *p_node) {
+ const ObjectID oid = p_node->get_instance_id();
+ if (rpc_cache.has(oid)) {
+ return rpc_cache[oid];
+ }
+ RPCConfigCache cache;
+ _parse_rpc_config(p_node->get_node_rpc_config(), true, cache);
+ if (p_node->get_script_instance()) {
+ _parse_rpc_config(p_node->get_script_instance()->get_rpc_config(), false, cache);
+ }
+ rpc_cache[oid] = cache;
+ return rpc_cache[oid];
+}
+
+_FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) {
+ switch (mode) {
+ case MultiplayerAPI::RPC_MODE_DISABLED: {
+ return false;
+ } break;
+ case MultiplayerAPI::RPC_MODE_ANY_PEER: {
+ return true;
+ } break;
+ case MultiplayerAPI::RPC_MODE_AUTHORITY: {
+ return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority();
+ } break;
+ }
+
+ return false;
+}
+
+String SceneRPCInterface::get_rpc_md5(const Object *p_obj) {
+ const Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, "");
+ const RPCConfigCache cache = _get_node_config(node);
+ String rpc_list;
+ for (const KeyValue<uint16_t, RPCConfig> &config : cache.configs) {
+ rpc_list += String(config.value.name);
+ }
+ return rpc_list.md5_text();
+}
+
+Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND_V(!root_node, nullptr);
+ Node *node = nullptr;
+
+ if (p_node_target & 0x80000000) {
+ // Use full path (not cached yet).
+ int ofs = p_node_target & 0x7FFFFFFF;
+
+ ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
+
+ NodePath np = paths;
+
+ node = root_node->get_node(np);
+
+ if (!node) {
+ ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
+ }
+ return node;
+ } else {
+ // Use cached path.
+ return Object::cast_to<Node>(multiplayer->get_path_cache()->get_cached_object(p_from, p_node_target));
+ }
+}
+
+void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ // Extract packet meta
+ int packet_min_size = 1;
+ int name_id_offset = 1;
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+ // Compute the meta size, which depends on the compression level.
+ int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT;
+ int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT;
+
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ name_id_offset += 1;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ name_id_offset += 2;
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ packet_min_size += 4;
+ name_id_offset += 4;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the node id compression mode.");
+ }
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ packet_min_size += 1;
+ break;
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ packet_min_size += 2;
+ break;
+ default:
+ ERR_FAIL_MSG("Was not possible to extract the name id compression mode.");
+ }
+ ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
+
+ uint32_t node_target = 0;
+ switch (node_id_compression) {
+ case NETWORK_NODE_ID_COMPRESSION_8:
+ node_target = p_packet[1];
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_16:
+ node_target = decode_uint16(p_packet + 1);
+ break;
+ case NETWORK_NODE_ID_COMPRESSION_32:
+ node_target = decode_uint32(p_packet + 1);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
+ }
+
+ Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
+ ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found.");
+
+ uint16_t name_id = 0;
+ switch (name_id_compression) {
+ case NETWORK_NAME_ID_COMPRESSION_8:
+ name_id = p_packet[name_id_offset];
+ break;
+ case NETWORK_NAME_ID_COMPRESSION_16:
+ name_id = decode_uint16(p_packet + name_id_offset);
+ break;
+ default:
+ // Unreachable, checked before.
+ CRASH_NOW();
+ }
+
+ const int packet_len = get_packet_len(node_target, p_packet_len);
+ _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
+}
+
+void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+ ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
+
+ // Check that remote can call the RPC on this node.
+ const RPCConfigCache &cache_config = _get_node_config(p_node);
+ ERR_FAIL_COND(!cache_config.configs.has(p_rpc_method_id));
+ const RPCConfig &config = cache_config.configs[p_rpc_method_id];
+
+ bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from);
+ ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
+
+ int argc = 0;
+
+ const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
+ if (byte_only_or_no_args) {
+ if (p_offset < p_packet_len) {
+ // This packet contains only bytes.
+ argc = 1;
+ }
+ } else {
+ // Normal variant, takes the argument count from the packet.
+ ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
+ argc = p_packet[p_offset];
+ p_offset += 1;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+ argp.resize(argc);
+
+#ifdef DEBUG_ENABLED
+ _profile_node_data("rpc_in", p_node->get_instance_id());
+#endif
+
+ int out;
+ MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ for (int i = 0; i < argc; i++) {
+ argp.write[i] = &args[i];
+ }
+
+ Callable::CallError ce;
+
+ p_node->callp(config.name, (const Variant **)argp.ptr(), argc, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce);
+ error = "RPC - " + error;
+ ERR_PRINT(error);
+ }
+}
+
+void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer.");
+
+ ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet.");
+
+ ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected.");
+
+ ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255).");
+
+ if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) {
+ ERR_FAIL_COND_MSG(p_to == multiplayer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(multiplayer->get_unique_id()) + ".");
+
+ ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
+ }
+
+ // See if all peers have cached path (if so, call can be fast).
+ int psc_id;
+ const bool has_all_peers = multiplayer->get_path_cache()->send_object_cache(p_from, p_to, psc_id);
+
+ // Create base packet, lots of hardcode because it must be tight.
+
+ int ofs = 0;
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) \
+ packet_cache.resize(m_amount);
+
+ // Encode meta.
+ uint8_t command_type = SceneMultiplayer::NETWORK_COMMAND_REMOTE_CALL;
+ uint8_t node_id_compression = UINT8_MAX;
+ uint8_t name_id_compression = UINT8_MAX;
+ bool byte_only_or_no_args = false;
+
+ MAKE_ROOM(1);
+ // The meta is composed along the way, so just set 0 for now.
+ packet_cache.write[0] = 0;
+ ofs += 1;
+
+ // Encode Node ID.
+ if (has_all_peers) {
+ // Compress the node ID only if all the target peers already know it.
+ if (psc_id >= 0 && psc_id <= 255) {
+ // We can encode the id in 1 byte
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(psc_id);
+ ofs += 1;
+ } else if (psc_id >= 0 && psc_id <= 65535) {
+ // We can encode the id in 2 bytes
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs]));
+ ofs += 2;
+ } else {
+ // Too big, let's use 4 bytes.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc_id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
+ } else {
+ // The targets don't know the node yet, so we need to use 32 bits int.
+ node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc_id, &(packet_cache.write[ofs]));
+ ofs += 4;
+ }
+
+ // Encode method ID
+ if (p_rpc_id <= UINT8_MAX) {
+ // The ID fits in 1 byte
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_8;
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id);
+ ofs += 1;
+ } else {
+ // The ID is larger, let's use 2 bytes
+ name_id_compression = NETWORK_NAME_ID_COMPRESSION_16;
+ MAKE_ROOM(ofs + 2);
+ encode_uint16(p_rpc_id, &(packet_cache.write[ofs]));
+ ofs += 2;
+ }
+
+ int len;
+ Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!");
+ if (byte_only_or_no_args) {
+ MAKE_ROOM(ofs + len);
+ } else {
+ MAKE_ROOM(ofs + 1 + len);
+ packet_cache.write[ofs] = p_argcount;
+ ofs += 1;
+ }
+ if (len) {
+ MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ofs += len;
+ }
+
+ ERR_FAIL_COND(command_type > 7);
+ ERR_FAIL_COND(node_id_compression > 3);
+ ERR_FAIL_COND(name_id_compression > 1);
+
+ // We can now set the meta
+ packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0);
+
+#ifdef DEBUG_ENABLED
+ multiplayer->profile_bandwidth("out", ofs);
+#endif
+
+ // Take chance and set transfer mode, since all send methods will use it.
+ peer->set_transfer_channel(p_config.channel);
+ peer->set_transfer_mode(p_config.transfer_mode);
+
+ if (has_all_peers) {
+ // They all have verified paths, so send fast.
+ peer->set_target_peer(p_to); // To all of you.
+ peer->put_packet(packet_cache.ptr(), ofs); // A message with love.
+ } else {
+ // Unreachable because the node ID is never compressed if the peers doesn't know it.
+ CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32);
+
+ // Not all verified path, so send one by one.
+
+ // Append path at the end, since we will need it for some packets.
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
+ CharString pname = String(from_path).utf8();
+ int path_len = encode_cstring(pname.get_data(), nullptr);
+ MAKE_ROOM(ofs + path_len);
+ encode_cstring(pname.get_data(), &(packet_cache.write[ofs]));
+
+ for (const int &P : multiplayer->get_connected_peers()) {
+ if (p_to < 0 && P == -p_to) {
+ continue; // Continue, excluded.
+ }
+
+ if (p_to > 0 && P != p_to) {
+ continue; // Continue, not for this peer.
+ }
+
+ bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P);
+
+ peer->set_target_peer(P); // To this one specifically.
+
+ if (confirmed) {
+ // This one confirmed path, so use id.
+ encode_uint32(psc_id, &(packet_cache.write[1]));
+ peer->put_packet(packet_cache.ptr(), ofs);
+ } else {
+ // This one did not confirm path yet, so use entire path (sorry!).
+ encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag.
+ peer->put_packet(packet_cache.ptr(), ofs + path_len);
+ }
+ }
+ }
+}
+
+Error SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_V_MSG(!peer.is_valid(), ERR_UNCONFIGURED, "Trying to call an RPC while no multiplayer peer is active.");
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V_MSG(!node || !node->is_inside_tree(), ERR_INVALID_PARAMETER, "The object must be a valid Node inside the SceneTree");
+ ERR_FAIL_COND_V_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_CONNECTION_ERROR, "Trying to call an RPC via a multiplayer peer which is not connected.");
+
+ int caller_id = multiplayer->get_unique_id();
+ bool call_local_native = false;
+ bool call_local_script = false;
+ const RPCConfigCache &config_cache = _get_node_config(node);
+ uint16_t rpc_id = config_cache.ids.has(p_method) ? config_cache.ids[p_method] : UINT16_MAX;
+ ERR_FAIL_COND_V_MSG(rpc_id == UINT16_MAX, ERR_INVALID_PARAMETER,
+ vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is missing or not marked for RPCs in the local script.", p_method, node->get_path()));
+ const RPCConfig &config = config_cache.configs[rpc_id];
+
+ ERR_FAIL_COND_V_MSG(p_peer_id == caller_id && !config.call_local, ERR_INVALID_PARAMETER, "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
+
+ if (p_peer_id == 0 || p_peer_id == caller_id || (p_peer_id < 0 && p_peer_id != -caller_id)) {
+ if (rpc_id & (1 << 15)) {
+ call_local_native = config.call_local;
+ } else {
+ call_local_script = config.call_local;
+ }
+ }
+
+ if (p_peer_id != caller_id) {
+#ifdef DEBUG_ENABLED
+ _profile_node_data("rpc_out", node->get_instance_id());
+#endif
+
+ _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
+ }
+
+ if (call_local_native) {
+ Callable::CallError ce;
+
+ multiplayer->set_remote_sender_override(multiplayer->get_unique_id());
+ node->callp(p_method, p_arg, p_argcount, ce);
+ multiplayer->set_remote_sender_override(0);
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in local call: - " + error + ".";
+ ERR_PRINT(error);
+ return FAILED;
+ }
+ }
+
+ if (call_local_script) {
+ Callable::CallError ce;
+ ce.error = Callable::CallError::CALL_OK;
+
+ multiplayer->set_remote_sender_override(multiplayer->get_unique_id());
+ node->get_script_instance()->callp(p_method, p_arg, p_argcount, ce);
+ multiplayer->set_remote_sender_override(0);
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in script local call: - " + error + ".";
+ ERR_PRINT(error);
+ return FAILED;
+ }
+ }
+ return OK;
+}
diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h
new file mode 100644
index 0000000000..aa9be525a2
--- /dev/null
+++ b/modules/multiplayer/scene_rpc_interface.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* scene_rpc_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_RPC_INTERFACE_H
+#define SCENE_RPC_INTERFACE_H
+
+#include "core/object/ref_counted.h"
+#include "scene/main/multiplayer_api.h"
+
+class SceneMultiplayer;
+class Node;
+
+class SceneRPCInterface : public RefCounted {
+ GDCLASS(SceneRPCInterface, RefCounted);
+
+private:
+ struct RPCConfig {
+ StringName name;
+ MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ bool call_local = false;
+ MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
+ int channel = 0;
+
+ bool operator==(RPCConfig const &p_other) const {
+ return name == p_other.name;
+ }
+ };
+
+ struct RPCConfigCache {
+ HashMap<uint16_t, RPCConfig> configs;
+ HashMap<StringName, uint16_t> ids;
+ };
+
+ struct SortRPCConfig {
+ StringName::AlphCompare compare;
+ bool operator()(const RPCConfig &p_a, const RPCConfig &p_b) const {
+ return compare(p_a.name, p_b.name);
+ }
+ };
+
+ enum NetworkNodeIdCompression {
+ NETWORK_NODE_ID_COMPRESSION_8 = 0,
+ NETWORK_NODE_ID_COMPRESSION_16,
+ NETWORK_NODE_ID_COMPRESSION_32,
+ };
+
+ enum NetworkNameIdCompression {
+ NETWORK_NAME_ID_COMPRESSION_8 = 0,
+ NETWORK_NAME_ID_COMPRESSION_16,
+ };
+
+ SceneMultiplayer *multiplayer = nullptr;
+ Vector<uint8_t> packet_cache;
+
+ HashMap<ObjectID, RPCConfigCache> rpc_cache;
+
+protected:
+ _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
+ void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+
+ void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
+ Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
+
+ void _parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache);
+ const RPCConfigCache &_get_node_config(const Node *p_node);
+
+public:
+ Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len);
+ String get_rpc_md5(const Object *p_obj);
+
+ SceneRPCInterface(SceneMultiplayer *p_multiplayer) { multiplayer = p_multiplayer; }
+};
+
+#endif // SCENE_RPC_INTERFACE_H
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index d16d41b438..2cdb5b7cb4 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -123,6 +123,18 @@ void GodotNavigationServer::add_command(SetCommand *command) const {
}
}
+Array GodotNavigationServer::get_maps() const {
+ Array all_map_rids;
+ List<RID> maps_owned;
+ map_owner.get_owned_list(&maps_owned);
+ if (maps_owned.size()) {
+ for (const RID &E : maps_owned) {
+ all_map_rids.push_back(E);
+ }
+ }
+ return all_map_rids;
+}
+
RID GodotNavigationServer::map_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
@@ -198,11 +210,11 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
return map->get_edge_connection_margin();
}
-Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
- return map->get_path(p_origin, p_destination, p_optimize, p_layers);
+ return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers);
}
Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@@ -233,6 +245,48 @@ RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3
return map->get_closest_point_owner(p_point);
}
+Array GodotNavigationServer::map_get_regions(RID p_map) const {
+ Array regions_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, regions_rids);
+ const LocalVector<NavRegion *> regions = map->get_regions();
+ regions_rids.resize(regions.size());
+ for (uint32_t i = 0; i < regions.size(); i++) {
+ regions_rids[i] = regions[i]->get_self();
+ }
+ return regions_rids;
+}
+
+Array GodotNavigationServer::map_get_agents(RID p_map) const {
+ Array agents_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, agents_rids);
+ const LocalVector<RvoAgent *> agents = map->get_agents();
+ agents_rids.resize(agents.size());
+ for (uint32_t i = 0; i < agents.size(); i++) {
+ agents_rids[i] = agents[i]->get_self();
+ }
+ return agents_rids;
+}
+
+RID GodotNavigationServer::region_get_map(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, RID());
+ if (region->get_map()) {
+ return region->get_map()->get_self();
+ }
+ return RID();
+}
+
+RID GodotNavigationServer::agent_get_map(RID p_agent) const {
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, RID());
+ if (agent->get_map()) {
+ return agent->get_map()->get_self();
+ }
+ return RID();
+}
+
RID GodotNavigationServer::region_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
@@ -271,18 +325,58 @@ COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
region->set_transform(p_transform);
}
-COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers) {
+COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_enter_cost < 0.0);
- region->set_layers(p_layers);
+ region->set_enter_cost(p_enter_cost);
}
-uint32_t GodotNavigationServer::region_get_layers(RID p_region) const {
+real_t GodotNavigationServer::region_get_enter_cost(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
- return region->get_layers();
+ return region->get_enter_cost();
+}
+
+COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_travel_cost < 0.0);
+
+ region->set_travel_cost(p_travel_cost);
+}
+
+real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_travel_cost();
+}
+
+bool GodotNavigationServer::region_owns_point(RID p_region, const Vector3 &p_point) const {
+ const NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, false);
+ if (region->get_map()) {
+ RID closest_point_owner = map_get_closest_point_owner(region->get_map()->get_self(), p_point);
+ return closest_point_owner == region->get_self();
+ }
+ return false;
+}
+
+COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+
+ region->set_navigation_layers(p_navigation_layers);
+}
+
+uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_navigation_layers();
}
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
@@ -449,15 +543,15 @@ COMMAND_1(free, RID, p_object) {
NavMap *map = map_owner.get_or_null(p_object);
// Removes any assigned region
- std::vector<NavRegion *> regions = map->get_regions();
- for (size_t i(0); i < regions.size(); i++) {
+ LocalVector<NavRegion *> regions = map->get_regions();
+ for (uint32_t i = 0; i < regions.size(); i++) {
map->remove_region(regions[i]);
regions[i]->set_map(nullptr);
}
// Remove any assigned agent
- std::vector<RvoAgent *> agents = map->get_agents();
- for (size_t i(0); i < agents.size(); i++) {
+ LocalVector<RvoAgent *> agents = map->get_agents();
+ for (uint32_t i = 0; i < agents.size(); i++) {
map->remove_agent(agents[i]);
agents[i]->set_map(nullptr);
}
@@ -512,6 +606,15 @@ void GodotNavigationServer::flush_queries() {
commands.clear();
}
+void GodotNavigationServer::map_force_update(RID p_map) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ flush_queries();
+
+ map->sync();
+}
+
void GodotNavigationServer::process(real_t p_delta_time) {
flush_queries();
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 7ad5e2d501..da1f8cba0b 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -69,7 +69,7 @@ class GodotNavigationServer : public NavigationServer3D {
/// Mutex used to make any operation threadsafe.
Mutex operations_mutex;
- std::vector<SetCommand *> commands;
+ LocalVector<SetCommand *> commands;
mutable RID_Owner<NavMap> map_owner;
mutable RID_Owner<NavRegion> region_owner;
@@ -85,6 +85,8 @@ public:
void add_command(SetCommand *command) const;
+ virtual Array get_maps() const override;
+
virtual RID map_create() const override;
COMMAND_2(map_set_active, RID, p_map, bool, p_active);
virtual bool map_is_active(RID p_map) const override;
@@ -98,17 +100,31 @@ public:
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const override;
- virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const override;
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override;
virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override;
virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override;
+ virtual Array map_get_regions(RID p_map) const override;
+ virtual Array map_get_agents(RID p_map) const override;
+
+ virtual void map_force_update(RID p_map) override;
+
virtual RID region_create() const override;
+
+ COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost);
+ virtual real_t region_get_enter_cost(RID p_region) const override;
+ COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost);
+ virtual real_t region_get_travel_cost(RID p_region) const override;
+
+ virtual bool region_owns_point(RID p_region, const Vector3 &p_point) const override;
+
COMMAND_2(region_set_map, RID, p_region, RID, p_map);
- COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers);
- virtual uint32_t region_get_layers(RID p_region) const override;
+ virtual RID region_get_map(RID p_region) const override;
+ COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers);
+ virtual uint32_t region_get_navigation_layers(RID p_region) const override;
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const override;
@@ -118,6 +134,7 @@ public:
virtual RID agent_create() const override;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
+ virtual RID agent_get_map(RID p_agent) const override;
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index cbc0adc574..49029b5513 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -30,9 +30,9 @@
#include "nav_map.h"
+#include "core/object/worker_thread_pool.h"
#include "nav_region.h"
#include "rvo_agent.h"
-
#include <algorithm>
#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
@@ -65,7 +65,7 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
return p;
}
-Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
// Find the start poly and the end poly on this map.
const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
@@ -78,7 +78,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Polygon &p = polygons[i];
// Only consider the polygon if it in a region with compatible layers.
- if ((p_layers & p.owner->get_layers()) == 0) {
+ if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) {
continue;
}
@@ -117,7 +117,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
}
// List of all reachable navigation polys.
- std::vector<gd::NavigationPoly> navigation_polys;
+ LocalVector<gd::NavigationPoly> navigation_polys;
navigation_polys.reserve(polygons.size() * 0.75);
// Add the start polygon to the reachable navigation polygons.
@@ -140,6 +140,8 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
float reachable_d = 1e30;
bool is_reachable = true;
+ gd::NavigationPoly *prev_least_cost_poly = nullptr;
+
while (true) {
// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
@@ -152,28 +154,34 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Edge::Connection &connection = edge.connections[connection_index];
// Only consider the connection to another polygon if this polygon is in a region with compatible layers.
- if ((p_layers & connection.polygon->owner->get_layers()) == 0) {
+ if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) {
continue;
}
+ float region_enter_cost = 0.0;
+ float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost();
+
+ if (prev_least_cost_poly != nullptr && !(prev_least_cost_poly->poly->owner->get_self() == least_cost_poly->poly->owner->get_self())) {
+ region_enter_cost = least_cost_poly->poly->owner->get_enter_cost();
+ }
+ prev_least_cost_poly = least_cost_poly;
+
Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway);
- const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
+ const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance;
- const std::vector<gd::NavigationPoly>::iterator it = std::find(
- navigation_polys.begin(),
- navigation_polys.end(),
- gd::NavigationPoly(connection.polygon));
+ int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon));
- if (it != navigation_polys.end()) {
+ if (already_visited_polygon_index != -1) {
// Polygon already visited, check if we can reduce the travel cost.
- if (new_distance < it->traveled_distance) {
- it->back_navigation_poly_id = least_cost_id;
- it->back_navigation_edge = connection.edge;
- it->back_navigation_edge_pathway_start = connection.pathway_start;
- it->back_navigation_edge_pathway_end = connection.pathway_end;
- it->traveled_distance = new_distance;
- it->entry = new_entry;
+ gd::NavigationPoly &avp = navigation_polys[already_visited_polygon_index];
+ if (new_distance < avp.traveled_distance) {
+ avp.back_navigation_poly_id = least_cost_id;
+ avp.back_navigation_edge = connection.edge;
+ avp.back_navigation_edge_pathway_start = connection.pathway_start;
+ avp.back_navigation_edge_pathway_end = connection.pathway_end;
+ avp.traveled_distance = new_distance;
+ avp.entry = new_entry;
}
} else {
// Add the neighbour polygon to the reachable ones.
@@ -238,7 +246,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
gd::NavigationPoly *np = &navigation_polys[element->get()];
float cost = np->traveled_distance;
- cost += np->entry.distance_to(end_point);
+ cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost());
if (cost < least_cost) {
least_cost_id = np->self_id;
least_cost = cost;
@@ -249,7 +257,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Stores the further reachable end polygon, in case our goal is not reachable.
if (is_reachable) {
- float d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
+ float d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost();
if (reachable_d > d) {
reachable_d = d;
reachable_end = navigation_polys[least_cost_id].poly;
@@ -460,15 +468,15 @@ void NavMap::add_region(NavRegion *p_region) {
}
void NavMap::remove_region(NavRegion *p_region) {
- const std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region);
- if (it != regions.end()) {
- regions.erase(it);
+ int64_t region_index = regions.find(p_region);
+ if (region_index != -1) {
+ regions.remove_at_unordered(region_index);
regenerate_links = true;
}
}
bool NavMap::has_agent(RvoAgent *agent) const {
- return std::find(agents.begin(), agents.end(), agent) != agents.end();
+ return (agents.find(agent) != -1);
}
void NavMap::add_agent(RvoAgent *agent) {
@@ -480,15 +488,15 @@ void NavMap::add_agent(RvoAgent *agent) {
void NavMap::remove_agent(RvoAgent *agent) {
remove_agent_as_controlled(agent);
- const std::vector<RvoAgent *>::iterator it = std::find(agents.begin(), agents.end(), agent);
- if (it != agents.end()) {
- agents.erase(it);
+ int64_t agent_index = agents.find(agent);
+ if (agent_index != -1) {
+ agents.remove_at_unordered(agent_index);
agents_dirty = true;
}
}
void NavMap::set_agent_as_controlled(RvoAgent *agent) {
- const bool exist = std::find(controlled_agents.begin(), controlled_agents.end(), agent) != controlled_agents.end();
+ const bool exist = (controlled_agents.find(agent) != -1);
if (!exist) {
ERR_FAIL_COND(!has_agent(agent));
controlled_agents.push_back(agent);
@@ -496,22 +504,23 @@ void NavMap::set_agent_as_controlled(RvoAgent *agent) {
}
void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
- const std::vector<RvoAgent *>::iterator it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
- if (it != controlled_agents.end()) {
- controlled_agents.erase(it);
+ int64_t active_avoidance_agent_index = controlled_agents.find(agent);
+ if (active_avoidance_agent_index != -1) {
+ controlled_agents.remove_at_unordered(active_avoidance_agent_index);
+ agents_dirty = true;
}
}
void NavMap::sync() {
// Check if we need to update the links.
if (regenerate_polygons) {
- for (size_t r(0); r < regions.size(); r++) {
+ for (uint32_t r = 0; r < regions.size(); r++) {
regions[r]->scratch_polygons();
}
regenerate_links = true;
}
- for (size_t r(0); r < regions.size(); r++) {
+ for (uint32_t r = 0; r < regions.size(); r++) {
if (regions[r]->sync()) {
regenerate_links = true;
}
@@ -519,37 +528,37 @@ void NavMap::sync() {
if (regenerate_links) {
// Remove regions connections.
- for (size_t r(0); r < regions.size(); r++) {
+ for (uint32_t r = 0; r < regions.size(); r++) {
regions[r]->get_connections().clear();
}
// Resize the polygon count.
int count = 0;
- for (size_t r(0); r < regions.size(); r++) {
+ for (uint32_t r = 0; r < regions.size(); r++) {
count += regions[r]->get_polygons().size();
}
polygons.resize(count);
// Copy all region polygons in the map.
count = 0;
- for (size_t r(0); r < regions.size(); r++) {
- std::copy(
- regions[r]->get_polygons().data(),
- regions[r]->get_polygons().data() + regions[r]->get_polygons().size(),
- polygons.begin() + count);
+ for (uint32_t r = 0; r < regions.size(); r++) {
+ const LocalVector<gd::Polygon> &polygons_source = regions[r]->get_polygons();
+ for (uint32_t n = 0; n < polygons_source.size(); n++) {
+ polygons[count + n] = polygons_source[n];
+ }
count += regions[r]->get_polygons().size();
}
// Group all edges per key.
- Map<gd::EdgeKey, Vector<gd::Edge::Connection>> connections;
- for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
+ HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
+ for (uint32_t poly_id = 0; poly_id < polygons.size(); poly_id++) {
gd::Polygon &poly(polygons[poly_id]);
- for (size_t p(0); p < poly.points.size(); p++) {
+ for (uint32_t p = 0; p < poly.points.size(); p++) {
int next_point = (p + 1) % poly.points.size();
gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
- Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *connection = connections.find(ek);
+ HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
if (!connection) {
connections[ek] = Vector<gd::Edge::Connection>();
}
@@ -563,7 +572,7 @@ void NavMap::sync() {
connections[ek].push_back(new_connection);
} else {
// The edge is already connected with another edge, skip.
- ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
+ ERR_PRINT_ONCE("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problems.");
}
}
}
@@ -652,6 +661,7 @@ void NavMap::sync() {
// Update agents tree.
if (agents_dirty) {
+ // cannot use LocalVector here as RVO library expects std::vector to build KdTree
std::vector<RVO::Agent *> raw_agents;
raw_agents.reserve(agents.size());
for (size_t i(0); i < agents.size(); i++) {
@@ -673,14 +683,8 @@ void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
void NavMap::step(real_t p_deltatime) {
deltatime = p_deltatime;
if (controlled_agents.size() > 0) {
- if (step_work_pool.get_thread_count() == 0) {
- step_work_pool.init();
- }
- step_work_pool.do_work(
- controlled_agents.size(),
- this,
- &NavMap::compute_single_step,
- controlled_agents.data());
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &NavMap::compute_single_step, controlled_agents.ptr(), controlled_agents.size(), -1, true, SNAME("NavigationMapAgents"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
}
}
@@ -690,7 +694,7 @@ void NavMap::dispatch_callbacks() {
}
}
-void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
+void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
Vector3 from = path[path.size() - 1];
if (from.is_equal_approx(p_to_point)) {
@@ -726,5 +730,4 @@ NavMap::NavMap() {
}
NavMap::~NavMap() {
- step_work_pool.finish();
}
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index 5232e42bed..e50a1afbe9 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -28,14 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RVO_SPACE_H
-#define RVO_SPACE_H
+#ifndef NAV_MAP_H
+#define NAV_MAP_H
#include "nav_rid.h"
#include "core/math/math_defs.h"
-#include "core/templates/map.h"
-#include "core/templates/thread_work_pool.h"
+#include "core/object/worker_thread_pool.h"
+#include "core/templates/rb_map.h"
#include "nav_utils.h"
#include <KdTree.h>
@@ -50,18 +50,18 @@ class NavMap : public NavRid {
/// To find the polygons edges the vertices are displaced in a grid where
/// each cell has the following cell_size.
- real_t cell_size = 0.3;
+ real_t cell_size = 0.25;
/// This value is used to detect the near edges to connect.
- real_t edge_connection_margin = 5.0;
+ real_t edge_connection_margin = 0.25;
bool regenerate_polygons = true;
bool regenerate_links = true;
- std::vector<NavRegion *> regions;
+ LocalVector<NavRegion *> regions;
/// Map polygons
- std::vector<gd::Polygon> polygons;
+ LocalVector<gd::Polygon> polygons;
/// Rvo world
RVO::KdTree rvo;
@@ -70,10 +70,10 @@ class NavMap : public NavRid {
bool agents_dirty = false;
/// All the Agents (even the controlled one)
- std::vector<RvoAgent *> agents;
+ LocalVector<RvoAgent *> agents;
/// Controlled agents
- std::vector<RvoAgent *> controlled_agents;
+ LocalVector<RvoAgent *> controlled_agents;
/// Physics delta time
real_t deltatime = 0.0;
@@ -81,9 +81,6 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id = 0;
- /// Pooled threads for computing steps
- ThreadWorkPool step_work_pool;
-
public:
NavMap();
~NavMap();
@@ -105,7 +102,7 @@ public:
gd::PointKey get_point_key(const Vector3 &p_pos) const;
- Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const;
Vector3 get_closest_point(const Vector3 &p_point) const;
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
@@ -114,14 +111,14 @@ public:
void add_region(NavRegion *p_region);
void remove_region(NavRegion *p_region);
- const std::vector<NavRegion *> &get_regions() const {
+ const LocalVector<NavRegion *> &get_regions() const {
return regions;
}
bool has_agent(RvoAgent *agent) const;
void add_agent(RvoAgent *agent);
void remove_agent(RvoAgent *agent);
- const std::vector<RvoAgent *> &get_agents() const {
+ const LocalVector<RvoAgent *> &get_agents() const {
return agents;
}
@@ -138,7 +135,7 @@ public:
private:
void compute_single_step(uint32_t index, RvoAgent **agent);
- void clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
+ void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
};
-#endif // RVO_SPACE_H
+#endif // NAV_MAP_H
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index fea0ad519a..88740807eb 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -40,12 +40,12 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
-void NavRegion::set_layers(uint32_t p_layers) {
- layers = p_layers;
+void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
}
-uint32_t NavRegion::get_layers() const {
- return layers;
+uint32_t NavRegion::get_navigation_layers() const {
+ return navigation_layers;
}
void NavRegion::set_transform(Transform3D p_transform) {
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index 7a6da281c0..c9d2d80f6c 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -45,13 +45,15 @@ class NavRegion : public NavRid {
NavMap *map = nullptr;
Transform3D transform;
Ref<NavigationMesh> mesh;
- uint32_t layers = 1;
+ uint32_t navigation_layers = 1;
+ float enter_cost = 0.0;
+ float travel_cost = 1.0;
Vector<gd::Edge::Connection> connections;
bool polygons_dirty = true;
/// Cache
- std::vector<gd::Polygon> polygons;
+ LocalVector<gd::Polygon> polygons;
public:
NavRegion() {}
@@ -65,8 +67,14 @@ public:
return map;
}
- void set_layers(uint32_t p_layers);
- uint32_t get_layers() const;
+ void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); }
+ float get_enter_cost() const { return enter_cost; }
+
+ void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); }
+ float get_travel_cost() const { return travel_cost; }
+
+ void set_navigation_layers(uint32_t p_navigation_layers);
+ uint32_t get_navigation_layers() const;
void set_transform(Transform3D transform);
const Transform3D &get_transform() const {
@@ -85,7 +93,7 @@ public:
Vector3 get_connection_pathway_start(int p_connection_id) const;
Vector3 get_connection_pathway_end(int p_connection_id) const;
- std::vector<gd::Polygon> const &get_polygons() const {
+ LocalVector<gd::Polygon> const &get_polygons() const {
return polygons;
}
diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h
index 5b6c695ca4..47f04b6a75 100644
--- a/modules/navigation/nav_utils.h
+++ b/modules/navigation/nav_utils.h
@@ -32,8 +32,9 @@
#define NAV_UTILS_H
#include "core/math/vector3.h"
-#include "core/templates/vector.h"
-
+#include "core/templates/hash_map.h"
+#include "core/templates/hashfuncs.h"
+#include "core/templates/local_vector.h"
#include <vector>
class NavRegion;
@@ -49,15 +50,18 @@ union PointKey {
};
uint64_t key = 0;
- bool operator<(const PointKey &p_key) const { return key < p_key.key; }
};
struct EdgeKey {
PointKey a;
PointKey b;
- bool operator<(const EdgeKey &p_key) const {
- return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
+ static uint32_t hash(const EdgeKey &p_val) {
+ return hash_one_uint64(p_val.a.key) ^ hash_one_uint64(p_val.b.key);
+ }
+
+ bool operator==(const EdgeKey &p_key) const {
+ return (a.key == p_key.a.key) && (b.key == p_key.b.key);
}
EdgeKey(const PointKey &p_a = PointKey(), const PointKey &p_b = PointKey()) :
@@ -92,13 +96,13 @@ struct Polygon {
NavRegion *owner = nullptr;
/// The points of this `Polygon`
- std::vector<Point> points;
+ LocalVector<Point> points;
/// Are the points clockwise ?
bool clockwise;
/// The edges of this `Polygon`
- std::vector<Edge> edges;
+ LocalVector<Edge> edges;
/// The center of this `Polygon`
Vector3 center;
@@ -120,6 +124,8 @@ struct NavigationPoly {
/// The distance to the destination.
float traveled_distance = 0.0;
+ NavigationPoly() { poly = nullptr; }
+
NavigationPoly(const Polygon *p_poly) :
poly(p_poly) {}
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 9e2daf3a99..6e8ac77f79 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -34,7 +34,6 @@
#include "core/math/convex_hull.h"
#include "core/os/thread.h"
-#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/3d/physics_body_3d.h"
@@ -173,14 +172,16 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node);
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
- Ref<Mesh> mesh = multimesh->get_mesh();
- if (mesh.is_valid()) {
- int n = multimesh->get_visible_instance_count();
- if (n == -1) {
- n = multimesh->get_instance_count();
- }
- for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ if (multimesh.is_valid()) {
+ Ref<Mesh> mesh = multimesh->get_mesh();
+ if (mesh.is_valid()) {
+ int n = multimesh->get_visible_instance_count();
+ if (n == -1) {
+ n = multimesh->get_instance_count();
+ }
+ for (int i = 0; i < n; i++) {
+ _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ }
}
}
}
@@ -202,14 +203,17 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node);
if (static_body->get_collision_layer() & p_collision_mask) {
- for (int i = 0; i < p_node->get_child_count(); ++i) {
- Node *child = p_node->get_child(i);
- if (Object::cast_to<CollisionShape3D>(child)) {
- CollisionShape3D *col_shape = Object::cast_to<CollisionShape3D>(child);
-
- Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * col_shape->get_transform();
+ List<uint32_t> shape_owners;
+ static_body->get_shape_owners(&shape_owners);
+ for (uint32_t shape_owner : shape_owners) {
+ const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
+ for (int i = 0; i < shape_count; i++) {
+ Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i);
+ if (s.is_null()) {
+ continue;
+ }
- Ref<Shape3D> s = col_shape->get_shape();
+ const Transform3D transform = p_navmesh_transform * static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
if (box) {
@@ -443,9 +447,34 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
cfg.minRegionArea = (int)(p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size());
cfg.mergeRegionArea = (int)(p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size());
cfg.maxVertsPerPoly = (int)p_nav_mesh->get_verts_per_poly();
- cfg.detailSampleDist = p_nav_mesh->get_detail_sample_distance() < 0.9f ? 0 : p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance();
+ cfg.detailSampleDist = MAX(p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_nav_mesh->get_cell_height() * p_nav_mesh->get_detail_sample_max_error();
+ if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_nav_mesh->get_agent_height())) {
+ WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_nav_mesh->get_agent_max_climb())) {
+ WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_nav_mesh->get_agent_radius())) {
+ WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_nav_mesh->get_edge_max_length())) {
+ WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.minRegionArea, p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size())) {
+ WARN_PRINT("Property region_min_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size())) {
+ WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_nav_mesh->get_verts_per_poly())) {
+ WARN_PRINT("Property verts_per_poly is converted to int and loses precision.");
+ }
+ if (p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance() < 0.1f) {
+ WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
+ }
+
cfg.bmin[0] = bmin[0];
cfg.bmin[1] = bmin[1];
cfg.bmin[2] = bmin[2];
@@ -453,6 +482,21 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
cfg.bmax[1] = bmax[1];
cfg.bmax[2] = bmax[2];
+ AABB baking_aabb = p_nav_mesh->get_filter_baking_aabb();
+
+ bool aabb_has_no_volume = baking_aabb.has_no_volume();
+
+ if (!aabb_has_no_volume) {
+ Vector3 baking_aabb_offset = p_nav_mesh->get_filter_baking_aabb_offset();
+
+ cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x;
+ cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y;
+ cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z;
+ cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0];
+ cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1];
+ cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
+ }
+
#ifdef TOOLS_ENABLED
if (ep) {
ep->step(TTR("Calculating grid size..."), 2);
@@ -460,6 +504,14 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
#endif
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
+ // ~30000000 seems to be around sweetspot where Editor baking breaks
+ if ((cfg.width * cfg.height) > 30000000) {
+ WARN_PRINT("NavigationMesh baking process will likely fail."
+ "\nSource geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings."
+ "\nIf baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues."
+ "\nIt is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry.");
+ }
+
#ifdef TOOLS_ENABLED
if (ep) {
ep->step(TTR("Creating heightfield..."), 3);
@@ -594,6 +646,13 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
#ifdef TOOLS_ENABLED
EditorProgress *ep(nullptr);
+ // FIXME
+#endif
+#if 0
+ // After discussion on devchat disabled EditorProgress for now as it is not thread-safe and uses hacks and Main::iteration() for steps.
+ // EditorProgress randomly crashes the Engine when the bake function is used with a thread e.g. inside Editor with a tool script and procedural navigation
+ // This was not a problem in older versions as previously Godot was unable to (re)bake NavigationMesh at runtime.
+ // If EditorProgress is fixed and made thread-safe this should be enabled again.
if (Engine::get_singleton()->is_editor_hint()) {
ep = memnew(EditorProgress("bake", TTR("Navigation Mesh Generator Setup:"), 11));
}
diff --git a/modules/navigation/register_types.cpp b/modules/navigation/register_types.cpp
index 218f2c2937..62ae2c7f02 100644
--- a/modules/navigation/register_types.cpp
+++ b/modules/navigation/register_types.cpp
@@ -51,21 +51,29 @@ NavigationServer3D *new_server() {
return memnew(GodotNavigationServer);
}
-void register_navigation_types() {
- NavigationServer3DManager::set_default_server(new_server);
+void initialize_navigation_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ NavigationServer3DManager::set_default_server(new_server);
#ifndef _3D_DISABLED
- _nav_mesh_generator = memnew(NavigationMeshGenerator);
- GDREGISTER_CLASS(NavigationMeshGenerator);
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
+ _nav_mesh_generator = memnew(NavigationMeshGenerator);
+ GDREGISTER_CLASS(NavigationMeshGenerator);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
#endif
+ }
#ifdef TOOLS_ENABLED
- EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
+ }
#endif
}
-void unregister_navigation_types() {
+void uninitialize_navigation_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ return;
+ }
+
#ifndef _3D_DISABLED
if (_nav_mesh_generator) {
memdelete(_nav_mesh_generator);
diff --git a/modules/navigation/register_types.h b/modules/navigation/register_types.h
index 11fa5769d7..c4dbd19ed3 100644
--- a/modules/navigation/register_types.h
+++ b/modules/navigation/register_types.h
@@ -31,7 +31,9 @@
#ifndef NAVIGATION_REGISTER_TYPES_H
#define NAVIGATION_REGISTER_TYPES_H
-void register_navigation_types();
-void unregister_navigation_types();
+#include "modules/register_module_types.h"
+
+void initialize_navigation_module(ModuleInitializationLevel p_level);
+void uninitialize_navigation_module(ModuleInitializationLevel p_level);
#endif // NAVIGATION_REGISTER_TYPES_H
diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp
index a6a5660c0c..4ec72ad43f 100644
--- a/modules/navigation/rvo_agent.cpp
+++ b/modules/navigation/rvo_agent.cpp
@@ -65,8 +65,9 @@ void RvoAgent::dispatch_callback() {
return;
}
Object *obj = ObjectDB::get_instance(callback.id);
- if (obj == nullptr) {
+ if (!obj) {
callback.id = ObjectID();
+ return;
}
Callable::CallError responseCallError;
diff --git a/modules/noise/editor/noise_editor_plugin.cpp b/modules/noise/editor/noise_editor_plugin.cpp
index 32c3f0aad4..27a86f45b5 100644
--- a/modules/noise/editor/noise_editor_plugin.cpp
+++ b/modules/noise/editor/noise_editor_plugin.cpp
@@ -54,7 +54,7 @@ public:
set_custom_minimum_size(Size2(0, EDSCALE * PREVIEW_HEIGHT));
_texture_rect = memnew(TextureRect);
- _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_COVERED);
add_child(_texture_rect);
diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp
index a8d38dee62..b21e3247d7 100644
--- a/modules/noise/fastnoise_lite.cpp
+++ b/modules/noise/fastnoise_lite.cpp
@@ -30,6 +30,24 @@
#include "fastnoise_lite.h"
+_FastNoiseLite::FractalType FastNoiseLite::_convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type) {
+ _FastNoiseLite::FractalType type;
+ switch (p_domain_warp_fractal_type) {
+ case DOMAIN_WARP_FRACTAL_NONE:
+ type = _FastNoiseLite::FractalType_None;
+ break;
+ case DOMAIN_WARP_FRACTAL_PROGRESSIVE:
+ type = _FastNoiseLite::FractalType_DomainWarpProgressive;
+ break;
+ case DOMAIN_WARP_FRACTAL_INDEPENDENT:
+ type = _FastNoiseLite::FractalType_DomainWarpIndependent;
+ break;
+ default:
+ type = _FastNoiseLite::FractalType_None;
+ }
+ return type;
+}
+
FastNoiseLite::FastNoiseLite() {
_noise.SetNoiseType((_FastNoiseLite::NoiseType)noise_type);
_noise.SetSeed(seed);
@@ -50,7 +68,7 @@ FastNoiseLite::FastNoiseLite() {
_domain_warp_noise.SetSeed(seed);
_domain_warp_noise.SetDomainWarpAmp(domain_warp_amplitude);
_domain_warp_noise.SetFrequency(domain_warp_frequency);
- _domain_warp_noise.SetFractalType(_FastNoiseLite::FractalType_None);
+ _domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(domain_warp_fractal_type));
_domain_warp_noise.SetFractalOctaves(domain_warp_fractal_octaves);
_domain_warp_noise.SetFractalLacunarity(domain_warp_fractal_lacunarity);
_domain_warp_noise.SetFractalGain(domain_warp_fractal_gain);
@@ -241,23 +259,7 @@ real_t FastNoiseLite::get_domain_warp_frequency() const {
void FastNoiseLite::set_domain_warp_fractal_type(DomainWarpFractalType p_domain_warp_fractal_type) {
domain_warp_fractal_type = p_domain_warp_fractal_type;
- // This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices.
- _FastNoiseLite::FractalType type;
- switch (p_domain_warp_fractal_type) {
- case DOMAIN_WARP_FRACTAL_NONE:
- type = _FastNoiseLite::FractalType_None;
- break;
- case DOMAIN_WARP_FRACTAL_PROGRESSIVE:
- type = _FastNoiseLite::FractalType_DomainWarpProgressive;
- break;
- case DOMAIN_WARP_FRACTAL_INDEPENDENT:
- type = _FastNoiseLite::FractalType_DomainWarpIndependent;
- break;
- default:
- type = _FastNoiseLite::FractalType_None;
- }
-
- _domain_warp_noise.SetFractalType(type);
+ _domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(p_domain_warp_fractal_type));
emit_changed();
}
@@ -298,6 +300,12 @@ real_t FastNoiseLite::get_domain_warp_fractal_gain() const {
// Noise interface functions.
real_t FastNoiseLite::get_noise_1d(real_t p_x) const {
+ p_x += offset.x;
+ if (domain_warp_enabled) {
+ // Needed since DomainWarp expects a reference.
+ real_t y_dummy = 0;
+ _domain_warp_noise.DomainWarp(p_x, y_dummy);
+ }
return get_noise_2d(p_x, 0.0);
}
@@ -306,10 +314,12 @@ real_t FastNoiseLite::get_noise_2dv(Vector2 p_v) const {
}
real_t FastNoiseLite::get_noise_2d(real_t p_x, real_t p_y) const {
+ p_x += offset.x;
+ p_y += offset.y;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y);
}
- return _noise.GetNoise(p_x + offset.x, p_y + offset.y);
+ return _noise.GetNoise(p_x, p_y);
}
real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const {
@@ -317,10 +327,13 @@ real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const {
}
real_t FastNoiseLite::get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const {
+ p_x += offset.x;
+ p_y += offset.y;
+ p_z += offset.z;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y, p_z);
}
- return _noise.GetNoise(p_x + offset.x, p_y + offset.y, p_z + offset.z);
+ return _noise.GetNoise(p_x, p_y, p_z);
}
void FastNoiseLite::_changed() {
diff --git a/modules/noise/fastnoise_lite.h b/modules/noise/fastnoise_lite.h
index 0a4251868b..fe8cd7ce6e 100644
--- a/modules/noise/fastnoise_lite.h
+++ b/modules/noise/fastnoise_lite.h
@@ -127,6 +127,9 @@ private:
real_t domain_warp_fractal_lacunarity = 6;
real_t domain_warp_fractal_gain = 0.5;
+ // This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices.
+ _FastNoiseLite::FractalType _convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type);
+
public:
FastNoiseLite();
~FastNoiseLite();
diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp
index ad3df0a016..d14b63f7c8 100644
--- a/modules/noise/noise.cpp
+++ b/modules/noise/noise.cpp
@@ -31,6 +31,8 @@
#include "noise.h"
Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt) const {
+ ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());
+
int skirt_width = p_width * p_blend_skirt;
int skirt_height = p_height * p_blend_skirt;
int src_width = p_width + skirt_width;
@@ -55,6 +57,8 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co
}
Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space) const {
+ ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());
+
Vector<uint8_t> data;
data.resize(p_width * p_height);
diff --git a/modules/noise/noise_texture.cpp b/modules/noise/noise_texture.cpp
index e9d16e548c..ca55d3b96d 100644
--- a/modules/noise/noise_texture.cpp
+++ b/modules/noise/noise_texture.cpp
@@ -81,8 +81,8 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "in_3d_space"), "set_in_3d_space", "is_in_3d_space");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "is_generating_mipmaps");
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index 3623da3bb9..d0cfc4e944 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -39,15 +39,22 @@
#include "editor/noise_editor_plugin.h"
#endif
-void register_noise_types() {
- GDREGISTER_CLASS(NoiseTexture);
- GDREGISTER_ABSTRACT_CLASS(Noise);
- GDREGISTER_CLASS(FastNoiseLite);
+void initialize_noise_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(NoiseTexture);
+ GDREGISTER_ABSTRACT_CLASS(Noise);
+ GDREGISTER_CLASS(FastNoiseLite);
+ }
#ifdef TOOLS_ENABLED
- EditorPlugins::add_by_type<NoiseEditorPlugin>();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<NoiseEditorPlugin>();
+ }
#endif
}
-void unregister_noise_types() {
+void uninitialize_noise_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/noise/register_types.h b/modules/noise/register_types.h
index e441a48518..dfe5a480de 100644
--- a/modules/noise/register_types.h
+++ b/modules/noise/register_types.h
@@ -31,7 +31,9 @@
#ifndef NOISE_REGISTER_TYPES_H
#define NOISE_REGISTER_TYPES_H
-void register_noise_types();
-void unregister_noise_types();
+#include "modules/register_module_types.h"
+
+void initialize_noise_module(ModuleInitializationLevel p_level);
+void uninitialize_noise_module(ModuleInitializationLevel p_level);
#endif // NOISE_REGISTER_TYPES_H
diff --git a/modules/ogg/config.py b/modules/ogg/config.py
index 5a417ba8dd..f7ec4de6be 100644
--- a/modules/ogg/config.py
+++ b/modules/ogg/config.py
@@ -8,8 +8,8 @@ def configure(env):
def get_doc_classes():
return [
- "OGGPacketSequence",
- "OGGPacketSequencePlayback",
+ "OggPacketSequence",
+ "OggPacketSequencePlayback",
]
diff --git a/modules/ogg/doc_classes/OGGPacketSequence.xml b/modules/ogg/doc_classes/OggPacketSequence.xml
index bff3691ce0..d3bd4455d8 100644
--- a/modules/ogg/doc_classes/OGGPacketSequence.xml
+++ b/modules/ogg/doc_classes/OggPacketSequence.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OGGPacketSequence" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OggPacketSequence" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A sequence of OGG packets.
+ A sequence of Ogg packets.
</brief_description>
<description>
- A sequence of OGG packets.
+ A sequence of Ogg packets.
</description>
<tutorials>
</tutorials>
@@ -21,7 +21,7 @@
Contains the granule positions for each page in this packet sequence.
</member>
<member name="packet_data" type="Array" setter="set_packet_data" getter="get_packet_data" default="[]">
- Contains the raw packets that make up this OGGPacketSequence.
+ Contains the raw packets that make up this OggPacketSequence.
</member>
<member name="sampling_rate" type="float" setter="set_sampling_rate" getter="get_sampling_rate" default="0.0">
Holds sample rate information about this sequence. Must be set by another class that actually understands the codec.
diff --git a/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml b/modules/ogg/doc_classes/OggPacketSequencePlayback.xml
index 11fc1f4cb6..5c0d75278b 100644
--- a/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml
+++ b/modules/ogg/doc_classes/OggPacketSequencePlayback.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OGGPacketSequencePlayback" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OggPacketSequencePlayback" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp
index da52ecfdd5..de8bf4a087 100644
--- a/modules/ogg/ogg_packet_sequence.cpp
+++ b/modules/ogg/ogg_packet_sequence.cpp
@@ -31,7 +31,7 @@
#include "ogg_packet_sequence.h"
#include "core/variant/typed_array.h"
-void OGGPacketSequence::push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data) {
+void OggPacketSequence::push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data) {
Vector<PackedByteArray> data_stored;
for (int i = 0; i < p_data.size(); i++) {
data_stored.push_back(p_data[i]);
@@ -41,7 +41,7 @@ void OGGPacketSequence::push_page(int64_t p_granule_pos, const Vector<PackedByte
data_version++;
}
-void OGGPacketSequence::set_packet_data(const Array &p_data) {
+void OggPacketSequence::set_packet_data(const Array &p_data) {
data_version++; // Update the data version so old playbacks know that they can't rely on us anymore.
page_data.clear();
for (int page_idx = 0; page_idx < p_data.size(); page_idx++) {
@@ -54,7 +54,7 @@ void OGGPacketSequence::set_packet_data(const Array &p_data) {
}
}
-Array OGGPacketSequence::get_packet_data() const {
+Array OggPacketSequence::get_packet_data() const {
Array ret;
for (const Vector<PackedByteArray> &page : page_data) {
Array page_variant;
@@ -66,7 +66,7 @@ Array OGGPacketSequence::get_packet_data() const {
return ret;
}
-void OGGPacketSequence::set_packet_granule_positions(const Array &p_granule_positions) {
+void OggPacketSequence::set_packet_granule_positions(const Array &p_granule_positions) {
data_version++; // Update the data version so old playbacks know that they can't rely on us anymore.
page_granule_positions.clear();
for (int page_idx = 0; page_idx < p_granule_positions.size(); page_idx++) {
@@ -75,7 +75,7 @@ void OGGPacketSequence::set_packet_granule_positions(const Array &p_granule_posi
}
}
-Array OGGPacketSequence::get_packet_granule_positions() const {
+Array OggPacketSequence::get_packet_granule_positions() const {
Array ret;
for (int64_t granule_pos : page_granule_positions) {
ret.push_back(granule_pos);
@@ -83,22 +83,22 @@ Array OGGPacketSequence::get_packet_granule_positions() const {
return ret;
}
-void OGGPacketSequence::set_sampling_rate(float p_sampling_rate) {
+void OggPacketSequence::set_sampling_rate(float p_sampling_rate) {
sampling_rate = p_sampling_rate;
}
-float OGGPacketSequence::get_sampling_rate() const {
+float OggPacketSequence::get_sampling_rate() const {
return sampling_rate;
}
-int64_t OGGPacketSequence::get_final_granule_pos() const {
+int64_t OggPacketSequence::get_final_granule_pos() const {
if (!page_granule_positions.is_empty()) {
return page_granule_positions[page_granule_positions.size() - 1];
}
return -1;
}
-float OGGPacketSequence::get_length() const {
+float OggPacketSequence::get_length() const {
int64_t granule_pos = get_final_granule_pos();
if (granule_pos < 0) {
return 0;
@@ -106,33 +106,33 @@ float OGGPacketSequence::get_length() const {
return granule_pos / sampling_rate;
}
-Ref<OGGPacketSequencePlayback> OGGPacketSequence::instance_playback() {
- Ref<OGGPacketSequencePlayback> playback;
+Ref<OggPacketSequencePlayback> OggPacketSequence::instantiate_playback() {
+ Ref<OggPacketSequencePlayback> playback;
playback.instantiate();
- playback->ogg_packet_sequence = Ref<OGGPacketSequence>(this);
+ playback->ogg_packet_sequence = Ref<OggPacketSequence>(this);
playback->data_version = data_version;
return playback;
}
-void OGGPacketSequence::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_packet_data", "packet_data"), &OGGPacketSequence::set_packet_data);
- ClassDB::bind_method(D_METHOD("get_packet_data"), &OGGPacketSequence::get_packet_data);
+void OggPacketSequence::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_packet_data", "packet_data"), &OggPacketSequence::set_packet_data);
+ ClassDB::bind_method(D_METHOD("get_packet_data"), &OggPacketSequence::get_packet_data);
- ClassDB::bind_method(D_METHOD("set_packet_granule_positions", "granule_positions"), &OGGPacketSequence::set_packet_granule_positions);
- ClassDB::bind_method(D_METHOD("get_packet_granule_positions"), &OGGPacketSequence::get_packet_granule_positions);
+ ClassDB::bind_method(D_METHOD("set_packet_granule_positions", "granule_positions"), &OggPacketSequence::set_packet_granule_positions);
+ ClassDB::bind_method(D_METHOD("get_packet_granule_positions"), &OggPacketSequence::get_packet_granule_positions);
- ClassDB::bind_method(D_METHOD("set_sampling_rate", "sampling_rate"), &OGGPacketSequence::set_sampling_rate);
- ClassDB::bind_method(D_METHOD("get_sampling_rate"), &OGGPacketSequence::get_sampling_rate);
+ ClassDB::bind_method(D_METHOD("set_sampling_rate", "sampling_rate"), &OggPacketSequence::set_sampling_rate);
+ ClassDB::bind_method(D_METHOD("get_sampling_rate"), &OggPacketSequence::get_sampling_rate);
- ClassDB::bind_method(D_METHOD("get_length"), &OGGPacketSequence::get_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &OggPacketSequence::get_length);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "packet_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_data", "get_packet_data");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "granule_positions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_granule_positions", "get_packet_granule_positions");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sampling_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_sampling_rate", "get_sampling_rate");
}
-bool OGGPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const {
+bool OggPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const {
ERR_FAIL_COND_V(data_version != ogg_packet_sequence->data_version, false);
ERR_FAIL_COND_V(ogg_packet_sequence->page_data.is_empty(), false);
ERR_FAIL_COND_V(ogg_packet_sequence->page_granule_positions.is_empty(), false);
@@ -161,7 +161,7 @@ bool OGGPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const {
return true;
}
-uint32_t OGGPacketSequencePlayback::seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive) {
+uint32_t OggPacketSequencePlayback::seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive) {
// FIXME: This function needs better corner case handling.
if (before_page_inclusive == after_page_inclusive) {
return before_page_inclusive;
@@ -198,7 +198,7 @@ uint32_t OGGPacketSequencePlayback::seek_page_internal(int64_t granule, uint32_t
}
}
-bool OGGPacketSequencePlayback::seek_page(int64_t p_granule_pos) {
+bool OggPacketSequencePlayback::seek_page(int64_t p_granule_pos) {
int correct_page = seek_page_internal(p_granule_pos, 0, ogg_packet_sequence->page_data.size() - 1);
if (correct_page == -1) {
return false;
@@ -213,10 +213,10 @@ bool OGGPacketSequencePlayback::seek_page(int64_t p_granule_pos) {
return true;
}
-OGGPacketSequencePlayback::OGGPacketSequencePlayback() {
+OggPacketSequencePlayback::OggPacketSequencePlayback() {
packet = new ogg_packet();
}
-OGGPacketSequencePlayback::~OGGPacketSequencePlayback() {
+OggPacketSequencePlayback::~OggPacketSequencePlayback() {
delete packet;
}
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index 73e3cb4fff..efd3b64a39 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -38,12 +38,12 @@
#include "core/variant/variant.h"
#include "thirdparty/libogg/ogg/ogg.h"
-class OGGPacketSequencePlayback;
+class OggPacketSequencePlayback;
-class OGGPacketSequence : public Resource {
- GDCLASS(OGGPacketSequence, Resource);
+class OggPacketSequence : public Resource {
+ GDCLASS(OggPacketSequence, Resource);
- friend class OGGPacketSequencePlayback;
+ friend class OggPacketSequencePlayback;
// List of pages, each of which is a list of packets on that page. The innermost PackedByteArrays contain complete ogg packets.
Vector<Vector<PackedByteArray>> page_data;
@@ -73,7 +73,7 @@ public:
void set_packet_granule_positions(const Array &p_granule_positions);
Array get_packet_granule_positions() const;
- // Sets a sampling rate associated with this object. OGGPacketSequence doesn't understand codecs,
+ // Sets a sampling rate associated with this object. OggPacketSequence doesn't understand codecs,
// so this value is naively stored as a convenience.
void set_sampling_rate(float p_sampling_rate);
@@ -86,18 +86,18 @@ public:
// Returns the granule position of the last page in this sequence.
int64_t get_final_granule_pos() const;
- Ref<OGGPacketSequencePlayback> instance_playback();
+ Ref<OggPacketSequencePlayback> instantiate_playback();
- OGGPacketSequence() {}
- virtual ~OGGPacketSequence() {}
+ OggPacketSequence() {}
+ virtual ~OggPacketSequence() {}
};
-class OGGPacketSequencePlayback : public RefCounted {
- GDCLASS(OGGPacketSequencePlayback, RefCounted);
+class OggPacketSequencePlayback : public RefCounted {
+ GDCLASS(OggPacketSequencePlayback, RefCounted);
- friend class OGGPacketSequence;
+ friend class OggPacketSequence;
- Ref<OGGPacketSequence> ogg_packet_sequence;
+ Ref<OggPacketSequence> ogg_packet_sequence;
mutable int64_t page_cursor = 0;
mutable int32_t packet_cursor = 0;
@@ -121,8 +121,8 @@ public:
// Returns true on success, false on failure.
bool seek_page(int64_t p_granule_pos);
- OGGPacketSequencePlayback();
- virtual ~OGGPacketSequencePlayback();
+ OggPacketSequencePlayback();
+ virtual ~OggPacketSequencePlayback();
};
#endif // OGG_PACKET_SEQUENCE_H
diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp
index 3d04351032..aca6d1e1f8 100644
--- a/modules/ogg/register_types.cpp
+++ b/modules/ogg/register_types.cpp
@@ -32,9 +32,17 @@
#include "ogg_packet_sequence.h"
-void register_ogg_types() {
- GDREGISTER_CLASS(OGGPacketSequence);
- GDREGISTER_CLASS(OGGPacketSequencePlayback);
+void initialize_ogg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
+ GDREGISTER_CLASS(OggPacketSequence);
+ GDREGISTER_CLASS(OggPacketSequencePlayback);
}
-void unregister_ogg_types() {}
+void uninitialize_ogg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/ogg/register_types.h b/modules/ogg/register_types.h
index 6ef7dc93ca..9065d26d07 100644
--- a/modules/ogg/register_types.h
+++ b/modules/ogg/register_types.h
@@ -31,7 +31,9 @@
#ifndef OGG_REGISTER_TYPES_H
#define OGG_REGISTER_TYPES_H
-void register_ogg_types();
-void unregister_ogg_types();
+#include "modules/register_module_types.h"
+
+void initialize_ogg_module(ModuleInitializationLevel p_level);
+void uninitialize_ogg_module(ModuleInitializationLevel p_level);
#endif // OGG_REGISTER_TYPES_H
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index ff320236a7..593d1ff3c1 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -35,7 +35,11 @@ if env["platform"] == "android":
# may need to include java parts of the openxr loader
elif env["platform"] == "linuxbsd":
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX", "XR_USE_PLATFORM_XLIB"])
+ env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
+
+ if env["x11"]:
+ env_thirdparty.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
+
# FIXME: Review what needs to be set for Android and macOS.
env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
@@ -81,6 +85,8 @@ if env["platform"] == "android":
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp")
+
env.modules_sources += module_obj
if env["tools"]:
diff --git a/modules/openxr/action_map/openxr_action.h b/modules/openxr/action_map/openxr_action.h
index 5e57f89133..a7c1c9988c 100644
--- a/modules/openxr/action_map/openxr_action.h
+++ b/modules/openxr/action_map/openxr_action.h
@@ -84,4 +84,4 @@ public:
VARIANT_ENUM_CAST(OpenXRAction::ActionType);
-#endif // !OPENXR_ACTION_H
+#endif // OPENXR_ACTION_H
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index 366e131369..0eb5302442 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -182,10 +182,40 @@ void OpenXRActionMap::create_default_action_sets() {
Ref<OpenXRAction> ax_touch = action_set->add_new_action("ax_touch", "A/X touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_button = action_set->add_new_action("by_button", "B/Y button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_touch = action_set->add_new_action("by_touch", "B/Y touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
- Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
+ Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE,
+ "/user/hand/left,"
+ "/user/hand/right,"
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot,"
+ "/user/vive_tracker_htcx/role/right_foot,"
+ "/user/vive_tracker_htcx/role/left_shoulder,"
+ "/user/vive_tracker_htcx/role/right_shoulder,"
+ "/user/vive_tracker_htcx/role/left_elbow,"
+ "/user/vive_tracker_htcx/role/right_elbow,"
+ "/user/vive_tracker_htcx/role/left_knee,"
+ "/user/vive_tracker_htcx/role/right_knee,"
+ "/user/vive_tracker_htcx/role/waist,"
+ "/user/vive_tracker_htcx/role/chest,"
+ "/user/vive_tracker_htcx/role/camera,"
+ "/user/vive_tracker_htcx/role/keyboard");
Ref<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
- Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC, "/user/hand/left,/user/hand/right");
+ Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC,
+ "/user/hand/left,"
+ "/user/hand/right,"
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot,"
+ "/user/vive_tracker_htcx/role/right_foot,"
+ "/user/vive_tracker_htcx/role/left_shoulder,"
+ "/user/vive_tracker_htcx/role/right_shoulder,"
+ "/user/vive_tracker_htcx/role/left_elbow,"
+ "/user/vive_tracker_htcx/role/right_elbow,"
+ "/user/vive_tracker_htcx/role/left_knee,"
+ "/user/vive_tracker_htcx/role/right_knee,"
+ "/user/vive_tracker_htcx/role/waist,"
+ "/user/vive_tracker_htcx/role/chest,"
+ "/user/vive_tracker_htcx/role/camera,"
+ "/user/vive_tracker_htcx/role/keyboard");
// Create our interaction profiles
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
@@ -399,6 +429,37 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
+
+ // Create our HTC Vive tracker profile
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_tracker_htcx");
+ profile->add_new_binding(default_pose,
+ // "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_foot/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_knee/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_knee/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/waist/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/chest/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/camera/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/keyboard/input/grip/pose");
+ profile->add_new_binding(haptic,
+ // "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_foot/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_shoulder/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_shoulder/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_elbow/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_elbow/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_knee/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_knee/output/haptic,"
+ "/user/vive_tracker_htcx/role/waist/output/haptic,"
+ "/user/vive_tracker_htcx/role/chest/output/haptic,"
+ "/user/vive_tracker_htcx/role/camera/output/haptic,"
+ "/user/vive_tracker_htcx/role/keyboard/output/haptic");
add_interaction_profile(profile);
}
diff --git a/modules/openxr/action_map/openxr_action_map.h b/modules/openxr/action_map/openxr_action_map.h
index dcd8fc71aa..8659cd3942 100644
--- a/modules/openxr/action_map/openxr_action_map.h
+++ b/modules/openxr/action_map/openxr_action_map.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef OPENXR_ACTION_SETS_H
-#define OPENXR_ACTION_SETS_H
+#ifndef OPENXR_ACTION_MAP_H
+#define OPENXR_ACTION_MAP_H
#include "core/io/resource.h"
@@ -79,4 +79,4 @@ public:
~OpenXRActionMap();
};
-#endif // !OPENXR_ACTION_SETS_H
+#endif // OPENXR_ACTION_MAP_H
diff --git a/modules/openxr/action_map/openxr_action_set.h b/modules/openxr/action_map/openxr_action_set.h
index b1d7168894..2ef7ba4c32 100644
--- a/modules/openxr/action_map/openxr_action_set.h
+++ b/modules/openxr/action_map/openxr_action_set.h
@@ -72,4 +72,4 @@ public:
~OpenXRActionSet();
};
-#endif // !OPENXR_ACTION_SET_H
+#endif // OPENXR_ACTION_SET_H
diff --git a/modules/openxr/action_map/openxr_defs.cpp b/modules/openxr/action_map/openxr_defs.cpp
index e10326449c..89860199be 100644
--- a/modules/openxr/action_map/openxr_defs.cpp
+++ b/modules/openxr/action_map/openxr_defs.cpp
@@ -32,8 +32,28 @@
// Our top level paths to which devices can be bound
OpenXRDefs::TopLevelPath OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_TOP_LEVEL_PATH_MAX] = {
+ // Core OpenXR paths
{ "Left hand controller", "/user/hand/left" },
{ "Right hand controller", "/user/hand/right" },
+ { "Head", "/user/head" },
+ { "Gamepad", "/user/gamepad" },
+ { "Treadmill", "/user/treadmill" },
+
+ // Specific to HTC tracker extension
+ // { "Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object" },
+ { "Left foot tracker", "/user/vive_tracker_htcx/role/left_foot" },
+ { "Right foot tracker", "/user/vive_tracker_htcx/role/right_foot" },
+ { "Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder" },
+ { "Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder" },
+ { "Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow" },
+ { "Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow" },
+ { "Left knee tracker", "/user/vive_tracker_htcx/role/left_knee" },
+ { "Right knee tracker", "/user/vive_tracker_htcx/role/right_knee" },
+ { "Waist tracker", "/user/vive_tracker_htcx/role/waist" },
+ { "Chest tracker", "/user/vive_tracker_htcx/role/chest" },
+ { "Camera tracker", "/user/vive_tracker_htcx/role/camera" },
+ { "Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard" },
+
};
// Fallback Khronos simple controller
@@ -378,6 +398,137 @@ OpenXRDefs::IOPath OpenXRDefs::huawei_controller_paths[] = {
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
+// HTC Vive tracker
+// Interestingly enough trackers don't have buttons or inputs, yet these are defined in the spec.
+// I think this can be supported through attachments on the trackers.
+OpenXRDefs::IOPath OpenXRDefs::vive_tracker_controller_paths[] = {
+ // { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+
+ // { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+
+ // { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+
+ // { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+};
+
OpenXRDefs::InteractionProfile OpenXRDefs::available_interaction_profiles[] = {
{
"Simple controller", // display_name
@@ -439,6 +590,13 @@ OpenXRDefs::InteractionProfile OpenXRDefs::available_interaction_profiles[] = {
huawei_controller_paths, // io_paths
sizeof(huawei_controller_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
+
+ {
+ "HTC Vive tracker", // display_name
+ "/interaction_profiles/htc/vive_tracker_htcx", // openxr_path
+ vive_tracker_controller_paths, // io_paths
+ sizeof(vive_tracker_controller_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
+ },
};
int OpenXRDefs::available_interaction_profile_count = sizeof(OpenXRDefs::available_interaction_profiles) / sizeof(OpenXRDefs::InteractionProfile);
diff --git a/modules/openxr/action_map/openxr_defs.h b/modules/openxr/action_map/openxr_defs.h
index dbda4757f1..446e6eb9c6 100644
--- a/modules/openxr/action_map/openxr_defs.h
+++ b/modules/openxr/action_map/openxr_defs.h
@@ -51,8 +51,28 @@
class OpenXRDefs {
public:
enum TOP_LEVEL_PATH {
+ // Core OpenXR toplevel paths
OPENXR_LEFT_HAND,
OPENXR_RIGHT_HAND,
+ OPENXR_HEAD,
+ OPENXR_GAMEPAD,
+ OPENXR_TREADMILL,
+
+ // HTC tracker extension toplevel paths
+ // OPENXR_HTC_HANDHELD_TRACKER,
+ OPENXR_HTC_LEFT_FOOT_TRACKER,
+ OPENXR_HTC_RIGHT_FOOT_TRACKER,
+ OPENXR_HTC_LEFT_SHOULDER_TRACKER,
+ OPENXR_HTC_RIGHT_SHOULDER_TRACKER,
+ OPENXR_HTC_LEFT_ELBOW_TRACKER,
+ OPENXR_HTC_RIGHT_ELBOW_TRACKER,
+ OPENXR_HTC_LEFT_KNEE_TRACKER,
+ OPENXR_HTC_RIGHT_KNEE_TRACKER,
+ OPENXR_HTC_WAIST_TRACKER,
+ OPENXR_HTC_CHEST_TRACKER,
+ OPENXR_HTC_CAMERA_TRACKER,
+ OPENXR_HTC_KEYBOARD_TRACKER,
+
OPENXR_TOP_LEVEL_PATH_MAX
};
@@ -89,6 +109,7 @@ private:
static IOPath vive_cosmos_paths[];
static IOPath vive_focus3_paths[];
static IOPath huawei_controller_paths[];
+ static IOPath vive_tracker_controller_paths[];
static InteractionProfile available_interaction_profiles[];
static int available_interaction_profile_count;
@@ -100,4 +121,4 @@ public:
static PackedStringArray get_interaction_profile_paths();
};
-#endif // !OPENXR_DEFS_H
+#endif // OPENXR_DEFS_H
diff --git a/modules/openxr/action_map/openxr_interaction_profile.cpp b/modules/openxr/action_map/openxr_interaction_profile.cpp
index 342c36cdff..99d7a17acf 100644
--- a/modules/openxr/action_map/openxr_interaction_profile.cpp
+++ b/modules/openxr/action_map/openxr_interaction_profile.cpp
@@ -40,7 +40,7 @@ void OpenXRIPBinding::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_paths"), &OpenXRIPBinding::get_paths);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "paths", PROPERTY_HINT_ARRAY_TYPE, "STRING"), "set_paths", "get_paths");
- ClassDB::bind_method(D_METHOD("has_path"), &OpenXRIPBinding::has_path);
+ ClassDB::bind_method(D_METHOD("has_path", "path"), &OpenXRIPBinding::has_path);
ClassDB::bind_method(D_METHOD("add_path", "path"), &OpenXRIPBinding::add_path);
ClassDB::bind_method(D_METHOD("remove_path", "path"), &OpenXRIPBinding::remove_path);
}
diff --git a/modules/openxr/action_map/openxr_interaction_profile.h b/modules/openxr/action_map/openxr_interaction_profile.h
index 46b1bda50f..c77fd490bb 100644
--- a/modules/openxr/action_map/openxr_interaction_profile.h
+++ b/modules/openxr/action_map/openxr_interaction_profile.h
@@ -98,4 +98,4 @@ public:
~OpenXRInteractionProfile();
};
-#endif // !OPENXR_INTERACTION_PROFILE_H
+#endif // OPENXR_INTERACTION_PROFILE_H
diff --git a/modules/openxr/doc_classes/OpenXRIPBinding.xml b/modules/openxr/doc_classes/OpenXRIPBinding.xml
index 9e1176874a..f96637f2f5 100644
--- a/modules/openxr/doc_classes/OpenXRIPBinding.xml
+++ b/modules/openxr/doc_classes/OpenXRIPBinding.xml
@@ -24,7 +24,7 @@
</method>
<method name="has_path" qualifiers="const">
<return type="bool" />
- <argument index="0" name="arg0" type="String" />
+ <argument index="0" name="path" type="String" />
<description>
Returns [code]true[/code] if this input/output path is part of this binding.
</description>
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 74f708bc95..25bf496de9 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -5,10 +5,10 @@
</brief_description>
<description>
The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games.
- Due to the needs of OpenXR this interface works slightly different then other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
+ Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset.
</description>
<tutorials>
- <link title="OpenXR documentation">$DOCS_URL/tutorials/vr/openxr/index.html</link>
+ <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link>
</tutorials>
<signals>
<signal name="pose_recentered">
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
index e2a4f67f16..41c6465f43 100644
--- a/modules/openxr/editor/openxr_action_editor.cpp
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -63,8 +63,7 @@ void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_te
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
- ERR_FAIL_COND(p_idx < 0);
- ERR_FAIL_COND(p_idx >= OpenXRAction::OPENXR_ACTION_MAX);
+ ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX);
action->set_action_type(OpenXRAction::ActionType(p_idx));
}
diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h
index 6e1b7ab779..6cf098cf08 100644
--- a/modules/openxr/editor/openxr_action_editor.h
+++ b/modules/openxr/editor/openxr_action_editor.h
@@ -64,4 +64,4 @@ public:
OpenXRActionEditor(Ref<OpenXRAction> p_action);
};
-#endif // !OPENXR_ACTION_EDITOR_H
+#endif // OPENXR_ACTION_EDITOR_H
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 6e9a2e1b61..0a2d0a3110 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -258,7 +258,7 @@ void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_
}
void OpenXRActionMapEditor::_on_save_action_map() {
- Error err = ResourceSaver::save(edited_path, action_map);
+ Error err = ResourceSaver::save(action_map, edited_path);
if (err != OK) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), edited_path));
return;
@@ -344,6 +344,7 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() {
tabs = memnew(TabContainer);
tabs->set_h_size_flags(SIZE_EXPAND_FILL);
tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ tabs->set_theme_type_variation("TabContainerOdd");
tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed));
tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed));
add_child(tabs);
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index dfc941b500..a19bc90f56 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -97,4 +97,4 @@ public:
~OpenXRActionMapEditor();
};
-#endif // !OPENXR_ACTION_MAP_EDITOR_H
+#endif // OPENXR_ACTION_MAP_EDITOR_H
diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h
index f3960dcbf9..d8c85d03dd 100644
--- a/modules/openxr/editor/openxr_action_set_editor.h
+++ b/modules/openxr/editor/openxr_action_set_editor.h
@@ -85,4 +85,4 @@ public:
OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
};
-#endif // !OPENXR_ACTION_SET_EDITOR_H
+#endif // OPENXR_ACTION_SET_EDITOR_H
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
index af8ee7d54c..ce230ee95b 100644
--- a/modules/openxr/editor/openxr_editor_plugin.h
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -50,4 +50,4 @@ public:
~OpenXREditorPlugin();
};
-#endif // !OPENXR_EDITOR_PLUGIN_H
+#endif // OPENXR_EDITOR_PLUGIN_H
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.cpp b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
index 24ac5494dd..e2dc2d1b74 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
@@ -169,9 +169,7 @@ void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, co
Button *path_add = memnew(Button);
path_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
path_add->set_flat(true);
- Vector<Variant> add_binds;
- add_binds.push_back(String(p_io_path->openxr_path));
- path_add->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for), add_binds);
+ path_add->connect("pressed", callable_mp(this, &OpenXRInteractionProfileEditor::select_action_for).bind(String(p_io_path->openxr_path)));
path_hb->add_child(path_add);
if (interaction_profile.is_valid()) {
@@ -198,10 +196,7 @@ void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, co
Button *action_rem = memnew(Button);
action_rem->set_flat(true);
action_rem->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- Vector<Variant> remove_binds;
- remove_binds.push_back(action->get_name_with_set());
- remove_binds.push_back(String(p_io_path->openxr_path));
- action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditorBase *)this, &OpenXRInteractionProfileEditorBase::_remove_binding), remove_binds);
+ action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditorBase *)this, &OpenXRInteractionProfileEditorBase::_remove_binding).bind(action->get_name_with_set(), String(p_io_path->openxr_path)));
action_hb->add_child(action_rem);
}
}
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h
index f50da1a003..20a37a80eb 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.h
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.h
@@ -80,4 +80,4 @@ public:
OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
};
-#endif // !OPENXR_INTERACTION_PROFILE_EDITOR_H
+#endif // OPENXR_INTERACTION_PROFILE_EDITOR_H
diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp
index c2a2965200..80e58044d5 100644
--- a/modules/openxr/editor/openxr_select_action_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_action_dialog.cpp
@@ -96,11 +96,9 @@ void OpenXRSelectActionDialog::open() {
Button *action_button = memnew(Button);
String action_name = action->get_name_with_set();
- Vector<Variant> binds;
- binds.push_back(action_name);
action_button->set_flat(true);
action_button->set_text(action->get_name() + ": " + action->get_localized_name());
- action_button->connect("pressed", callable_mp(this, &OpenXRSelectActionDialog::_on_select_action), binds);
+ action_button->connect("pressed", callable_mp(this, &OpenXRSelectActionDialog::_on_select_action).bind(action_name));
action_hb->add_child(action_button);
action_buttons[action_name] = action_button->get_path();
diff --git a/modules/openxr/editor/openxr_select_action_dialog.h b/modules/openxr/editor/openxr_select_action_dialog.h
index ea2c30373b..cbe1380e18 100644
--- a/modules/openxr/editor/openxr_select_action_dialog.h
+++ b/modules/openxr/editor/openxr_select_action_dialog.h
@@ -64,4 +64,4 @@ public:
OpenXRSelectActionDialog(Ref<OpenXRActionMap> p_action_map);
};
-#endif // !OPENXR_SELECT_ACTION_DIALOG_H
+#endif // OPENXR_SELECT_ACTION_DIALOG_H
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
index 12b110f146..23b025db08 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
@@ -80,11 +80,9 @@ void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_inclu
String path = interaction_profiles[i];
if (!p_do_not_include.has(path)) {
Button *ip_button = memnew(Button);
- Vector<Variant> binds;
- binds.push_back(path);
ip_button->set_flat(true);
ip_button->set_text(OpenXRDefs::get_profile(path)->display_name);
- ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile), binds);
+ ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path));
main_vb->add_child(ip_button);
ip_buttons[path] = ip_button->get_path();
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
index d177861ff3..54bfe3120a 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
@@ -63,4 +63,4 @@ public:
OpenXRSelectInteractionProfileDialog();
};
-#endif // !OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
+#endif // OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
diff --git a/modules/openxr/extensions/openxr_android_extension.h b/modules/openxr/extensions/openxr_android_extension.h
index e102197a55..88b0e310e7 100644
--- a/modules/openxr/extensions/openxr_android_extension.h
+++ b/modules/openxr/extensions/openxr_android_extension.h
@@ -44,4 +44,4 @@ private:
static OpenXRAndroidExtension *singleton;
};
-#endif // !OPENXR_ANDROID_EXTENSION_H
+#endif // OPENXR_ANDROID_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index f3064041b8..ecc6e0dd4e 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -32,8 +32,8 @@
#define OPENXR_EXTENSION_WRAPPER_H
#include "core/error/error_macros.h"
-#include "core/math/camera_matrix.h"
-#include "core/templates/map.h"
+#include "core/math/projection.h"
+#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
#include "thirdparty/openxr/src/common/xr_linear.h"
@@ -49,10 +49,10 @@ protected:
// Store extension we require.
// If bool pointer is a nullptr this means this extension is mandatory and initialisation will fail if it is not available
// If bool pointer is set, value will be set to true or false depending on whether extension is available
- Map<const char *, bool *> request_extensions;
+ HashMap<String, bool *> request_extensions;
public:
- virtual Map<const char *, bool *> get_request_extensions() {
+ virtual HashMap<String, bool *> get_request_extensions() {
return request_extensions;
}
@@ -97,11 +97,11 @@ public:
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0;
virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) = 0;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0;
- virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) = 0;
+ virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0;
virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) = 0;
OpenXRGraphicsExtensionWrapper(OpenXRAPI *p_openxr_api) :
OpenXRExtensionWrapper(p_openxr_api){};
};
-#endif // ~OPENXR_EXTENSION_WRAPPER_H
+#endif // OPENXR_EXTENSION_WRAPPER_H
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
new file mode 100644
index 0000000000..302acf4e30
--- /dev/null
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* openxr_htc_vive_tracker_extension.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_htc_vive_tracker_extension.h"
+#include "core/string/print_string.h"
+
+OpenXRHTCViveTrackerExtension *OpenXRHTCViveTrackerExtension::singleton = nullptr;
+
+OpenXRHTCViveTrackerExtension *OpenXRHTCViveTrackerExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRHTCViveTrackerExtension::OpenXRHTCViveTrackerExtension(OpenXRAPI *p_openxr_api) :
+ OpenXRExtensionWrapper(p_openxr_api) {
+ singleton = this;
+
+ request_extensions[XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME] = &available;
+}
+
+OpenXRHTCViveTrackerExtension::~OpenXRHTCViveTrackerExtension() {
+ singleton = nullptr;
+}
+
+bool OpenXRHTCViveTrackerExtension::is_available() {
+ return available;
+}
+
+bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &event) {
+ switch (event.type) {
+ case XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX: {
+ // Investigate if we need to do more here
+ print_verbose("OpenXR EVENT: VIVE tracker connected");
+
+ return true;
+ } break;
+ default: {
+ return false;
+ } break;
+ }
+}
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
new file mode 100644
index 0000000000..7f37351f27
--- /dev/null
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* openxr_htc_vive_tracker_extension.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
+#define OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRHTCViveTrackerExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRHTCViveTrackerExtension *get_singleton();
+
+ OpenXRHTCViveTrackerExtension(OpenXRAPI *p_openxr_api);
+ virtual ~OpenXRHTCViveTrackerExtension() override;
+
+ bool is_available();
+ virtual bool on_event_polled(const XrEventDataBuffer &event) override;
+
+private:
+ static OpenXRHTCViveTrackerExtension *singleton;
+
+ bool available = false;
+};
+
+#endif // OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 1eb7635a82..2608c4ac17 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -33,7 +33,7 @@
#include "../extensions/openxr_vulkan_extension.h"
#include "../openxr_api.h"
#include "../openxr_util.h"
-#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
+#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
@@ -420,7 +420,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
return true;
}
-bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) {
+bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
// Even though this is a Vulkan renderer we're using OpenGL coordinate systems
XrMatrix4x4f matrix;
XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
@@ -438,7 +438,6 @@ bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, false);
ERR_FAIL_COND_V(p_from_render_target.is_null(), false);
- ERR_FAIL_NULL_V(RendererStorageRD::base_singleton, false);
RID source_image = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(p_from_render_target);
ERR_FAIL_COND_V(source_image.is_null(), false);
@@ -450,11 +449,9 @@ bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target
ERR_FAIL_COND_V(fb.is_null(), false);
// Our vulkan extension can only be used in conjunction with our vulkan renderer.
- // We need access to the effects object in order to have access to our copy logic.
- // Breaking all the rules but there is no nice way to do this.
- EffectsRD *effects = RendererStorageRD::base_singleton->get_effects();
- ERR_FAIL_NULL_V(effects, false);
- effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
+ RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
+ ERR_FAIL_NULL_V(copy_effects, false);
+ copy_effects->copy_to_fb_rect(source_image, fb, Rect2i(), false, false, false, false, depth_image, data->is_multiview);
return true;
}
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h
index cf55ae264f..5dddc4b9c9 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.h
+++ b/modules/openxr/extensions/openxr_vulkan_extension.h
@@ -63,7 +63,7 @@ public:
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
virtual bool get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) override;
virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) override;
- virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, CameraMatrix &r_camera_matrix) override;
+ virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) override;
virtual bool copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) override;
private:
@@ -78,11 +78,11 @@ private:
bool check_graphics_api_support(XrVersion p_desired_version);
- VkInstance vulkan_instance;
- VkPhysicalDevice vulkan_physical_device;
- VkDevice vulkan_device;
- uint32_t vulkan_queue_family_index;
- uint32_t vulkan_queue_index;
+ VkInstance vulkan_instance = nullptr;
+ VkPhysicalDevice vulkan_physical_device = nullptr;
+ VkDevice vulkan_device = nullptr;
+ uint32_t vulkan_queue_family_index = 0;
+ uint32_t vulkan_queue_index = 0;
XrResult xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements);
XrResult xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result);
@@ -90,4 +90,4 @@ private:
XrResult xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result);
};
-#endif // !OPENXR_VULKAN_EXTENSION_H
+#endif // OPENXR_VULKAN_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 2e9be48f01..92d074cb75 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -48,7 +48,9 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
-#include "openxr_interface.h"
+#include "extensions/openxr_htc_vive_tracker_extension.h"
+
+#include "modules/openxr/openxr_interface.h"
OpenXRAPI *OpenXRAPI::singleton = nullptr;
@@ -56,19 +58,14 @@ bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
// @TODO we need an overrule switch so we can force enable openxr, i.e run "godot --openxr_enabled"
if (Engine::get_singleton()->is_editor_hint() && p_check_run_in_editor) {
-#ifdef TOOLS_ENABLED
// Disabled for now, using XR inside of the editor we'll be working on during the coming months.
return false;
-
- // bool enabled = GLOBAL_GET("xr/openxr/in_editor"); // EDITOR_GET("xr/openxr/in_editor");
- // return enabled;
-#else
- // we should never get here, editor hint won't be true if the editor isn't compiled in.
- return false;
-#endif
} else {
- bool enabled = GLOBAL_GET("xr/openxr/enabled");
- return enabled;
+ if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
+ return GLOBAL_GET("xr/openxr/enabled");
+ } else {
+ return XRServer::get_xr_mode() == XRServer::XRMODE_ON;
+ }
}
}
@@ -170,9 +167,9 @@ bool OpenXRAPI::load_supported_extensions() {
return true;
}
-bool OpenXRAPI::is_extension_supported(const char *p_extension) const {
+bool OpenXRAPI::is_extension_supported(const String &p_extension) const {
for (uint32_t i = 0; i < num_supported_extensions; i++) {
- if (strcmp(supported_extensions[i].extensionName, p_extension) == 0) {
+ if (supported_extensions[i].extensionName == p_extension) {
#ifdef DEBUG
print_line("OpenXR: requested extension", p_extension, "is supported");
#endif
@@ -204,9 +201,9 @@ bool OpenXRAPI::create_instance() {
// Create our OpenXR instance, this will query any registered extension wrappers for extensions we need to enable.
// Append the extensions requested by the registered extension wrappers.
- Map<const char *, bool *> requested_extensions;
+ HashMap<String, bool *> requested_extensions;
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
- Map<const char *, bool *> wrapper_request_extensions = wrapper->get_request_extensions();
+ const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_request_extensions();
// requested_extensions.insert(wrapper_request_extensions.begin(), wrapper_request_extensions.end());
for (auto &requested_extension : wrapper_request_extensions) {
@@ -224,6 +221,7 @@ bool OpenXRAPI::create_instance() {
// Check which extensions are supported
enabled_extensions.clear();
+
for (auto &requested_extension : requested_extensions) {
if (!is_extension_supported(requested_extension.key)) {
if (requested_extension.value == nullptr) {
@@ -238,13 +236,18 @@ bool OpenXRAPI::create_instance() {
*requested_extension.value = true;
// and record that we want to enable it
- enabled_extensions.push_back(requested_extension.key);
+ enabled_extensions.push_back(requested_extension.key.ascii());
} else {
// record that we want to enable this
- enabled_extensions.push_back(requested_extension.key);
+ enabled_extensions.push_back(requested_extension.key.ascii());
}
}
+ Vector<const char *> extension_ptrs;
+ for (int i = 0; i < enabled_extensions.size(); i++) {
+ extension_ptrs.push_back(enabled_extensions[i].get_data());
+ }
+
// Get our project name
String project_name = GLOBAL_GET("application/config/name");
@@ -264,8 +267,8 @@ bool OpenXRAPI::create_instance() {
application_info, // applicationInfo
0, // enabledApiLayerCount, need to find out if we need support for this?
nullptr, // enabledApiLayerNames
- uint32_t(enabled_extensions.size()), // enabledExtensionCount
- enabled_extensions.ptr() // enabledExtensionNames
+ uint32_t(extension_ptrs.size()), // enabledExtensionCount
+ extension_ptrs.ptr() // enabledExtensionNames
};
copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE);
@@ -1001,7 +1004,7 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
ERR_FAIL_V(false);
#endif
} else if (p_rendering_driver == "opengl3") {
-#ifdef OPENGL3_ENABLED
+#ifdef GLES3_ENABLED
// graphics_extension = memnew(OpenXROpenGLExtension(this));
// register_extension_wrapper(graphics_extension);
ERR_FAIL_V_MSG(false, "OpenXR: OpenGL is not supported at this time.");
@@ -1096,7 +1099,7 @@ Size2 OpenXRAPI::get_recommended_target_size() {
return target_size;
}
-XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
XrResult result;
ERR_FAIL_COND_V(!running, XRPose::XR_TRACKING_CONFIDENCE_NONE);
@@ -1172,7 +1175,7 @@ bool OpenXRAPI::get_view_transform(uint32_t p_view, Transform3D &r_transform) {
return true;
}
-bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix) {
+bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix) {
ERR_FAIL_COND_V(!running, false);
ERR_FAIL_NULL_V(graphics_extension, false);
@@ -1638,6 +1641,9 @@ OpenXRAPI::OpenXRAPI() {
// our android wrapper will initialize our android loader at this point
register_extension_wrapper(memnew(OpenXRAndroidExtension(this)));
#endif
+
+ // register our other extensions
+ register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this)));
}
OpenXRAPI::~OpenXRAPI() {
@@ -1724,7 +1730,7 @@ XRPose::TrackingConfidence OpenXRAPI::transform_from_location(const XrHandJointL
return _transform_from_location(p_location, r_transform);
}
-void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity) {
+void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
if (p_velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
XrVector3f linear_velocity = p_velocity.linearVelocity;
r_linear_velocity = Vector3(linear_velocity.x, linear_velocity.y, linear_velocity.z);
@@ -2297,7 +2303,7 @@ Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_tracker) {
return result_state.isActive ? Vector2(result_state.currentState.x, result_state.currentState.y) : Vector2();
}
-XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, XRPose::XR_TRACKING_CONFIDENCE_NONE);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, XRPose::XR_TRACKING_CONFIDENCE_NONE);
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 57475944c0..dc224c4237 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef OPENXR_DRIVER_H
-#define OPENXR_DRIVER_H
+#ifndef OPENXR_API_H
+#define OPENXR_API_H
#include "core/error/error_macros.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/os/memory.h"
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/vector.h"
#include "servers/xr/xr_pose.h"
@@ -73,7 +73,7 @@ private:
uint32_t num_supported_extensions = 0;
XrExtensionProperties *supported_extensions = nullptr;
Vector<OpenXRExtensionWrapper *> registered_extension_wrappers;
- Vector<const char *> enabled_extensions;
+ Vector<CharString> enabled_extensions;
bool ext_hp_mixed_reality_available = false;
bool ext_samsung_odyssey_available = false;
@@ -104,9 +104,9 @@ private:
// state
XrInstance instance = XR_NULL_HANDLE;
- XrSystemId system_id;
+ XrSystemId system_id = 0;
String system_name;
- uint32_t vendor_id;
+ uint32_t vendor_id = 0;
XrSystemTrackingProperties tracking_properties;
XrSession session = XR_NULL_HANDLE;
XrSessionState session_state = XR_SESSION_STATE_UNKNOWN;
@@ -132,7 +132,7 @@ private:
bool load_layer_properties();
bool load_supported_extensions();
- bool is_extension_supported(const char *p_extension) const;
+ bool is_extension_supported(const String &p_extension) const;
// instance
bool create_instance();
@@ -225,7 +225,7 @@ protected:
// helper method to get a valid Transform3D from an openxr space location
XRPose::TrackingConfidence transform_from_location(const XrSpaceLocation &p_location, Transform3D &r_transform);
XRPose::TrackingConfidence transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform);
- void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity);
+ void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
public:
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
@@ -247,9 +247,9 @@ public:
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; };
Size2 get_recommended_target_size();
- XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
- bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix);
+ bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, Projection &p_camera_matrix);
bool process();
void pre_render();
@@ -285,11 +285,11 @@ public:
bool get_action_bool(RID p_action, RID p_tracker);
float get_action_float(RID p_action, RID p_tracker);
Vector2 get_action_vector2(RID p_action, RID p_tracker);
- XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
OpenXRAPI();
~OpenXRAPI();
};
-#endif // !OPENXR_DRIVER_H
+#endif // OPENXR_API_H
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 41ce8c019e..6c2f08e21d 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -57,7 +57,24 @@ PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {
PackedStringArray arr = {
"left_hand", // /user/hand/left is mapped to our defaults
"right_hand", // /user/hand/right is mapped to our defaults
- "/user/treadmill"
+ "/user/treadmill",
+
+ // Even though these are only available if you have the tracker extension,
+ // we add these as we may be deploying on a different platform than our
+ // editor is running on.
+ "/user/vive_tracker_htcx/role/handheld_object",
+ "/user/vive_tracker_htcx/role/left_foot",
+ "/user/vive_tracker_htcx/role/right_foot",
+ "/user/vive_tracker_htcx/role/left_shoulder",
+ "/user/vive_tracker_htcx/role/right_shoulder",
+ "/user/vive_tracker_htcx/role/left_elbow",
+ "/user/vive_tracker_htcx/role/right_elbow",
+ "/user/vive_tracker_htcx/role/left_knee",
+ "/user/vive_tracker_htcx/role/right_knee",
+ "/user/vive_tracker_htcx/role/waist",
+ "/user/vive_tracker_htcx/role/chest",
+ "/user/vive_tracker_htcx/role/camera",
+ "/user/vive_tracker_htcx/role/keyboard"
};
return arr;
@@ -106,14 +123,14 @@ void OpenXRInterface::_load_action_map() {
#ifdef TOOLS_ENABLED
// Save our action sets so our user can
action_map->set_path(default_tres_name, true);
- ResourceSaver::save(default_tres_name, action_map);
+ ResourceSaver::save(action_map, default_tres_name);
#endif
}
}
// process our action map
if (action_map.is_valid()) {
- Map<Ref<OpenXRAction>, Action *> xr_actions;
+ HashMap<Ref<OpenXRAction>, Action *> xr_actions;
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
@@ -614,8 +631,8 @@ Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Trans
return p_cam_transform * xr_server->get_reference_frame() * t;
}
-CameraMatrix OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
- CameraMatrix cm;
+Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+ Projection cm;
if (openxr_api) {
if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index a223acfed0..a99012fd1d 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -121,7 +121,7 @@ public:
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual void process() override;
virtual void pre_render() override;
@@ -140,4 +140,4 @@ public:
~OpenXRInterface();
};
-#endif // !OPENXR_INTERFACE_H
+#endif // OPENXR_INTERFACE_H
diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h
index 4371b74d2f..a5cc7cd512 100644
--- a/modules/openxr/openxr_util.h
+++ b/modules/openxr/openxr_util.h
@@ -44,4 +44,4 @@ public:
static String make_xr_version_string(XrVersion p_version);
};
-#endif // !OPENXR_UTIL_H
+#endif // OPENXR_UTIL_H
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 0b48be5f2a..c765f169dc 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -54,49 +54,55 @@ static void _editor_init() {
#endif
-OpenXRAPI *openxr_api = nullptr;
-Ref<OpenXRInterface> openxr_interface;
+static OpenXRAPI *openxr_api = nullptr;
+static Ref<OpenXRInterface> openxr_interface;
-void preregister_openxr_types() {
- // For now we create our openxr device here. If we merge it with openxr_interface we'll create that here soon.
+void initialize_openxr_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ // For now we create our openxr device here. If we merge it with openxr_interface we'll create that here soon.
- if (OpenXRAPI::openxr_is_enabled()) {
- openxr_api = memnew(OpenXRAPI);
- ERR_FAIL_NULL(openxr_api);
+ if (OpenXRAPI::openxr_is_enabled()) {
+ openxr_api = memnew(OpenXRAPI);
+ ERR_FAIL_NULL(openxr_api);
- if (!openxr_api->initialize(Main::get_rendering_driver_name())) {
- memdelete(openxr_api);
- openxr_api = nullptr;
- return;
+ if (!openxr_api->initialize(Main::get_rendering_driver_name())) {
+ memdelete(openxr_api);
+ openxr_api = nullptr;
+ return;
+ }
}
}
-}
-void register_openxr_types() {
- GDREGISTER_CLASS(OpenXRInterface);
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(OpenXRInterface);
- GDREGISTER_CLASS(OpenXRAction);
- GDREGISTER_CLASS(OpenXRActionSet);
- GDREGISTER_CLASS(OpenXRActionMap);
- GDREGISTER_CLASS(OpenXRIPBinding);
- GDREGISTER_CLASS(OpenXRInteractionProfile);
+ GDREGISTER_CLASS(OpenXRAction);
+ GDREGISTER_CLASS(OpenXRActionSet);
+ GDREGISTER_CLASS(OpenXRActionMap);
+ GDREGISTER_CLASS(OpenXRIPBinding);
+ GDREGISTER_CLASS(OpenXRInteractionProfile);
- XRServer *xr_server = XRServer::get_singleton();
- if (xr_server) {
- openxr_interface.instantiate();
- xr_server->add_interface(openxr_interface);
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server) {
+ openxr_interface.instantiate();
+ xr_server->add_interface(openxr_interface);
- if (openxr_interface->initialize_on_startup()) {
- openxr_interface->initialize();
+ if (openxr_interface->initialize_on_startup()) {
+ openxr_interface->initialize();
+ }
}
- }
#ifdef TOOLS_ENABLED
- EditorNode::add_init_callback(_editor_init);
+ EditorNode::add_init_callback(_editor_init);
#endif
+ }
}
-void unregister_openxr_types() {
+void uninitialize_openxr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (openxr_interface.is_valid()) {
// uninitialize just in case
if (openxr_interface->is_initialized()) {
diff --git a/modules/openxr/register_types.h b/modules/openxr/register_types.h
index fb42770750..1b3d98422d 100644
--- a/modules/openxr/register_types.h
+++ b/modules/openxr/register_types.h
@@ -33,8 +33,9 @@
#define MODULE_OPENXR_HAS_PREREGISTER
-void preregister_openxr_types();
-void register_openxr_types();
-void unregister_openxr_types();
+#include "modules/register_module_types.h"
+
+void initialize_openxr_module(ModuleInitializationLevel p_level);
+void uninitialize_openxr_module(ModuleInitializationLevel p_level);
#endif // OPENXR_REGISTER_TYPES_H
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
index ecc256bff9..9b35b5616e 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -152,17 +152,17 @@ void LightmapRaycasterEmbree::commit() {
rtcCommitScene(embree_scene);
}
-void LightmapRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
- for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
- rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+void LightmapRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) {
+ for (const int &E : p_mesh_ids) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes = p_mesh_ids;
}
void LightmapRaycasterEmbree::clear_mesh_filter() {
- for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
- rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ for (const int &E : filter_meshes) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes.clear();
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h
index c420e617e5..2e9f59dda4 100644
--- a/modules/raycast/lightmap_raycaster.h
+++ b/modules/raycast/lightmap_raycaster.h
@@ -52,8 +52,8 @@ private:
static void filter_function(const struct RTCFilterFunctionNArguments *p_args);
- Map<unsigned int, AlphaTextureData> alpha_textures;
- Set<int> filter_meshes;
+ HashMap<unsigned int, AlphaTextureData> alpha_textures;
+ HashSet<int> filter_meshes;
public:
virtual bool intersect(Ray &p_ray) override;
@@ -64,7 +64,7 @@ public:
virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) override;
virtual void commit() override;
- virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override;
virtual void clear_mesh_filter() override;
static LightmapRaycaster *create_embree_raycaster();
@@ -74,4 +74,4 @@ public:
~LightmapRaycasterEmbree();
};
-#endif
+#endif // LIGHTMAP_RAYCASTER_H
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 2e0d17fb28..13824c3830 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -30,6 +30,7 @@
#include "raycast_occlusion_cull.h"
#include "core/config/project_settings.h"
+#include "core/object/worker_thread_pool.h"
#include "core/templates/local_vector.h"
#ifdef __SSE2__
@@ -78,17 +79,17 @@ void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
memset(camera_ray_masks.ptr(), ~0, camera_rays_tile_count * TILE_RAYS * sizeof(uint32_t));
}
-void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool) {
+void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) {
CameraRayThreadData td;
- td.thread_count = p_thread_work_pool.get_thread_count();
+ td.thread_count = WorkerThreadPool::get_singleton()->get_thread_count();
td.z_near = p_cam_projection.get_z_near();
td.z_far = p_cam_projection.get_z_far() * 1.05f;
td.camera_pos = p_cam_transform.origin;
- td.camera_dir = -p_cam_transform.basis.get_axis(2);
+ td.camera_dir = -p_cam_transform.basis.get_column(2);
td.camera_orthogonal = p_cam_orthogonal;
- CameraMatrix inv_camera_matrix = p_cam_projection.inverse();
+ Projection inv_camera_matrix = p_cam_projection.inverse();
Vector3 camera_corner_proj = Vector3(-1.0f, -1.0f, -1.0f);
Vector3 camera_corner_view = inv_camera_matrix.xform(camera_corner_proj);
td.pixel_corner = p_cam_transform.xform(camera_corner_view);
@@ -106,7 +107,8 @@ void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D
debug_tex_range = td.z_far;
- p_thread_work_pool.do_work(td.thread_count, this, &RaycastHZBuffer::_camera_rays_threaded, &td);
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RaycastHZBuffer::_camera_rays_threaded, &td, td.thread_count, -1, true, SNAME("RaycastOcclusionCullUpdateCamera"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
}
void RaycastOcclusionCull::RaycastHZBuffer::_camera_rays_threaded(uint32_t p_thread, const CameraRayThreadData *p_data) {
@@ -223,9 +225,9 @@ void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3
occluder->vertices = p_vertices;
occluder->indices = p_indices;
- for (Set<InstanceID>::Element *E = occluder->users.front(); E; E = E->next()) {
- RID scenario_rid = E->get().scenario;
- RID instance_rid = E->get().instance;
+ for (const InstanceID &E : occluder->users) {
+ RID scenario_rid = E.scenario;
+ RID instance_rid = E.instance;
ERR_CONTINUE(!scenarios.has(scenario_rid));
Scenario &scenario = scenarios[scenario_rid];
ERR_CONTINUE(!scenario.instances.has(instance_rid));
@@ -331,10 +333,10 @@ void RaycastOcclusionCull::scenario_remove_instance(RID p_scenario, RID p_instan
}
void RaycastOcclusionCull::Scenario::_update_dirty_instance_thread(int p_idx, RID *p_instances) {
- _update_dirty_instance(p_idx, p_instances, nullptr);
+ _update_dirty_instance(p_idx, p_instances);
}
-void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_instances, ThreadWorkPool *p_thread_pool) {
+void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_instances) {
OccluderInstance *occ_inst = instances.getptr(p_instances[p_idx]);
if (!occ_inst) {
@@ -355,14 +357,16 @@ void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_in
const Vector3 *read_ptr = occ->vertices.ptr();
Vector3 *write_ptr = occ_inst->xformed_vertices.ptr();
- if (p_thread_pool && vertices_size > 1024) {
+ if (vertices_size > 1024) {
TransformThreadData td;
td.xform = occ_inst->xform;
td.read = read_ptr;
td.write = write_ptr;
td.vertex_count = vertices_size;
- td.thread_count = p_thread_pool->get_thread_count();
- p_thread_pool->do_work(td.thread_count, this, &Scenario::_transform_vertices_thread, &td);
+ td.thread_count = WorkerThreadPool::get_singleton()->get_thread_count();
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &Scenario::_transform_vertices_thread, &td, td.thread_count, -1, true, SNAME("RaycastOcclusionCull"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+
} else {
_transform_vertices_range(read_ptr, write_ptr, occ_inst->xform, 0, vertices_size);
}
@@ -392,7 +396,7 @@ void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) {
scenario->commit_done = true;
}
-bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
+bool RaycastOcclusionCull::Scenario::update() {
ERR_FAIL_COND_V(singleton == nullptr, false);
if (commit_thread == nullptr) {
@@ -426,13 +430,15 @@ bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
instances.erase(removed_instances[i]);
}
- if (dirty_instances_array.size() / p_thread_pool.get_thread_count() > 128) {
+ if (dirty_instances_array.size() / WorkerThreadPool::get_singleton()->get_thread_count() > 128) {
// Lots of instances, use per-instance threading
- p_thread_pool.do_work(dirty_instances_array.size(), this, &Scenario::_update_dirty_instance_thread, dirty_instances_array.ptr());
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &Scenario::_update_dirty_instance_thread, dirty_instances_array.ptr(), dirty_instances_array.size(), -1, true, SNAME("RaycastOcclusionCullUpdate"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+
} else {
// Few instances, use threading on the vertex transforms
for (unsigned int i = 0; i < dirty_instances_array.size(); i++) {
- _update_dirty_instance(i, dirty_instances_array.ptr(), &p_thread_pool);
+ _update_dirty_instance(i, dirty_instances_array.ptr());
}
}
@@ -454,10 +460,9 @@ bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
next_scene = rtcNewScene(raycast_singleton->ebr_device);
rtcSetSceneBuildQuality(next_scene, RTCBuildQuality(raycast_singleton->build_quality));
- const RID *inst_rid = nullptr;
- while ((inst_rid = instances.next(inst_rid))) {
- OccluderInstance *occ_inst = instances.getptr(*inst_rid);
- Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
+ for (const KeyValue<RID, OccluderInstance> &E : instances) {
+ const OccluderInstance *occ_inst = &E.value;
+ const Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
if (!occ || !occ_inst->enabled) {
continue;
@@ -485,7 +490,7 @@ void RaycastOcclusionCull::Scenario::_raycast(uint32_t p_idx, const RaycastThrea
rtcIntersect16((const int *)&p_raycast_data->masks[p_idx * TILE_RAYS], ebr_scene[current_scene_idx], &ctx, &p_raycast_data->rays[p_idx]);
}
-void RaycastOcclusionCull::Scenario::raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const {
+void RaycastOcclusionCull::Scenario::raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count) const {
ERR_FAIL_COND(singleton == nullptr);
if (raycast_singleton->ebr_device == nullptr) {
return; // Embree is initialized on demand when there is some scenario with occluders in it.
@@ -499,7 +504,8 @@ void RaycastOcclusionCull::Scenario::raycast(CameraRayTile *r_rays, const uint32
td.rays = r_rays;
td.masks = p_valid_masks;
- p_thread_pool.do_work(p_tile_count, this, &Scenario::_raycast, &td);
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &Scenario::_raycast, &td, p_tile_count, -1, true, SNAME("RaycastOcclusionCullRaycast"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
}
////////////////////////////////////////////////////////
@@ -525,7 +531,7 @@ void RaycastOcclusionCull::buffer_set_size(RID p_buffer, const Vector2i &p_size)
buffers[p_buffer].resize(p_size);
}
-void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {
+void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) {
if (!buffers.has(p_buffer)) {
return;
}
@@ -538,17 +544,17 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_
Scenario &scenario = scenarios[buffer.scenario_rid];
- bool removed = scenario.update(p_thread_pool);
+ bool removed = scenario.update();
if (removed) {
scenarios.erase(buffer.scenario_rid);
return;
}
- buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_thread_pool);
+ buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal);
- scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count, p_thread_pool);
- buffer.sort_rays(-p_cam_transform.basis.get_axis(2), p_cam_orthogonal);
+ scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count);
+ buffer.sort_rays(-p_cam_transform.basis.get_column(2), p_cam_orthogonal);
buffer.update_mips();
}
@@ -573,9 +579,8 @@ void RaycastOcclusionCull::set_build_quality(RS::ViewportOcclusionCullingBuildQu
build_quality = p_quality;
- const RID *scenario_rid = nullptr;
- while ((scenario_rid = scenarios.next(scenario_rid))) {
- scenarios[*scenario_rid].dirty = true;
+ for (KeyValue<RID, Scenario> &K : scenarios) {
+ K.value.dirty = true;
}
}
@@ -596,9 +601,8 @@ RaycastOcclusionCull::RaycastOcclusionCull() {
}
RaycastOcclusionCull::~RaycastOcclusionCull() {
- const RID *scenario_rid = nullptr;
- while ((scenario_rid = scenarios.next(scenario_rid))) {
- Scenario &scenario = scenarios[*scenario_rid];
+ for (KeyValue<RID, Scenario> &K : scenarios) {
+ Scenario &scenario = K.value;
if (scenario.commit_thread) {
scenario.commit_thread->wait_to_finish();
memdelete(scenario.commit_thread);
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index 8453c5341d..056b808640 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef OCCLUSION_CULL_RAYCASTER_H
-#define OCCLUSION_CULL_RAYCASTER_H
+#ifndef RAYCAST_OCCLUSION_CULL_H
+#define RAYCAST_OCCLUSION_CULL_H
#include "core/io/image.h"
-#include "core/math/camera_matrix.h"
+#include "core/math/projection.h"
#include "core/object/object.h"
#include "core/object/ref_counted.h"
#include "core/templates/local_vector.h"
@@ -76,7 +76,7 @@ public:
virtual void clear() override;
virtual void resize(const Size2i &p_size) override;
void sort_rays(const Vector3 &p_camera_dir, bool p_orthogonal);
- void update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool);
+ void update_camera_rays(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal);
~RaycastHZBuffer();
};
@@ -86,11 +86,13 @@ private:
RID scenario;
RID instance;
- bool operator<(const InstanceID &rhs) const {
- if (instance == rhs.instance) {
- return rhs.scenario < scenario;
- }
- return instance < rhs.instance;
+ static uint32_t hash(const InstanceID &p_ins) {
+ uint32_t h = hash_murmur3_one_64(p_ins.scenario.get_id());
+ return hash_fmix32(hash_murmur3_one_64(p_ins.instance.get_id(), h));
+ }
+ bool operator==(const InstanceID &rhs) const {
+ return instance == rhs.instance && rhs.scenario == scenario;
+ ;
}
InstanceID() {}
@@ -101,7 +103,7 @@ private:
struct Occluder {
PackedVector3Array vertices;
PackedInt32Array indices;
- Set<InstanceID> users;
+ HashSet<InstanceID, InstanceID> users;
};
struct OccluderInstance {
@@ -136,19 +138,19 @@ private:
int current_scene_idx = 0;
HashMap<RID, OccluderInstance> instances;
- Set<RID> dirty_instances; // To avoid duplicates
+ HashSet<RID> dirty_instances; // To avoid duplicates
LocalVector<RID> dirty_instances_array; // To iterate and split into threads
LocalVector<RID> removed_instances;
void _update_dirty_instance_thread(int p_idx, RID *p_instances);
- void _update_dirty_instance(int p_idx, RID *p_instances, ThreadWorkPool *p_thread_pool);
+ void _update_dirty_instance(int p_idx, RID *p_instances);
void _transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data);
void _transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to);
static void _commit_scene(void *p_ud);
- bool update(ThreadWorkPool &p_thread_pool);
+ bool update();
void _raycast(uint32_t p_thread, const RaycastThreadData *p_raycast_data) const;
- void raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const;
+ void raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count) const;
};
static RaycastOcclusionCull *raycast_singleton;
@@ -181,7 +183,8 @@ public:
virtual HZBuffer *buffer_get_ptr(RID p_buffer) override;
virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) override;
virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) override;
- virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) override;
+ virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal) override;
+
virtual RID buffer_get_debug_texture(RID p_buffer) override;
virtual void set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) override;
@@ -190,4 +193,4 @@ public:
~RaycastOcclusionCull();
};
-#endif // OCCLUSION_CULL_RAYCASTER_H
+#endif // RAYCAST_OCCLUSION_CULL_H
diff --git a/modules/raycast/register_types.cpp b/modules/raycast/register_types.cpp
index 053039a85b..42de1d971d 100644
--- a/modules/raycast/register_types.cpp
+++ b/modules/raycast/register_types.cpp
@@ -36,7 +36,11 @@
RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
-void register_raycast_types() {
+void initialize_raycast_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
LightmapRaycasterEmbree::make_default_raycaster();
StaticRaycasterEmbree::make_default_raycaster();
@@ -44,7 +48,11 @@ void register_raycast_types() {
raycast_occlusion_cull = memnew(RaycastOcclusionCull);
}
-void unregister_raycast_types() {
+void uninitialize_raycast_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (raycast_occlusion_cull) {
memdelete(raycast_occlusion_cull);
}
diff --git a/modules/raycast/register_types.h b/modules/raycast/register_types.h
index 0abc5eb63a..a917285390 100644
--- a/modules/raycast/register_types.h
+++ b/modules/raycast/register_types.h
@@ -28,5 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-void register_raycast_types();
-void unregister_raycast_types();
+#include "modules/register_module_types.h"
+
+void initialize_raycast_module(ModuleInitializationLevel p_level);
+void uninitialize_raycast_module(ModuleInitializationLevel p_level);
diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster.cpp
index 9df6d5ff43..7659eea27f 100644
--- a/modules/raycast/static_raycaster.cpp
+++ b/modules/raycast/static_raycaster.cpp
@@ -94,17 +94,17 @@ void StaticRaycasterEmbree::commit() {
rtcCommitScene(embree_scene);
}
-void StaticRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
- for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
- rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+void StaticRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) {
+ for (const int &E : p_mesh_ids) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes = p_mesh_ids;
}
void StaticRaycasterEmbree::clear_mesh_filter() {
- for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
- rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ for (const int &E : filter_meshes) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes.clear();
diff --git a/modules/raycast/static_raycaster.h b/modules/raycast/static_raycaster.h
index cce19ba60d..607a392683 100644
--- a/modules/raycast/static_raycaster.h
+++ b/modules/raycast/static_raycaster.h
@@ -41,7 +41,7 @@ private:
static RTCDevice embree_device;
RTCScene embree_scene;
- Set<int> filter_meshes;
+ HashSet<int> filter_meshes;
public:
virtual bool intersect(Ray &p_ray) override;
@@ -50,7 +50,7 @@ public:
virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) override;
virtual void commit() override;
- virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override;
virtual void clear_mesh_filter() override;
static StaticRaycaster *create_embree_raycaster();
@@ -61,4 +61,4 @@ public:
~StaticRaycasterEmbree();
};
-#endif
+#endif // STATIC_RAYCASTER_H
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index ee1137b71f..67ce37219b 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -52,9 +52,9 @@ int RegExMatch::_find(const Variant &p_name) const {
return i;
} else if (p_name.get_type() == Variant::STRING) {
- const Map<String, int>::Element *found = names.find((String)p_name);
+ HashMap<String, int>::ConstIterator found = names.find((String)p_name);
if (found) {
- return found->value();
+ return found->value;
}
}
@@ -75,8 +75,8 @@ int RegExMatch::get_group_count() const {
Dictionary RegExMatch::get_names() const {
Dictionary result;
- for (const Map<String, int>::Element *i = names.front(); i != nullptr; i = i->next()) {
- result[i->key()] = i->value();
+ for (const KeyValue<String, int> &E : names) {
+ result[E.key] = E.value;
}
return result;
@@ -194,6 +194,7 @@ Error RegEx::compile(const String &p_pattern) {
Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) const {
ERR_FAIL_COND_V(!is_valid(), nullptr);
+ ERR_FAIL_COND_V_MSG(p_offset < 0, nullptr, "RegEx search offset must be >= 0");
Ref<RegExMatch> result = memnew(RegExMatch);
@@ -258,6 +259,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
}
Array RegEx::search_all(const String &p_subject, int p_offset, int p_end) const {
+ ERR_FAIL_COND_V_MSG(p_offset < 0, Array(), "RegEx search offset must be >= 0");
+
int last_end = -1;
Array result;
Ref<RegExMatch> match = search(p_subject, p_offset, p_end);
@@ -274,6 +277,7 @@ Array RegEx::search_all(const String &p_subject, int p_offset, int p_end) const
String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_all, int p_offset, int p_end) const {
ERR_FAIL_COND_V(!is_valid(), String());
+ ERR_FAIL_COND_V_MSG(p_offset < 0, String(), "RegEx sub offset must be >= 0");
// safety_zone is the number of chars we allocate in addition to the number of chars expected in order to
// guard against the PCRE API writing one additional \0 at the end. PCRE's API docs are unclear on whether
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
index e7221f4070..1455188670 100644
--- a/modules/regex/regex.h
+++ b/modules/regex/regex.h
@@ -33,7 +33,7 @@
#include "core/object/ref_counted.h"
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "core/variant/array.h"
#include "core/variant/dictionary.h"
@@ -48,7 +48,7 @@ class RegExMatch : public RefCounted {
String subject;
Vector<Range> data;
- Map<String, int> names;
+ HashMap<String, int> names;
friend class RegEx;
diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp
index 9289a724d8..2103c57f77 100644
--- a/modules/regex/register_types.cpp
+++ b/modules/regex/register_types.cpp
@@ -32,10 +32,17 @@
#include "core/object/class_db.h"
#include "regex.h"
-void register_regex_types() {
+void initialize_regex_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_CLASS(RegExMatch);
GDREGISTER_CLASS(RegEx);
}
-void unregister_regex_types() {
+void uninitialize_regex_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
index b2d5009ef4..c3edf23562 100644
--- a/modules/regex/register_types.h
+++ b/modules/regex/register_types.h
@@ -31,7 +31,9 @@
#ifndef REGEX_REGISTER_TYPES_H
#define REGEX_REGISTER_TYPES_H
-void register_regex_types();
-void unregister_regex_types();
+#include "modules/register_module_types.h"
+
+void initialize_regex_module(ModuleInitializationLevel p_level);
+void uninitialize_regex_module(ModuleInitializationLevel p_level);
#endif // REGEX_REGISTER_TYPES_H
diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h
index 3f500e7b2f..91af393db1 100644
--- a/modules/regex/tests/test_regex.h
+++ b/modules/regex/tests/test_regex.h
@@ -130,16 +130,6 @@ TEST_CASE("[RegEx] Empty Pattern") {
CHECK(re.is_valid());
}
-TEST_CASE("[RegEx] Invalid offset") {
- const String s = "Godot";
-
- RegEx re("o");
- REQUIRE(re.is_valid());
- CHECK(re.search(s, -1) == nullptr);
- CHECK(re.search_all(s, -1).size() == 0);
- CHECK(re.sub(s, "", true, -1) == "");
-}
-
TEST_CASE("[RegEx] Invalid end position") {
const String s = "Godot";
diff --git a/modules/register_module_types.h b/modules/register_module_types.h
index bc9aeb31ab..cfd1b355d4 100644
--- a/modules/register_module_types.h
+++ b/modules/register_module_types.h
@@ -31,8 +31,16 @@
#ifndef REGISTER_MODULE_TYPES_H
#define REGISTER_MODULE_TYPES_H
-void preregister_module_types();
-void register_module_types();
-void unregister_module_types();
+#include "core/extension/gdnative_interface.h"
+
+enum ModuleInitializationLevel {
+ MODULE_INITIALIZATION_LEVEL_CORE = GDNATIVE_INITIALIZATION_CORE,
+ MODULE_INITIALIZATION_LEVEL_SERVERS = GDNATIVE_INITIALIZATION_SERVERS,
+ MODULE_INITIALIZATION_LEVEL_SCENE = GDNATIVE_INITIALIZATION_SCENE,
+ MODULE_INITIALIZATION_LEVEL_EDITOR = GDNATIVE_INITIALIZATION_EDITOR
+};
+
+void initialize_modules(ModuleInitializationLevel p_level);
+void uninitialize_modules(ModuleInitializationLevel p_level);
#endif // REGISTER_MODULE_TYPES_H
diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp
index 1dc2e11ab9..a80419e0eb 100644
--- a/modules/squish/register_types.cpp
+++ b/modules/squish/register_types.cpp
@@ -32,8 +32,16 @@
#include "image_decompress_squish.h"
-void register_squish_types() {
+void initialize_squish_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
Image::_image_decompress_bc = image_decompress_squish;
}
-void unregister_squish_types() {}
+void uninitialize_squish_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h
index ed1c6b0716..5e9a4dfd50 100644
--- a/modules/squish/register_types.h
+++ b/modules/squish/register_types.h
@@ -31,7 +31,9 @@
#ifndef SQUISH_REGISTER_TYPES_H
#define SQUISH_REGISTER_TYPES_H
-void register_squish_types();
-void unregister_squish_types();
+#include "modules/register_module_types.h"
+
+void initialize_squish_module(ModuleInitializationLevel p_level);
+void uninitialize_squish_module(ModuleInitializationLevel p_level);
#endif // SQUISH_REGISTER_TYPES_H
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 7fe2e589b1..87e2fae2d0 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -80,8 +80,8 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
float fw, fh;
picture->size(&fw, &fh);
- uint32_t width = MIN(fw * p_scale, 16 * 1024);
- uint32_t height = MIN(fh * p_scale, 16 * 1024);
+ uint32_t width = MIN(round(fw * p_scale), 16 * 1024);
+ uint32_t height = MIN(round(fh * p_scale), 16 * 1024);
picture->size(width, height);
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
@@ -94,7 +94,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
}
- res = sw_canvas->push(move(picture));
+ res = sw_canvas->push(std::move(picture));
if (res != tvg::Result::Success) {
memfree(buffer);
ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index b59c815056..5b4d1d31ca 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -36,7 +36,11 @@
static ImageLoaderSVG *image_loader_svg = nullptr;
-void register_svg_types() {
+void initialize_svg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
if (tvg::Initializer::init(tvgEngine, 1) != tvg::Result::Success) {
return;
@@ -45,7 +49,11 @@ void register_svg_types() {
ImageLoader::add_image_format_loader(image_loader_svg);
}
-void unregister_svg_types() {
+void uninitialize_svg_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
if (!image_loader_svg) {
return;
}
diff --git a/modules/svg/register_types.h b/modules/svg/register_types.h
index 11fd9e7007..66f3e94600 100644
--- a/modules/svg/register_types.h
+++ b/modules/svg/register_types.h
@@ -31,7 +31,9 @@
#ifndef SVG_REGISTER_TYPES_H
#define SVG_REGISTER_TYPES_H
-void register_svg_types();
-void unregister_svg_types();
+#include "modules/register_module_types.h"
+
+void initialize_svg_module(ModuleInitializationLevel p_level);
+void uninitialize_svg_module(ModuleInitializationLevel p_level);
#endif // SVG_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 525d4d3efb..2d764a4006 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -75,18 +75,18 @@ if env["builtin_harfbuzz"]:
"src/hb-ot-meta.cc",
"src/hb-ot-metrics.cc",
"src/hb-ot-name.cc",
- "src/hb-ot-shape-complex-arabic.cc",
- "src/hb-ot-shape-complex-default.cc",
- "src/hb-ot-shape-complex-hangul.cc",
- "src/hb-ot-shape-complex-hebrew.cc",
- "src/hb-ot-shape-complex-indic-table.cc",
- "src/hb-ot-shape-complex-indic.cc",
- "src/hb-ot-shape-complex-khmer.cc",
- "src/hb-ot-shape-complex-myanmar.cc",
- "src/hb-ot-shape-complex-syllabic.cc",
- "src/hb-ot-shape-complex-thai.cc",
- "src/hb-ot-shape-complex-use.cc",
- "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shaper-arabic.cc",
+ "src/hb-ot-shaper-default.cc",
+ "src/hb-ot-shaper-hangul.cc",
+ "src/hb-ot-shaper-hebrew.cc",
+ "src/hb-ot-shaper-indic-table.cc",
+ "src/hb-ot-shaper-indic.cc",
+ "src/hb-ot-shaper-khmer.cc",
+ "src/hb-ot-shaper-myanmar.cc",
+ "src/hb-ot-shaper-syllabic.cc",
+ "src/hb-ot-shaper-thai.cc",
+ "src/hb-ot-shaper-use.cc",
+ "src/hb-ot-shaper-vowel-constraints.cc",
"src/hb-ot-shape-fallback.cc",
"src/hb-ot-shape-normalize.cc",
"src/hb-ot-shape.cc",
@@ -442,7 +442,7 @@ if env["builtin_icu"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- icu_data_name = "icudt70l.dat"
+ icu_data_name = "icudt71l.dat"
if env_icu["tools"]:
env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name)
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 1c38398c88..0170c007ae 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -116,6 +116,7 @@ if env["freetype_enabled"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -164,7 +165,14 @@ if env["freetype_enabled"]:
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)])
+ env_freetype.Append(
+ CPPDEFINES=[
+ "FT2_BUILD_LIBRARY",
+ "FT_CONFIG_OPTION_USE_PNG",
+ ("PNG_ARM_NEON_OPT", 0),
+ "FT_CONFIG_OPTION_SYSTEM_ZLIB",
+ ]
+ )
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
@@ -212,18 +220,18 @@ thirdparty_harfbuzz_sources = [
"src/hb-ot-meta.cc",
"src/hb-ot-metrics.cc",
"src/hb-ot-name.cc",
- "src/hb-ot-shape-complex-arabic.cc",
- "src/hb-ot-shape-complex-default.cc",
- "src/hb-ot-shape-complex-hangul.cc",
- "src/hb-ot-shape-complex-hebrew.cc",
- "src/hb-ot-shape-complex-indic-table.cc",
- "src/hb-ot-shape-complex-indic.cc",
- "src/hb-ot-shape-complex-khmer.cc",
- "src/hb-ot-shape-complex-myanmar.cc",
- "src/hb-ot-shape-complex-syllabic.cc",
- "src/hb-ot-shape-complex-thai.cc",
- "src/hb-ot-shape-complex-use.cc",
- "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shaper-arabic.cc",
+ "src/hb-ot-shaper-default.cc",
+ "src/hb-ot-shaper-hangul.cc",
+ "src/hb-ot-shaper-hebrew.cc",
+ "src/hb-ot-shaper-indic-table.cc",
+ "src/hb-ot-shaper-indic.cc",
+ "src/hb-ot-shaper-khmer.cc",
+ "src/hb-ot-shaper-myanmar.cc",
+ "src/hb-ot-shaper-syllabic.cc",
+ "src/hb-ot-shaper-thai.cc",
+ "src/hb-ot-shaper-use.cc",
+ "src/hb-ot-shaper-vowel-constraints.cc",
"src/hb-ot-shape-fallback.cc",
"src/hb-ot-shape-normalize.cc",
"src/hb-ot-shape.cc",
@@ -564,7 +572,7 @@ thirdparty_icu_sources = [
]
thirdparty_icu_sources = [thirdparty_icu_dir + file for file in thirdparty_icu_sources]
-icu_data_name = "icudt70l.dat"
+icu_data_name = "icudt71l.dat"
if env["static_icu_data"]:
env_icu.Depends("../../../thirdparty/icu4c/icudata.gen.h", "../../../thirdparty/icu4c/" + icu_data_name)
@@ -616,15 +624,15 @@ env.Append(CPPDEFINES=["GDEXTENSION"])
env.Append(CPPPATH=["../"])
sources = Glob("../*.cpp")
-if env["platform"] == "osx":
- methods.write_osx_plist(
- f'./bin/libtextserver_advanced.osx.{env["target"]}.framework',
- f'libtextserver_advanced.osx.{env["target"]}',
+if env["platform"] == "macos":
+ methods.write_macos_plist(
+ f'./bin/libtextserver_advanced.macos.{env["target"]}.framework',
+ f'libtextserver_advanced.macos.{env["target"]}',
"org.godotengine.textserver_advanced",
"ICU / HarfBuzz / Graphite Text Server",
)
library = env.SharedLibrary(
- f'./bin/libtextserver_advanced.osx.{env["target"]}.framework/libtextserver_advanced.osx.{env["target"]}',
+ f'./bin/libtextserver_advanced.macos.{env["target"]}.framework/libtextserver_advanced.macos.{env["target"]}',
source=sources,
)
else:
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index d404f2851e..3c5229462c 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -98,7 +98,7 @@ def make_icu_data(target, source, env):
g.write("#endif")
-def write_osx_plist(target, binary_name, identifier, name):
+def write_macos_plist(target, binary_name, identifier, name):
os.makedirs(f"{target}/Resourece/", exist_ok=True)
f = open(f"{target}/Resourece/Info.plist", "w")
diff --git a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension
index 5956476a5e..11ed271ae9 100644
--- a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension
+++ b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension
@@ -8,5 +8,5 @@ linux.64.debug = "bin/libtextserver_advanced.linux.debug.64.so"
linux.64.release = "bin/libtextserver_advanced.linux.release.64.so"
windows.64.debug = "bin/libtextserver_advanced.windows.debug.64.dll"
windows.64.release = "bin/libtextserver_advanced.windows.release.64.dll"
-macos.debug = "bin/libtextserver_advanced.osx.debug.framework"
-macos.release = "bin/libtextserver_advanced.osx.release.framework"
+macos.debug = "bin/libtextserver_advanced.macos.debug.framework"
+macos.release = "bin/libtextserver_advanced.macos.release.framework"
diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp
index ed00bdfbf9..6a26584506 100644
--- a/modules/text_server_adv/register_types.cpp
+++ b/modules/text_server_adv/register_types.cpp
@@ -32,7 +32,11 @@
#include "text_server_adv.h"
-void preregister_text_server_adv_types() {
+void initialize_text_server_adv_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ return;
+ }
+
GDREGISTER_CLASS(TextServerAdvanced);
TextServerManager *tsman = TextServerManager::get_singleton();
if (tsman) {
@@ -42,10 +46,10 @@ void preregister_text_server_adv_types() {
}
}
-void register_text_server_adv_types() {
-}
-
-void unregister_text_server_adv_types() {
+void uninitialize_text_server_adv_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ return;
+ }
}
#ifdef GDEXTENSION
@@ -61,8 +65,9 @@ extern "C" {
GDNativeBool GDN_EXPORT textserver_advanced_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) {
GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
- init_obj.register_server_initializer(&preregister_text_server_adv_types);
- init_obj.register_server_terminator(&unregister_text_server_adv_types);
+ init_obj.register_initializer(&initialize_text_server_adv_module);
+ init_obj.register_terminator(&uninitialize_text_server_adv_module);
+ init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SERVERS);
return init_obj.init();
}
diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h
index d2b49fce6e..dfe20c860c 100644
--- a/modules/text_server_adv/register_types.h
+++ b/modules/text_server_adv/register_types.h
@@ -31,10 +31,14 @@
#ifndef TEXT_SERVER_ADV_REGISTER_TYPES_H
#define TEXT_SERVER_ADV_REGISTER_TYPES_H
-#define MODULE_TEXT_SERVER_ADV_HAS_PREREGISTER
+#ifdef GDEXTENSION
+#include <godot_cpp/core/class_db.hpp>
+using namespace godot;
+#else
+#include "modules/register_module_types.h"
+#endif
-void preregister_text_server_adv_types();
-void register_text_server_adv_types();
-void unregister_text_server_adv_types();
+void initialize_text_server_adv_module(ModuleInitializationLevel p_level);
+void uninitialize_text_server_adv_module(ModuleInitializationLevel p_level);
#endif // TEXT_SERVER_ADV_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h
index 2bd045b91a..025b62c6fb 100644
--- a/modules/text_server_adv/script_iterator.h
+++ b/modules/text_server_adv/script_iterator.h
@@ -75,4 +75,4 @@ public:
ScriptIterator(const String &p_string, int p_start, int p_length);
};
-#endif //SCRIPT_ITERATOR_H
+#endif // SCRIPT_ITERATOR_H
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 437fbe76ab..fa234081f0 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "text_server_adv.h"
+#include "core/object/worker_thread_pool.h"
#ifdef GDEXTENSION
// Headers for building as GDExtension plug-in.
@@ -75,7 +76,7 @@ using namespace core_bind;
hb_font_funcs_t *TextServerAdvanced::funcs = nullptr;
-TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref) {
bmp_font_t *bm_font = memnew(bmp_font_t);
if (!bm_font) {
@@ -228,11 +229,11 @@ void TextServerAdvanced::_bmp_free_font_funcs() {
}
}
-void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref) {
hb_font_set_funcs(p_font, funcs, _bmp_font_create(p_face, p_unref), _bmp_font_destroy);
}
-hb_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
+hb_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
hb_font_t *font;
hb_face_t *face = hb_face_create(nullptr, 0);
@@ -375,7 +376,7 @@ int64_t TextServerAdvanced::get_features() const {
void TextServerAdvanced::free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
@@ -476,259 +477,267 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const {
}
}
-_FORCE_INLINE_ void TextServerAdvanced::_insert_feature(const StringName &p_name, int32_t p_tag) {
+_FORCE_INLINE_ void TextServerAdvanced::_insert_feature(const StringName &p_name, int32_t p_tag, Variant::Type p_vtype, bool p_hidden) {
+ FeatureInfo fi;
+ fi.name = p_name;
+ fi.vtype = p_vtype;
+ fi.hidden = p_hidden;
+
feature_sets.insert(p_name, p_tag);
- feature_sets_inv.insert(p_tag, p_name);
+ feature_sets_inv.insert(p_tag, fi);
}
void TextServerAdvanced::_insert_feature_sets() {
// Registered OpenType feature tags.
- _insert_feature("access_all_alternates", HB_TAG('a', 'a', 'l', 't'));
- _insert_feature("above_base_forms", HB_TAG('a', 'b', 'v', 'f'));
- _insert_feature("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'));
- _insert_feature("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'));
- _insert_feature("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'));
- _insert_feature("akhands", HB_TAG('a', 'k', 'h', 'n'));
- _insert_feature("below_base_forms", HB_TAG('b', 'l', 'w', 'f'));
- _insert_feature("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'));
- _insert_feature("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'));
- _insert_feature("contextual_alternates", HB_TAG('c', 'a', 'l', 't'));
- _insert_feature("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'));
- _insert_feature("glyph_composition", HB_TAG('c', 'c', 'm', 'p'));
- _insert_feature("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'));
- _insert_feature("conjunct_forms", HB_TAG('c', 'j', 'c', 't'));
- _insert_feature("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'));
- _insert_feature("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'));
- _insert_feature("capital_spacing", HB_TAG('c', 'p', 's', 'p'));
- _insert_feature("contextual_swash", HB_TAG('c', 's', 'w', 'h'));
- _insert_feature("cursive_positioning", HB_TAG('c', 'u', 'r', 's'));
- _insert_feature("character_variant_01", HB_TAG('c', 'v', '0', '1'));
- _insert_feature("character_variant_02", HB_TAG('c', 'v', '0', '2'));
- _insert_feature("character_variant_03", HB_TAG('c', 'v', '0', '3'));
- _insert_feature("character_variant_04", HB_TAG('c', 'v', '0', '4'));
- _insert_feature("character_variant_05", HB_TAG('c', 'v', '0', '5'));
- _insert_feature("character_variant_06", HB_TAG('c', 'v', '0', '6'));
- _insert_feature("character_variant_07", HB_TAG('c', 'v', '0', '7'));
- _insert_feature("character_variant_08", HB_TAG('c', 'v', '0', '8'));
- _insert_feature("character_variant_09", HB_TAG('c', 'v', '0', '9'));
- _insert_feature("character_variant_10", HB_TAG('c', 'v', '1', '0'));
- _insert_feature("character_variant_11", HB_TAG('c', 'v', '1', '1'));
- _insert_feature("character_variant_12", HB_TAG('c', 'v', '1', '2'));
- _insert_feature("character_variant_13", HB_TAG('c', 'v', '1', '3'));
- _insert_feature("character_variant_14", HB_TAG('c', 'v', '1', '4'));
- _insert_feature("character_variant_15", HB_TAG('c', 'v', '1', '5'));
- _insert_feature("character_variant_16", HB_TAG('c', 'v', '1', '6'));
- _insert_feature("character_variant_17", HB_TAG('c', 'v', '1', '7'));
- _insert_feature("character_variant_18", HB_TAG('c', 'v', '1', '8'));
- _insert_feature("character_variant_19", HB_TAG('c', 'v', '1', '9'));
- _insert_feature("character_variant_20", HB_TAG('c', 'v', '2', '0'));
- _insert_feature("character_variant_21", HB_TAG('c', 'v', '2', '1'));
- _insert_feature("character_variant_22", HB_TAG('c', 'v', '2', '2'));
- _insert_feature("character_variant_23", HB_TAG('c', 'v', '2', '3'));
- _insert_feature("character_variant_24", HB_TAG('c', 'v', '2', '4'));
- _insert_feature("character_variant_25", HB_TAG('c', 'v', '2', '5'));
- _insert_feature("character_variant_26", HB_TAG('c', 'v', '2', '6'));
- _insert_feature("character_variant_27", HB_TAG('c', 'v', '2', '7'));
- _insert_feature("character_variant_28", HB_TAG('c', 'v', '2', '8'));
- _insert_feature("character_variant_29", HB_TAG('c', 'v', '2', '9'));
- _insert_feature("character_variant_30", HB_TAG('c', 'v', '3', '0'));
- _insert_feature("character_variant_31", HB_TAG('c', 'v', '3', '1'));
- _insert_feature("character_variant_32", HB_TAG('c', 'v', '3', '2'));
- _insert_feature("character_variant_33", HB_TAG('c', 'v', '3', '3'));
- _insert_feature("character_variant_34", HB_TAG('c', 'v', '3', '4'));
- _insert_feature("character_variant_35", HB_TAG('c', 'v', '3', '5'));
- _insert_feature("character_variant_36", HB_TAG('c', 'v', '3', '6'));
- _insert_feature("character_variant_37", HB_TAG('c', 'v', '3', '7'));
- _insert_feature("character_variant_38", HB_TAG('c', 'v', '3', '8'));
- _insert_feature("character_variant_39", HB_TAG('c', 'v', '3', '9'));
- _insert_feature("character_variant_40", HB_TAG('c', 'v', '4', '0'));
- _insert_feature("character_variant_41", HB_TAG('c', 'v', '4', '1'));
- _insert_feature("character_variant_42", HB_TAG('c', 'v', '4', '2'));
- _insert_feature("character_variant_43", HB_TAG('c', 'v', '4', '3'));
- _insert_feature("character_variant_44", HB_TAG('c', 'v', '4', '4'));
- _insert_feature("character_variant_45", HB_TAG('c', 'v', '4', '5'));
- _insert_feature("character_variant_46", HB_TAG('c', 'v', '4', '6'));
- _insert_feature("character_variant_47", HB_TAG('c', 'v', '4', '7'));
- _insert_feature("character_variant_48", HB_TAG('c', 'v', '4', '8'));
- _insert_feature("character_variant_49", HB_TAG('c', 'v', '4', '9'));
- _insert_feature("character_variant_50", HB_TAG('c', 'v', '5', '0'));
- _insert_feature("character_variant_51", HB_TAG('c', 'v', '5', '1'));
- _insert_feature("character_variant_52", HB_TAG('c', 'v', '5', '2'));
- _insert_feature("character_variant_53", HB_TAG('c', 'v', '5', '3'));
- _insert_feature("character_variant_54", HB_TAG('c', 'v', '5', '4'));
- _insert_feature("character_variant_55", HB_TAG('c', 'v', '5', '5'));
- _insert_feature("character_variant_56", HB_TAG('c', 'v', '5', '6'));
- _insert_feature("character_variant_57", HB_TAG('c', 'v', '5', '7'));
- _insert_feature("character_variant_58", HB_TAG('c', 'v', '5', '8'));
- _insert_feature("character_variant_59", HB_TAG('c', 'v', '5', '9'));
- _insert_feature("character_variant_60", HB_TAG('c', 'v', '6', '0'));
- _insert_feature("character_variant_61", HB_TAG('c', 'v', '6', '1'));
- _insert_feature("character_variant_62", HB_TAG('c', 'v', '6', '2'));
- _insert_feature("character_variant_63", HB_TAG('c', 'v', '6', '3'));
- _insert_feature("character_variant_64", HB_TAG('c', 'v', '6', '4'));
- _insert_feature("character_variant_65", HB_TAG('c', 'v', '6', '5'));
- _insert_feature("character_variant_66", HB_TAG('c', 'v', '6', '6'));
- _insert_feature("character_variant_67", HB_TAG('c', 'v', '6', '7'));
- _insert_feature("character_variant_68", HB_TAG('c', 'v', '6', '8'));
- _insert_feature("character_variant_69", HB_TAG('c', 'v', '6', '9'));
- _insert_feature("character_variant_70", HB_TAG('c', 'v', '7', '0'));
- _insert_feature("character_variant_71", HB_TAG('c', 'v', '7', '1'));
- _insert_feature("character_variant_72", HB_TAG('c', 'v', '7', '2'));
- _insert_feature("character_variant_73", HB_TAG('c', 'v', '7', '3'));
- _insert_feature("character_variant_74", HB_TAG('c', 'v', '7', '4'));
- _insert_feature("character_variant_75", HB_TAG('c', 'v', '7', '5'));
- _insert_feature("character_variant_76", HB_TAG('c', 'v', '7', '6'));
- _insert_feature("character_variant_77", HB_TAG('c', 'v', '7', '7'));
- _insert_feature("character_variant_78", HB_TAG('c', 'v', '7', '8'));
- _insert_feature("character_variant_79", HB_TAG('c', 'v', '7', '9'));
- _insert_feature("character_variant_80", HB_TAG('c', 'v', '8', '0'));
- _insert_feature("character_variant_81", HB_TAG('c', 'v', '8', '1'));
- _insert_feature("character_variant_82", HB_TAG('c', 'v', '8', '2'));
- _insert_feature("character_variant_83", HB_TAG('c', 'v', '8', '3'));
- _insert_feature("character_variant_84", HB_TAG('c', 'v', '8', '4'));
- _insert_feature("character_variant_85", HB_TAG('c', 'v', '8', '5'));
- _insert_feature("character_variant_86", HB_TAG('c', 'v', '8', '6'));
- _insert_feature("character_variant_87", HB_TAG('c', 'v', '8', '7'));
- _insert_feature("character_variant_88", HB_TAG('c', 'v', '8', '8'));
- _insert_feature("character_variant_89", HB_TAG('c', 'v', '8', '9'));
- _insert_feature("character_variant_90", HB_TAG('c', 'v', '9', '0'));
- _insert_feature("character_variant_91", HB_TAG('c', 'v', '9', '1'));
- _insert_feature("character_variant_92", HB_TAG('c', 'v', '9', '2'));
- _insert_feature("character_variant_93", HB_TAG('c', 'v', '9', '3'));
- _insert_feature("character_variant_94", HB_TAG('c', 'v', '9', '4'));
- _insert_feature("character_variant_95", HB_TAG('c', 'v', '9', '5'));
- _insert_feature("character_variant_96", HB_TAG('c', 'v', '9', '6'));
- _insert_feature("character_variant_97", HB_TAG('c', 'v', '9', '7'));
- _insert_feature("character_variant_98", HB_TAG('c', 'v', '9', '8'));
- _insert_feature("character_variant_99", HB_TAG('c', 'v', '9', '9'));
- _insert_feature("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'));
- _insert_feature("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'));
- _insert_feature("distances", HB_TAG('d', 'i', 's', 't'));
- _insert_feature("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'));
- _insert_feature("denominators", HB_TAG('d', 'n', 'o', 'm'));
- _insert_feature("dotless_forms", HB_TAG('d', 't', 'l', 's'));
- _insert_feature("expert_forms", HB_TAG('e', 'x', 'p', 't'));
- _insert_feature("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'));
- _insert_feature("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'));
- _insert_feature("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'));
- _insert_feature("terminal_forms", HB_TAG('f', 'i', 'n', 'a'));
- _insert_feature("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'));
- _insert_feature("fractions", HB_TAG('f', 'r', 'a', 'c'));
- _insert_feature("full_widths", HB_TAG('f', 'w', 'i', 'd'));
- _insert_feature("half_forms", HB_TAG('h', 'a', 'l', 'f'));
- _insert_feature("halant_forms", HB_TAG('h', 'a', 'l', 'n'));
- _insert_feature("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'));
- _insert_feature("historical_forms", HB_TAG('h', 'i', 's', 't'));
- _insert_feature("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'));
- _insert_feature("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'));
- _insert_feature("hangul", HB_TAG('h', 'n', 'g', 'l'));
- _insert_feature("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'));
- _insert_feature("half_widths", HB_TAG('h', 'w', 'i', 'd'));
- _insert_feature("initial_forms", HB_TAG('i', 'n', 'i', 't'));
- _insert_feature("isolated_forms", HB_TAG('i', 's', 'o', 'l'));
- _insert_feature("italics", HB_TAG('i', 't', 'a', 'l'));
- _insert_feature("justification_alternates", HB_TAG('j', 'a', 'l', 't'));
- _insert_feature("jis78_forms", HB_TAG('j', 'p', '7', '8'));
- _insert_feature("jis83_forms", HB_TAG('j', 'p', '8', '3'));
- _insert_feature("jis90_forms", HB_TAG('j', 'p', '9', '0'));
- _insert_feature("jis2004_forms", HB_TAG('j', 'p', '0', '4'));
- _insert_feature("kerning", HB_TAG('k', 'e', 'r', 'n'));
- _insert_feature("left_bounds", HB_TAG('l', 'f', 'b', 'd'));
- _insert_feature("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'));
- _insert_feature("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'));
- _insert_feature("lining_figures", HB_TAG('l', 'n', 'u', 'm'));
- _insert_feature("localized_forms", HB_TAG('l', 'o', 'c', 'l'));
- _insert_feature("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'));
- _insert_feature("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'));
- _insert_feature("mark_positioning", HB_TAG('m', 'a', 'r', 'k'));
- _insert_feature("medial_forms_2", HB_TAG('m', 'e', 'd', '2'));
- _insert_feature("medial_forms", HB_TAG('m', 'e', 'd', 'i'));
- _insert_feature("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'));
- _insert_feature("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'));
- _insert_feature("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'));
- _insert_feature("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'));
- _insert_feature("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'));
- _insert_feature("nukta_forms", HB_TAG('n', 'u', 'k', 't'));
- _insert_feature("numerators", HB_TAG('n', 'u', 'm', 'r'));
- _insert_feature("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'));
- _insert_feature("optical_bounds", HB_TAG('o', 'p', 'b', 'd'));
- _insert_feature("ordinals", HB_TAG('o', 'r', 'd', 'n'));
- _insert_feature("ornaments", HB_TAG('o', 'r', 'n', 'm'));
- _insert_feature("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'));
- _insert_feature("petite_capitals", HB_TAG('p', 'c', 'a', 'p'));
- _insert_feature("proportional_kana", HB_TAG('p', 'k', 'n', 'a'));
- _insert_feature("proportional_figures", HB_TAG('p', 'n', 'u', 'm'));
- _insert_feature("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'));
- _insert_feature("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'));
- _insert_feature("post_base_forms", HB_TAG('p', 's', 't', 'f'));
- _insert_feature("post_base_substitutions", HB_TAG('p', 's', 't', 's'));
- _insert_feature("proportional_widths", HB_TAG('p', 'w', 'i', 'd'));
- _insert_feature("quarter_widths", HB_TAG('q', 'w', 'i', 'd'));
- _insert_feature("randomize", HB_TAG('r', 'a', 'n', 'd'));
- _insert_feature("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'));
- _insert_feature("rakar_forms", HB_TAG('r', 'k', 'r', 'f'));
- _insert_feature("required_ligatures", HB_TAG('r', 'l', 'i', 'g'));
- _insert_feature("reph_forms", HB_TAG('r', 'p', 'h', 'f'));
- _insert_feature("right_bounds", HB_TAG('r', 't', 'b', 'd'));
- _insert_feature("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'));
- _insert_feature("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'));
- _insert_feature("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'));
- _insert_feature("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'));
- _insert_feature("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'));
- _insert_feature("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'));
- _insert_feature("optical_size", HB_TAG('s', 'i', 'z', 'e'));
- _insert_feature("small_capitals", HB_TAG('s', 'm', 'c', 'p'));
- _insert_feature("simplified_forms", HB_TAG('s', 'm', 'p', 'l'));
- _insert_feature("stylistic_set_01", HB_TAG('s', 's', '0', '1'));
- _insert_feature("stylistic_set_02", HB_TAG('s', 's', '0', '2'));
- _insert_feature("stylistic_set_03", HB_TAG('s', 's', '0', '3'));
- _insert_feature("stylistic_set_04", HB_TAG('s', 's', '0', '4'));
- _insert_feature("stylistic_set_05", HB_TAG('s', 's', '0', '5'));
- _insert_feature("stylistic_set_06", HB_TAG('s', 's', '0', '6'));
- _insert_feature("stylistic_set_07", HB_TAG('s', 's', '0', '7'));
- _insert_feature("stylistic_set_08", HB_TAG('s', 's', '0', '8'));
- _insert_feature("stylistic_set_09", HB_TAG('s', 's', '0', '9'));
- _insert_feature("stylistic_set_10", HB_TAG('s', 's', '1', '0'));
- _insert_feature("stylistic_set_11", HB_TAG('s', 's', '1', '1'));
- _insert_feature("stylistic_set_12", HB_TAG('s', 's', '1', '2'));
- _insert_feature("stylistic_set_13", HB_TAG('s', 's', '1', '3'));
- _insert_feature("stylistic_set_14", HB_TAG('s', 's', '1', '4'));
- _insert_feature("stylistic_set_15", HB_TAG('s', 's', '1', '5'));
- _insert_feature("stylistic_set_16", HB_TAG('s', 's', '1', '6'));
- _insert_feature("stylistic_set_17", HB_TAG('s', 's', '1', '7'));
- _insert_feature("stylistic_set_18", HB_TAG('s', 's', '1', '8'));
- _insert_feature("stylistic_set_19", HB_TAG('s', 's', '1', '9'));
- _insert_feature("stylistic_set_20", HB_TAG('s', 's', '2', '0'));
- _insert_feature("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'));
- _insert_feature("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'));
- _insert_feature("subscript", HB_TAG('s', 'u', 'b', 's'));
- _insert_feature("superscript", HB_TAG('s', 'u', 'p', 's'));
- _insert_feature("swash", HB_TAG('s', 'w', 's', 'h'));
- _insert_feature("titling", HB_TAG('t', 'i', 't', 'l'));
- _insert_feature("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'));
- _insert_feature("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'));
- _insert_feature("tabular_figures", HB_TAG('t', 'n', 'u', 'm'));
- _insert_feature("traditional_forms", HB_TAG('t', 'r', 'a', 'd'));
- _insert_feature("third_widths", HB_TAG('t', 'w', 'i', 'd'));
- _insert_feature("unicase", HB_TAG('u', 'n', 'i', 'c'));
- _insert_feature("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'));
- _insert_feature("vattu_variants", HB_TAG('v', 'a', 't', 'u'));
- _insert_feature("vertical_writing", HB_TAG('v', 'e', 'r', 't'));
- _insert_feature("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'));
- _insert_feature("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'));
- _insert_feature("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'));
- _insert_feature("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'));
- _insert_feature("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'));
- _insert_feature("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'));
- _insert_feature("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'));
- _insert_feature("slashed_zero", HB_TAG('z', 'e', 'r', 'o'));
+ // Name, Tag, Data Type, Hidden
+ _insert_feature("access_all_alternates", HB_TAG('a', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("above_base_forms", HB_TAG('a', 'b', 'v', 'f'), Variant::Type::INT, true);
+ _insert_feature("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'), Variant::Type::INT, true);
+ _insert_feature("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'), Variant::Type::INT, true);
+ _insert_feature("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'), Variant::Type::INT, false);
+ _insert_feature("akhands", HB_TAG('a', 'k', 'h', 'n'), Variant::Type::INT, true);
+ _insert_feature("below_base_forms", HB_TAG('b', 'l', 'w', 'f'), Variant::Type::INT, true);
+ _insert_feature("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'), Variant::Type::INT, true);
+ _insert_feature("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'), Variant::Type::INT, true);
+ _insert_feature("contextual_alternates", HB_TAG('c', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'), Variant::Type::BOOL, false);
+ _insert_feature("glyph_composition", HB_TAG('c', 'c', 'm', 'p'), Variant::Type::INT, true);
+ _insert_feature("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'), Variant::Type::INT, true);
+ _insert_feature("contextual_half_width_spacing", HB_TAG('c', 'h', 'w', 's'), Variant::Type::INT, true);
+ _insert_feature("conjunct_forms", HB_TAG('c', 'j', 'c', 't'), Variant::Type::INT, true);
+ _insert_feature("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'), Variant::Type::BOOL, false);
+ _insert_feature("capital_spacing", HB_TAG('c', 'p', 's', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("contextual_swash", HB_TAG('c', 's', 'w', 'h'), Variant::Type::INT, false);
+ _insert_feature("cursive_positioning", HB_TAG('c', 'u', 'r', 's'), Variant::Type::INT, true);
+ _insert_feature("character_variant_01", HB_TAG('c', 'v', '0', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_02", HB_TAG('c', 'v', '0', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_03", HB_TAG('c', 'v', '0', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_04", HB_TAG('c', 'v', '0', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_05", HB_TAG('c', 'v', '0', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_06", HB_TAG('c', 'v', '0', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_07", HB_TAG('c', 'v', '0', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_08", HB_TAG('c', 'v', '0', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_09", HB_TAG('c', 'v', '0', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_10", HB_TAG('c', 'v', '1', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_11", HB_TAG('c', 'v', '1', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_12", HB_TAG('c', 'v', '1', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_13", HB_TAG('c', 'v', '1', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_14", HB_TAG('c', 'v', '1', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_15", HB_TAG('c', 'v', '1', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_16", HB_TAG('c', 'v', '1', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_17", HB_TAG('c', 'v', '1', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_18", HB_TAG('c', 'v', '1', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_19", HB_TAG('c', 'v', '1', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_20", HB_TAG('c', 'v', '2', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_21", HB_TAG('c', 'v', '2', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_22", HB_TAG('c', 'v', '2', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_23", HB_TAG('c', 'v', '2', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_24", HB_TAG('c', 'v', '2', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_25", HB_TAG('c', 'v', '2', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_26", HB_TAG('c', 'v', '2', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_27", HB_TAG('c', 'v', '2', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_28", HB_TAG('c', 'v', '2', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_29", HB_TAG('c', 'v', '2', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_30", HB_TAG('c', 'v', '3', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_31", HB_TAG('c', 'v', '3', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_32", HB_TAG('c', 'v', '3', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_33", HB_TAG('c', 'v', '3', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_34", HB_TAG('c', 'v', '3', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_35", HB_TAG('c', 'v', '3', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_36", HB_TAG('c', 'v', '3', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_37", HB_TAG('c', 'v', '3', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_38", HB_TAG('c', 'v', '3', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_39", HB_TAG('c', 'v', '3', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_40", HB_TAG('c', 'v', '4', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_41", HB_TAG('c', 'v', '4', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_42", HB_TAG('c', 'v', '4', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_43", HB_TAG('c', 'v', '4', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_44", HB_TAG('c', 'v', '4', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_45", HB_TAG('c', 'v', '4', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_46", HB_TAG('c', 'v', '4', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_47", HB_TAG('c', 'v', '4', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_48", HB_TAG('c', 'v', '4', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_49", HB_TAG('c', 'v', '4', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_50", HB_TAG('c', 'v', '5', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_51", HB_TAG('c', 'v', '5', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_52", HB_TAG('c', 'v', '5', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_53", HB_TAG('c', 'v', '5', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_54", HB_TAG('c', 'v', '5', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_55", HB_TAG('c', 'v', '5', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_56", HB_TAG('c', 'v', '5', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_57", HB_TAG('c', 'v', '5', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_58", HB_TAG('c', 'v', '5', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_59", HB_TAG('c', 'v', '5', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_60", HB_TAG('c', 'v', '6', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_61", HB_TAG('c', 'v', '6', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_62", HB_TAG('c', 'v', '6', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_63", HB_TAG('c', 'v', '6', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_64", HB_TAG('c', 'v', '6', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_65", HB_TAG('c', 'v', '6', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_66", HB_TAG('c', 'v', '6', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_67", HB_TAG('c', 'v', '6', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_68", HB_TAG('c', 'v', '6', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_69", HB_TAG('c', 'v', '6', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_70", HB_TAG('c', 'v', '7', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_71", HB_TAG('c', 'v', '7', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_72", HB_TAG('c', 'v', '7', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_73", HB_TAG('c', 'v', '7', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_74", HB_TAG('c', 'v', '7', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_75", HB_TAG('c', 'v', '7', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_76", HB_TAG('c', 'v', '7', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_77", HB_TAG('c', 'v', '7', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_78", HB_TAG('c', 'v', '7', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_79", HB_TAG('c', 'v', '7', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_80", HB_TAG('c', 'v', '8', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_81", HB_TAG('c', 'v', '8', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_82", HB_TAG('c', 'v', '8', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_83", HB_TAG('c', 'v', '8', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_84", HB_TAG('c', 'v', '8', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_85", HB_TAG('c', 'v', '8', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_86", HB_TAG('c', 'v', '8', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_87", HB_TAG('c', 'v', '8', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_88", HB_TAG('c', 'v', '8', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_89", HB_TAG('c', 'v', '8', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_90", HB_TAG('c', 'v', '9', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_91", HB_TAG('c', 'v', '9', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_92", HB_TAG('c', 'v', '9', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_93", HB_TAG('c', 'v', '9', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_94", HB_TAG('c', 'v', '9', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_95", HB_TAG('c', 'v', '9', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_96", HB_TAG('c', 'v', '9', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_97", HB_TAG('c', 'v', '9', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_98", HB_TAG('c', 'v', '9', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_99", HB_TAG('c', 'v', '9', '9'), Variant::Type::BOOL, false);
+ _insert_feature("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("distances", HB_TAG('d', 'i', 's', 't'), Variant::Type::INT, true);
+ _insert_feature("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("denominators", HB_TAG('d', 'n', 'o', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("dotless_forms", HB_TAG('d', 't', 'l', 's'), Variant::Type::INT, true);
+ _insert_feature("expert_forms", HB_TAG('e', 'x', 'p', 't'), Variant::Type::BOOL, true);
+ _insert_feature("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'), Variant::Type::INT, true);
+ _insert_feature("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'), Variant::Type::INT, true);
+ _insert_feature("terminal_forms", HB_TAG('f', 'i', 'n', 'a'), Variant::Type::INT, true);
+ _insert_feature("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'), Variant::Type::INT, true);
+ _insert_feature("fractions", HB_TAG('f', 'r', 'a', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("full_widths", HB_TAG('f', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("half_forms", HB_TAG('h', 'a', 'l', 'f'), Variant::Type::INT, true);
+ _insert_feature("halant_forms", HB_TAG('h', 'a', 'l', 'n'), Variant::Type::INT, true);
+ _insert_feature("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("historical_forms", HB_TAG('h', 'i', 's', 't'), Variant::Type::INT, false);
+ _insert_feature("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("hangul", HB_TAG('h', 'n', 'g', 'l'), Variant::Type::INT, false);
+ _insert_feature("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'), Variant::Type::INT, false);
+ _insert_feature("half_widths", HB_TAG('h', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("initial_forms", HB_TAG('i', 'n', 'i', 't'), Variant::Type::INT, true);
+ _insert_feature("isolated_forms", HB_TAG('i', 's', 'o', 'l'), Variant::Type::INT, true);
+ _insert_feature("italics", HB_TAG('i', 't', 'a', 'l'), Variant::Type::INT, false);
+ _insert_feature("justification_alternates", HB_TAG('j', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("jis78_forms", HB_TAG('j', 'p', '7', '8'), Variant::Type::INT, false);
+ _insert_feature("jis83_forms", HB_TAG('j', 'p', '8', '3'), Variant::Type::INT, false);
+ _insert_feature("jis90_forms", HB_TAG('j', 'p', '9', '0'), Variant::Type::INT, false);
+ _insert_feature("jis2004_forms", HB_TAG('j', 'p', '0', '4'), Variant::Type::INT, false);
+ _insert_feature("kerning", HB_TAG('k', 'e', 'r', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("left_bounds", HB_TAG('l', 'f', 'b', 'd'), Variant::Type::INT, false);
+ _insert_feature("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("lining_figures", HB_TAG('l', 'n', 'u', 'm'), Variant::Type::INT, false);
+ _insert_feature("localized_forms", HB_TAG('l', 'o', 'c', 'l'), Variant::Type::INT, true);
+ _insert_feature("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'), Variant::Type::INT, true);
+ _insert_feature("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'), Variant::Type::INT, true);
+ _insert_feature("mark_positioning", HB_TAG('m', 'a', 'r', 'k'), Variant::Type::INT, true);
+ _insert_feature("medial_forms_2", HB_TAG('m', 'e', 'd', '2'), Variant::Type::INT, true);
+ _insert_feature("medial_forms", HB_TAG('m', 'e', 'd', 'i'), Variant::Type::INT, true);
+ _insert_feature("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'), Variant::Type::BOOL, false);
+ _insert_feature("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'), Variant::Type::INT, true);
+ _insert_feature("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'), Variant::Type::INT, true);
+ _insert_feature("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'), Variant::Type::INT, false);
+ _insert_feature("nukta_forms", HB_TAG('n', 'u', 'k', 't'), Variant::Type::INT, true);
+ _insert_feature("numerators", HB_TAG('n', 'u', 'm', 'r'), Variant::Type::BOOL, false);
+ _insert_feature("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'), Variant::Type::INT, false);
+ _insert_feature("optical_bounds", HB_TAG('o', 'p', 'b', 'd'), Variant::Type::INT, true);
+ _insert_feature("ordinals", HB_TAG('o', 'r', 'd', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("ornaments", HB_TAG('o', 'r', 'n', 'm'), Variant::Type::INT, false);
+ _insert_feature("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("petite_capitals", HB_TAG('p', 'c', 'a', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_kana", HB_TAG('p', 'k', 'n', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_figures", HB_TAG('p', 'n', 'u', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'), Variant::Type::INT, true);
+ _insert_feature("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'), Variant::Type::INT, true);
+ _insert_feature("post_base_forms", HB_TAG('p', 's', 't', 'f'), Variant::Type::INT, true);
+ _insert_feature("post_base_substitutions", HB_TAG('p', 's', 't', 's'), Variant::Type::INT, true);
+ _insert_feature("proportional_widths", HB_TAG('p', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("quarter_widths", HB_TAG('q', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("randomize", HB_TAG('r', 'a', 'n', 'd'), Variant::Type::INT, false);
+ _insert_feature("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'), Variant::Type::BOOL, true);
+ _insert_feature("rakar_forms", HB_TAG('r', 'k', 'r', 'f'), Variant::Type::INT, true);
+ _insert_feature("required_ligatures", HB_TAG('r', 'l', 'i', 'g'), Variant::Type::BOOL, true);
+ _insert_feature("reph_forms", HB_TAG('r', 'p', 'h', 'f'), Variant::Type::INT, true);
+ _insert_feature("right_bounds", HB_TAG('r', 't', 'b', 'd'), Variant::Type::INT, false);
+ _insert_feature("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'), Variant::Type::INT, true);
+ _insert_feature("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'), Variant::Type::INT, true);
+ _insert_feature("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'), Variant::Type::INT, false);
+ _insert_feature("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'), Variant::Type::INT, true);
+ _insert_feature("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'), Variant::Type::BOOL, false);
+ _insert_feature("optical_size", HB_TAG('s', 'i', 'z', 'e'), Variant::Type::INT, false);
+ _insert_feature("small_capitals", HB_TAG('s', 'm', 'c', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("simplified_forms", HB_TAG('s', 'm', 'p', 'l'), Variant::Type::INT, false);
+ _insert_feature("stylistic_set_01", HB_TAG('s', 's', '0', '1'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_02", HB_TAG('s', 's', '0', '2'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_03", HB_TAG('s', 's', '0', '3'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_04", HB_TAG('s', 's', '0', '4'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_05", HB_TAG('s', 's', '0', '5'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_06", HB_TAG('s', 's', '0', '6'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_07", HB_TAG('s', 's', '0', '7'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_08", HB_TAG('s', 's', '0', '8'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_09", HB_TAG('s', 's', '0', '9'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_10", HB_TAG('s', 's', '1', '0'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_11", HB_TAG('s', 's', '1', '1'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_12", HB_TAG('s', 's', '1', '2'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_13", HB_TAG('s', 's', '1', '3'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_14", HB_TAG('s', 's', '1', '4'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_15", HB_TAG('s', 's', '1', '5'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_16", HB_TAG('s', 's', '1', '6'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_17", HB_TAG('s', 's', '1', '7'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_18", HB_TAG('s', 's', '1', '8'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_19", HB_TAG('s', 's', '1', '9'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_20", HB_TAG('s', 's', '2', '0'), Variant::Type::BOOL, false);
+ _insert_feature("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'), Variant::Type::INT, true);
+ _insert_feature("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'), Variant::Type::INT, true);
+ _insert_feature("subscript", HB_TAG('s', 'u', 'b', 's'), Variant::Type::BOOL, false);
+ _insert_feature("superscript", HB_TAG('s', 'u', 'p', 's'), Variant::Type::BOOL, false);
+ _insert_feature("swash", HB_TAG('s', 'w', 's', 'h'), Variant::Type::INT, false);
+ _insert_feature("titling", HB_TAG('t', 'i', 't', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'), Variant::Type::INT, false);
+ _insert_feature("tabular_figures", HB_TAG('t', 'n', 'u', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("traditional_forms", HB_TAG('t', 'r', 'a', 'd'), Variant::Type::INT, false);
+ _insert_feature("third_widths", HB_TAG('t', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("unicase", HB_TAG('u', 'n', 'i', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("vattu_variants", HB_TAG('v', 'a', 't', 'u'), Variant::Type::INT, true);
+ _insert_feature("vertical_contextual_half_width_spacing", HB_TAG('v', 'c', 'h', 'w'), Variant::Type::BOOL, false);
+ _insert_feature("vertical_alternates", HB_TAG('v', 'e', 'r', 't'), Variant::Type::INT, false);
+ _insert_feature("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'), Variant::Type::INT, false);
+ _insert_feature("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'), Variant::Type::INT, false);
+ _insert_feature("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'), Variant::Type::INT, false);
+ _insert_feature("slashed_zero", HB_TAG('z', 'e', 'r', 'o'), Variant::Type::BOOL, false);
// Registered OpenType variation tag.
- _insert_feature("italic", HB_TAG('i', 't', 'a', 'l'));
- _insert_feature("optical_size", HB_TAG('o', 'p', 's', 'z'));
- _insert_feature("slant", HB_TAG('s', 'l', 'n', 't'));
- _insert_feature("width", HB_TAG('w', 'd', 't', 'h'));
- _insert_feature("weight", HB_TAG('w', 'g', 'h', 't'));
+ _insert_feature("italic", HB_TAG('i', 't', 'a', 'l'), Variant::Type::INT, false);
+ _insert_feature("optical_size", HB_TAG('o', 'p', 's', 'z'), Variant::Type::INT, false);
+ _insert_feature("slant", HB_TAG('s', 'l', 'n', 't'), Variant::Type::INT, false);
+ _insert_feature("width", HB_TAG('w', 'd', 't', 'h'), Variant::Type::INT, false);
+ _insert_feature("weight", HB_TAG('w', 'g', 'h', 't'), Variant::Type::INT, false);
}
int64_t TextServerAdvanced::name_to_tag(const String &p_name) const {
@@ -740,9 +749,23 @@ int64_t TextServerAdvanced::name_to_tag(const String &p_name) const {
return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
}
+Variant::Type TextServerAdvanced::_get_tag_type(int64_t p_tag) const {
+ if (feature_sets_inv.has(p_tag)) {
+ return feature_sets_inv[p_tag].vtype;
+ }
+ return Variant::Type::INT;
+}
+
+bool TextServerAdvanced::_get_tag_hidden(int64_t p_tag) const {
+ if (feature_sets_inv.has(p_tag)) {
+ return feature_sets_inv[p_tag].hidden;
+ }
+ return false;
+}
+
String TextServerAdvanced::tag_to_name(int64_t p_tag) const {
if (feature_sets_inv.has(p_tag)) {
- return feature_sets_inv[p_tag];
+ return feature_sets_inv[p_tag].name;
}
// No readable name, use tag string.
@@ -756,7 +779,7 @@ String TextServerAdvanced::tag_to_name(int64_t p_tag) const {
/* Font Glyph Rendering */
/*************************************************************************/
-_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
+_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
FontTexturePosition ret;
ret.index = -1;
@@ -878,8 +901,8 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
struct MSContext {
msdfgen::Point2 position;
- msdfgen::Shape *shape;
- msdfgen::Contour *contour;
+ msdfgen::Shape *shape = nullptr;
+ msdfgen::Contour *contour = nullptr;
};
class DistancePixelConversion {
@@ -953,7 +976,7 @@ void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
}
}
-_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
msdfgen::Shape shape;
shape.contours.clear();
@@ -1017,10 +1040,8 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
- if (p_font_data->work_pool.get_thread_count() == 0) {
- p_font_data->work_pool.init();
- }
- p_font_data->work_pool.do_work(h, this, &TextServerAdvanced::_generateMTSDF_threaded, &td);
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, SNAME("FontServerRasterizeMSDF"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
@@ -1059,7 +1080,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
int w = bitmap.width;
int h = bitmap.rows;
@@ -1136,12 +1157,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
/* Font Cache */
/*************************************************************************/
-_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.
- FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
+ FontForSizeAdvanced *fd = p_font_data->cache[p_size];
if (fd->glyph_map.has(p_glyph)) {
return fd->glyph_map[p_glyph].found;
}
@@ -1260,13 +1281,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
return false;
}
-_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const {
ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
- FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced);
+ FontForSizeAdvanced *fd = memnew(FontForSizeAdvanced);
fd->size = p_size;
if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
@@ -1288,7 +1309,16 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -1329,7 +1359,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
- hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform.elements[0][1]);
+ hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform[0][1]);
if (!p_font_data->face_init) {
// Get style flags and name.
@@ -1341,29 +1371,31 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
}
p_font_data->style_flags = 0;
if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
- p_font_data->style_flags |= FONT_BOLD;
+ p_font_data->style_flags.set_flag(FONT_BOLD);
}
if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
- p_font_data->style_flags |= FONT_ITALIC;
+ p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
- p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);
}
+
+ hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
// Get supported scripts from OpenType font data.
p_font_data->supported_scripts.clear();
- unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ unsigned int count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
+ hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, &count, script_tags);
for (unsigned int i = 0; i < count; i++) {
p_font_data->supported_scripts.insert(script_tags[i]);
}
memfree(script_tags);
}
- count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GPOS, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
+ hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GPOS, 0, &count, script_tags);
for (unsigned int i = 0; i < count; i++) {
p_font_data->supported_scripts.insert(script_tags[i]);
}
@@ -1587,21 +1619,49 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
// Read OpenType feature tags.
p_font_data->supported_features.clear();
- count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GSUB, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
+ hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GSUB, 0, &count, feature_tags);
for (unsigned int i = 0; i < count; i++) {
- p_font_data->supported_features[feature_tags[i]] = 1;
+ Dictionary ftr;
+
+ hb_ot_name_id_t lbl_id;
+ if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GSUB, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) {
+ PackedInt32Array lbl;
+ unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1;
+ lbl.resize(text_size);
+ memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size);
+ hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw());
+ ftr["label"] = String((const char32_t *)lbl.ptr());
+ }
+ ftr["type"] = _get_tag_type(feature_tags[i]);
+ ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
+
+ p_font_data->supported_features[feature_tags[i]] = ftr;
}
memfree(feature_tags);
}
- count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GPOS, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
+ hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GPOS, 0, &count, feature_tags);
for (unsigned int i = 0; i < count; i++) {
- p_font_data->supported_features[feature_tags[i]] = 1;
+ Dictionary ftr;
+
+ hb_ot_name_id_t lbl_id;
+ if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GPOS, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) {
+ PackedInt32Array lbl;
+ unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1;
+ lbl.resize(text_size);
+ memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size);
+ hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw());
+ ftr["label"] = String((const char32_t *)lbl.ptr());
+ }
+ ftr["type"] = _get_tag_type(feature_tags[i]);
+ ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
+
+ p_font_data->supported_features[feature_tags[i]] = ftr;
}
memfree(feature_tags);
}
@@ -1667,8 +1727,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
return true;
}
-_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) {
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : p_font_data->cache) {
+_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontAdvanced *p_font_data) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : p_font_data->cache) {
memdelete(E.value);
}
p_font_data->cache.clear();
@@ -1679,7 +1739,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_fo
}
hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, nullptr);
MutexLock lock(fd->mutex);
@@ -1691,13 +1751,15 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_
}
RID TextServerAdvanced::create_font() {
- FontDataAdvanced *fd = memnew(FontDataAdvanced);
+ _THREAD_SAFE_METHOD_
+
+ FontAdvanced *fd = memnew(FontAdvanced);
return font_owner.make_rid(fd);
}
void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1708,7 +1770,7 @@ void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteAr
}
void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1718,8 +1780,71 @@ void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
-void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
+void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1728,8 +1853,8 @@ void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontSty
fd->style_flags = p_style;
}
-int64_t /*FontStyle*/ TextServerAdvanced::font_get_style(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1739,7 +1864,7 @@ int64_t /*FontStyle*/ TextServerAdvanced::font_get_style(const RID &p_font_rid)
}
void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String &p_name) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1749,7 +1874,7 @@ void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String
}
String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -1759,7 +1884,7 @@ String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_name) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1769,7 +1894,7 @@ void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_na
}
String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -1779,7 +1904,7 @@ String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1790,7 +1915,7 @@ void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_anti
}
bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1798,12 +1923,12 @@ bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
if (fd->mipmaps != p_generate_mipmaps) {
- for (KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
}
@@ -1813,7 +1938,7 @@ void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p
}
bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1821,7 +1946,7 @@ bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const
}
void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1832,7 +1957,7 @@ void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &
}
bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1840,7 +1965,7 @@ bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p
}
void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1851,7 +1976,7 @@ void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_
}
int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1859,7 +1984,7 @@ int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) con
}
void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1870,7 +1995,7 @@ void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msd
}
int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1878,7 +2003,7 @@ int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1886,7 +2011,7 @@ void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
}
int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1894,7 +2019,7 @@ int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1905,7 +2030,7 @@ void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p
}
bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1913,7 +2038,7 @@ bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1924,7 +2049,7 @@ void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hin
}
TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -1932,7 +2057,7 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid)
}
void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1940,7 +2065,7 @@ void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, Te
}
TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positioning(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
MutexLock lock(fd->mutex);
@@ -1948,7 +2073,7 @@ TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positionin
}
void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_strength) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1959,7 +2084,7 @@ void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_stren
}
double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1967,7 +2092,7 @@ double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1978,7 +2103,7 @@ void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transfo
}
Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
MutexLock lock(fd->mutex);
@@ -1986,7 +2111,7 @@ Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const
}
void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1997,7 +2122,7 @@ void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, c
}
Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2005,7 +2130,7 @@ Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_
}
void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2016,7 +2141,7 @@ void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_o
}
double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2024,30 +2149,30 @@ double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const {
}
Array TextServerAdvanced::font_get_size_cache_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerAdvanced::font_clear_size_cache(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
memdelete(E.value);
}
fd->cache.clear();
}
void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2058,7 +2183,7 @@ void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vec
}
void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2069,7 +2194,7 @@ void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size,
}
double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2085,7 +2210,7 @@ double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size
}
void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -2095,7 +2220,7 @@ void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size,
}
double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2111,7 +2236,7 @@ double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_siz
}
void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2122,7 +2247,7 @@ void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int6
}
double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2138,7 +2263,7 @@ double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, in
}
void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2149,7 +2274,7 @@ void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int
}
double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2165,18 +2290,23 @@ double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, i
}
void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2191,60 +2321,8 @@ double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-void TextServerAdvanced::font_set_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing, int64_t p_value) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND(!fd);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- fd->cache[size]->spacing_glyph = p_value;
- } break;
- case TextServer::SPACING_SPACE: {
- fd->cache[size]->spacing_space = p_value;
- } break;
- default: {
- ERR_FAIL_MSG("Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
-}
-
-int64_t TextServerAdvanced::font_get_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, 0);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
-
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_glyph;
- }
- } break;
- case TextServer::SPACING_SPACE: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_space * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_space;
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(0, "Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
- return 0;
-}
-
int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -2256,7 +2334,7 @@ int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const
}
void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
@@ -2266,7 +2344,7 @@ void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector
}
void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2278,7 +2356,7 @@ void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector
}
void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -2304,14 +2382,12 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec
img->generate_mipmaps();
}
- tex.texture = Ref<ImageTexture>();
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
tex.dirty = false;
}
Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -2328,7 +2404,7 @@ Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, con
}
void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2344,7 +2420,7 @@ void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const V
}
PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -2357,7 +2433,7 @@ PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_
}
Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2366,15 +2442,14 @@ Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vecto
Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- ret.push_back(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ ret.push_back(E.key);
}
return ret;
}
void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2385,7 +2460,7 @@ void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2396,7 +2471,7 @@ void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i
}
double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) const {
- const FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ const FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2410,7 +2485,7 @@ double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) c
}
Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2438,7 +2513,7 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_
}
void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2453,7 +2528,7 @@ void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p
}
Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2474,7 +2549,7 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V
}
void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2489,7 +2564,7 @@ void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vect
}
Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2510,7 +2585,7 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec
}
void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2525,7 +2600,7 @@ void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector
}
Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -2541,7 +2616,7 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
}
void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2556,7 +2631,7 @@ void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
}
int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -2572,7 +2647,7 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co
}
void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2587,7 +2662,7 @@ void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const
}
RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
MutexLock lock(fd->mutex);
@@ -2612,8 +2687,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2627,7 +2701,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
MutexLock lock(fd->mutex);
@@ -2652,8 +2726,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2667,7 +2740,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
}
Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2684,13 +2757,22 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in
int error = FT_Load_Glyph(fd->cache[size]->face, index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
ERR_FAIL_COND_V(error, Dictionary());
- double h = fd->cache[size]->ascent;
+ if (fd->embolden != 0.f) {
+ FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64).
+ FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength);
+ }
+
+ if (fd->transform != Transform2D()) {
+ FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).
+ FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat);
+ }
+
double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
scale = scale * (double)p_size / (double)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
@@ -2708,7 +2790,7 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in
}
Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2717,14 +2799,14 @@ Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2735,7 +2817,7 @@ void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p
}
void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2746,7 +2828,7 @@ void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_si
}
void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2757,7 +2839,7 @@ void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size,
}
Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2765,7 +2847,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -2790,7 +2872,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
}
int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
ERR_FAIL_COND_V_MSG((p_variation_selector >= 0xd800 && p_variation_selector <= 0xdfff) || (p_variation_selector > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_variation_selector, 16) + ".");
@@ -2815,7 +2897,7 @@ int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t
}
bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
@@ -2823,7 +2905,7 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -2834,14 +2916,14 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
}
String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -2859,16 +2941,15 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const
#endif
if (at_size) {
const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- chars = chars + String::chr(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ chars = chars + String::chr(E.key);
}
}
return chars;
}
void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");
ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");
@@ -2901,7 +2982,7 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2930,7 +3011,7 @@ void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2976,8 +3057,7 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -3009,7 +3089,7 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
}
void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3055,8 +3135,7 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -3088,7 +3167,7 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
}
bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3100,7 +3179,7 @@ bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const
}
void TextServerAdvanced::font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3108,7 +3187,7 @@ void TextServerAdvanced::font_set_language_support_override(const RID &p_font_ri
}
bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3116,7 +3195,7 @@ bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_ri
}
void TextServerAdvanced::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3124,7 +3203,7 @@ void TextServerAdvanced::font_remove_language_support_override(const RID &p_font
}
PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -3136,7 +3215,7 @@ PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const
}
bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3150,7 +3229,7 @@ bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const S
}
void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3158,7 +3237,7 @@ void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid,
}
bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3166,7 +3245,7 @@ bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid,
}
void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3174,7 +3253,7 @@ void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_r
}
PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -3186,7 +3265,7 @@ PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RI
}
void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3196,7 +3275,7 @@ void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_r
}
Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3204,7 +3283,7 @@ Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_
}
Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3214,7 +3293,7 @@ Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid
}
Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3258,7 +3337,7 @@ void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) {
int64_t TextServerAdvanced::_convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const {
int64_t limit = p_pos;
if (p_utf32.length() != p_utf16.length()) {
- const UChar *data = p_utf16.ptr();
+ const UChar *data = p_utf16.get_data();
for (int i = 0; i < p_pos; i++) {
if (U16_IS_LEAD(data[i])) {
limit--;
@@ -3347,6 +3426,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
sd->direction = p_direction;
@@ -3493,6 +3573,31 @@ bool TextServerAdvanced::shaped_text_get_preserve_control(const RID &p_shaped) c
return sd->preserve_control;
}
+void TextServerAdvanced::shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->extra_spacing[p_spacing] != p_value) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->extra_spacing[p_spacing] = p_value;
+ invalidate(sd, false);
+ }
+}
+
+int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+
+ MutexLock lock(sd->mutex);
+ return sd->extra_spacing[p_spacing];
+}
+
TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
@@ -3736,6 +3841,8 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
}
RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -3758,6 +3865,9 @@ RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start,
new_sd->direction = sd->direction;
new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
if (!_shape_substr(new_sd, sd, p_start, p_length)) {
memdelete(new_sd);
@@ -3883,7 +3993,7 @@ RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const {
return sd->parent;
}
-double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) {
+double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
@@ -3899,7 +4009,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
int start_pos = 0;
int end_pos = sd->glyphs.size() - 1;
- if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
start = sd->glyphs.size() - 1;
@@ -3925,7 +4035,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
double justification_width;
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
if (sd->overrun_trim_data.trim_pos >= 0) {
if (sd->para_direction == DIRECTION_RTL) {
start_pos = sd->overrun_trim_data.trim_pos;
@@ -3940,7 +4050,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
justification_width = sd->width;
}
- if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
@@ -3979,7 +4089,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
- if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
+ if ((elongation_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_KASHIDA)) {
double delta_width_per_kashida = (p_width - justification_width) / elongation_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
@@ -4000,7 +4110,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
}
- if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {
double delta_width_per_space = (p_width - justification_width) / space_count;
double adv_remain = 0;
for (int i = start_pos; i <= end_pos; i++) {
@@ -4033,7 +4143,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
sd->fit_width_minimum_reached = true;
}
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
sd->width = justification_width;
}
@@ -4096,7 +4206,7 @@ double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const Pack
return 0.0;
}
-void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) {
+void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
@@ -4108,14 +4218,14 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
- bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
- bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
- bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
- bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+ bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);
+ bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);
+ bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);
+ bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
sd->overrun_trim_data.trim_pos = -1;
sd->overrun_trim_data.ellipsis_pos = -1;
return;
@@ -4168,7 +4278,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -4316,7 +4426,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
i++;
}
int r_end = sd->spans[i].end;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, (language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int j = r_start; j < r_end; j++) {
@@ -4572,7 +4682,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape
Glyph *sd_glyphs = sd->glyphs.ptrw();
int sd_size = sd->glyphs.size();
- if (sd->jstops.size() > 0) {
+ if (!sd->jstops.is_empty()) {
for (int i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
@@ -4762,12 +4872,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
RID f = p_fonts[p_fb_index];
- FontDataAdvanced *fd = font_owner.get_or_null(f);
+ FontAdvanced *fd = font_owner.get_or_null(f);
+ ERR_FAIL_COND(!fd);
+ MutexLock lock(fd->mutex);
+
Vector2i fss = _get_size(fd, fs);
hb_font_t *hb_font = _font_get_hb_handle(f, fs);
double scale = font_get_scale(f, fs);
- double sp_sp = font_get_spacing(f, fs, SPACING_SPACE);
- double sp_gl = font_get_spacing(f, fs, SPACING_GLYPH);
+ double sp_sp = p_sd->extra_spacing[SPACING_SPACE];
+ double sp_gl = p_sd->extra_spacing[SPACING_GLYPH];
+ bool last_run = (p_sd->end == p_end);
double ea = _get_extra_advance(f, fs);
bool subpos = (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
ERR_FAIL_COND(hb_font == nullptr);
@@ -4781,7 +4895,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
hb_buffer_set_script(p_sd->hb_buffer, p_script);
- if (!p_sd->spans[p_span].language.is_empty()) {
+ if (p_sd->spans[p_span].language.is_empty()) {
+ hb_language_t lang = hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1);
+ hb_buffer_set_language(p_sd->hb_buffer, lang);
+ } else {
hb_language_t lang = hb_language_from_string(p_sd->spans[p_span].language.ascii().get_data(), -1);
hb_buffer_set_language(p_sd->hb_buffer, lang);
}
@@ -4862,10 +4979,13 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
}
- if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
- gl.advance += sp_sp;
- } else {
- gl.advance += sp_gl;
+ if (!last_run || i < glyph_count - 1) {
+ // Do not add extra spacing to the last glyph of the string.
+ if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
+ gl.advance += sp_sp;
+ } else {
+ gl.advance += sp_gl;
+ }
}
if (p_sd->preserve_control) {
@@ -5196,9 +5316,9 @@ Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent).ceil();
+ return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
} else {
- return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width)).ceil();
+ return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], (sd->text_trimmed ? sd->width_trimmed : sd->width)).ceil();
}
}
@@ -5210,7 +5330,7 @@ double TextServerAdvanced::shaped_text_get_ascent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
- return sd->ascent;
+ return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const {
@@ -5221,7 +5341,7 @@ double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
- return sd->descent;
+ return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
double TextServerAdvanced::shaped_text_get_width(const RID &p_shaped) const {
@@ -5535,16 +5655,18 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const {
}
String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
Vector<char16_t> upper;
UErrorCode err = U_ZERO_ERROR;
- int32_t len = u_strToUpper(nullptr, 0, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ int32_t len = u_strToUpper(nullptr, 0, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
upper.resize(len);
err = U_ZERO_ERROR;
- u_strToUpper(upper.ptrw(), len, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ u_strToUpper(upper.ptrw(), len, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
// Convert back to UTF-32.
@@ -5552,16 +5674,17 @@ String TextServerAdvanced::string_to_upper(const String &p_string, const String
}
String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
Vector<char16_t> lower;
UErrorCode err = U_ZERO_ERROR;
- int32_t len = u_strToLower(nullptr, 0, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ int32_t len = u_strToLower(nullptr, 0, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
lower.resize(len);
err = U_ZERO_ERROR;
- u_strToLower(lower.ptrw(), len, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ u_strToLower(lower.ptrw(), len, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
// Convert back to UTF-32.
@@ -5569,12 +5692,13 @@ String TextServerAdvanced::string_to_lower(const String &p_string, const String
}
PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
- Set<int> breaks;
+ HashSet<int> breaks;
UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, p_language.ascii().get_data(), (const UChar *)utf16.ptr(), utf16.length(), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int i = 0; i < p_string.length(); i++) {
@@ -5623,8 +5747,10 @@ TextServerAdvanced::TextServerAdvanced() {
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
+#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
FT_Done_FreeType(ft_library);
}
+#endif
u_cleanup();
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 1b4293aa72..8cd0e753ba 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -65,12 +65,12 @@
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/ref.hpp>
+#include <godot_cpp/classes/worker_thread_pool.hpp>
#include <godot_cpp/templates/hash_map.hpp>
-#include <godot_cpp/templates/map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
-#include <godot_cpp/templates/thread_work_pool.hpp>
+
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@@ -78,8 +78,9 @@ using namespace godot;
#else
// Headers for building as built-in module.
+#include "core/object/worker_thread_pool.h"
+#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
-#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
#include "servers/text/text_server_extension.h"
@@ -126,19 +127,26 @@ class TextServerAdvanced : public TextServerExtension {
_THREAD_SAFE_CLASS_
struct NumSystemData {
- Set<StringName> lang;
+ HashSet<StringName> lang;
String digits;
String percent_sign;
String exp;
};
Vector<NumSystemData> num_systems;
- Map<StringName, int32_t> feature_sets;
- Map<int32_t, StringName> feature_sets_inv;
+
+ struct FeatureInfo {
+ StringName name;
+ Variant::Type vtype = Variant::INT;
+ bool hidden = false;
+ };
+
+ HashMap<StringName, int32_t> feature_sets;
+ HashMap<int32_t, FeatureInfo> feature_sets_inv;
void _insert_num_systems_lang();
void _insert_feature_sets();
- _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag);
+ _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag, Variant::Type p_vtype = Variant::INT, bool p_hidden = false);
// ICU support data.
@@ -176,7 +184,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector2 advance;
};
- struct FontDataForSizeAdvanced {
+ struct FontForSizeAdvanced {
double ascent = 0.0;
double descent = 0.0;
double underline_position = 0.0;
@@ -184,15 +192,11 @@ class TextServerAdvanced : public TextServerExtension {
double scale = 1.0;
double oversampling = 1.0;
- int spacing_glyph = 0;
- int spacing_space = 0;
-
Vector2i size;
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- Map<Vector2i, Vector2> kerning_map;
-
+ HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr;
#ifdef MODULE_FREETYPE_ENABLED
@@ -200,7 +204,7 @@ class TextServerAdvanced : public TextServerExtension {
FT_StreamRec stream;
#endif
- ~FontDataForSizeAdvanced() {
+ ~FontForSizeAdvanced() {
if (hb_handle != nullptr) {
hb_font_destroy(hb_handle);
}
@@ -212,7 +216,7 @@ class TextServerAdvanced : public TextServerExtension {
}
};
- struct FontDataAdvanced {
+ struct FontAdvanced {
Mutex mutex;
bool antialiased = true;
@@ -229,49 +233,48 @@ class TextServerAdvanced : public TextServerExtension {
double embolden = 0.0;
Transform2D transform;
- uint32_t style_flags = 0;
+ BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
- Map<Vector2i, FontDataForSizeAdvanced *> cache;
+ HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
bool face_init = false;
- Set<uint32_t> supported_scripts;
+ HashSet<uint32_t> supported_scripts;
Dictionary supported_features;
Dictionary supported_varaitions;
Dictionary feature_overrides;
// Language/script support override.
- Map<String, bool> language_support_overrides;
- Map<String, bool> script_support_overrides;
+ HashMap<String, bool> language_support_overrides;
+ HashMap<String, bool> script_support_overrides;
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
- mutable ThreadWorkPool work_pool;
+ int face_index = 0;
- ~FontDataAdvanced() {
- work_pool.finish();
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : cache) {
+ ~FontAdvanced() {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : cache) {
memdelete(E.value);
}
cache.clear();
}
};
- _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
#ifdef MODULE_MSDFGEN_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
#endif
- _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
- _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const;
- _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data);
+ _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontAdvanced *p_font_data);
void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
- _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size(const FontAdvanced *p_font_data, int p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -281,7 +284,7 @@ class TextServerAdvanced : public TextServerExtension {
}
}
- _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontAdvanced *p_font_data, const Vector2i &p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -292,6 +295,8 @@ class TextServerAdvanced : public TextServerExtension {
}
_FORCE_INLINE_ double _get_extra_advance(RID p_font_rid, int p_font_size) const;
+ _FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const;
+ _FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const;
// Shaped text cache data.
struct TrimData {
@@ -334,7 +339,7 @@ class TextServerAdvanced : public TextServerExtension {
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
};
- Map<Variant, EmbeddedObject> objects;
+ HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
@@ -351,6 +356,7 @@ class TextServerAdvanced : public TextServerExtension {
double descent = 0.0; // Descent for horizontal layout, 1/2 of width for vertical.
double width = 0.0; // Width for horizontal layout, height for vertical.
double width_trimmed = 0.0;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
double upos = 0.0;
double uthk = 0.0;
@@ -389,7 +395,7 @@ class TextServerAdvanced : public TextServerExtension {
// Common data.
double oversampling = 1.0;
- mutable RID_PtrOwner<FontDataAdvanced> font_owner;
+ mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
void _realign(ShapedTextDataAdvanced *p_sd) const;
@@ -407,11 +413,11 @@ class TextServerAdvanced : public TextServerExtension {
static hb_font_funcs_t *funcs;
struct bmp_font_t {
- TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr;
+ TextServerAdvanced::FontForSizeAdvanced *face = nullptr;
bool unref = false; /* Whether to destroy bm_face when done. */
};
- static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
+ static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref);
static void _bmp_font_destroy(void *p_data);
static hb_bool_t _bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data);
static hb_position_t _bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
@@ -422,8 +428,8 @@ class TextServerAdvanced : public TextServerExtension {
static hb_bool_t _bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data);
static void _bmp_create_font_funcs();
static void _bmp_free_font_funcs();
- static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
- static hb_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
+ static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref);
+ static hb_font_t *_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
hb_font_t *_font_get_hb_handle(const RID &p_font, int64_t p_font_size) const;
@@ -474,8 +480,13 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
- virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
- virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
+ virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override;
+ virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override;
virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override;
virtual String font_get_style_name(const RID &p_font_rid) const override;
@@ -541,9 +552,6 @@ public:
virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override;
virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_set_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing, int64_t p_value) override;
- virtual int64_t font_get_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing) const override;
-
virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override;
@@ -641,6 +649,9 @@ public:
virtual void shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(const RID &p_shaped) const override;
+ virtual void shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) override;
+ virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const override;
+
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
@@ -652,7 +663,7 @@ public:
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;
virtual RID shaped_text_get_parent(const RID &p_shaped) const override;
- virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(const RID &p_shaped) override;
@@ -664,7 +675,7 @@ public:
virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override;
virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override;
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 1753bc8b86..de0a549900 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -111,6 +111,7 @@ if env["freetype_enabled"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -159,7 +160,14 @@ if env["freetype_enabled"]:
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)])
+ env_freetype.Append(
+ CPPDEFINES=[
+ "FT2_BUILD_LIBRARY",
+ "FT_CONFIG_OPTION_USE_PNG",
+ ("PNG_ARM_NEON_OPT", 0),
+ "FT_CONFIG_OPTION_SYSTEM_ZLIB",
+ ]
+ )
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
@@ -176,15 +184,15 @@ env.Append(CPPDEFINES=["GDEXTENSION"])
env.Append(CPPPATH=["../"])
sources = Glob("../*.cpp")
-if env["platform"] == "osx":
- methods.write_osx_plist(
- f'./bin/libtextserver_fallback.osx.{env["target"]}.framework',
- f'libtextserver_fallback.osx.{env["target"]}',
+if env["platform"] == "macos":
+ methods.write_macos_plist(
+ f'./bin/libtextserver_fallback.macos.{env["target"]}.framework',
+ f'libtextserver_fallback.macos.{env["target"]}',
"org.godotengine.textserver_fallback",
"Fallback Text Server",
)
library = env.SharedLibrary(
- f'./bin/libtextserver_fallback.osx.{env["target"]}.framework/libtextserver_fallback.osx.{env["target"]}',
+ f'./bin/libtextserver_fallback.macos.{env["target"]}.framework/libtextserver_fallback.macos.{env["target"]}',
source=sources,
)
else:
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index d404f2851e..3c5229462c 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -98,7 +98,7 @@ def make_icu_data(target, source, env):
g.write("#endif")
-def write_osx_plist(target, binary_name, identifier, name):
+def write_macos_plist(target, binary_name, identifier, name):
os.makedirs(f"{target}/Resourece/", exist_ok=True)
f = open(f"{target}/Resourece/Info.plist", "w")
diff --git a/modules/text_server_fb/gdextension_build/text_server_fb.gdextension b/modules/text_server_fb/gdextension_build/text_server_fb.gdextension
index 1026c6cb85..9236555d63 100644
--- a/modules/text_server_fb/gdextension_build/text_server_fb.gdextension
+++ b/modules/text_server_fb/gdextension_build/text_server_fb.gdextension
@@ -8,5 +8,5 @@ linux.64.debug = "bin/libtextserver_fallback.linux.debug.64.so"
linux.64.release = "bin/libtextserver_fallback.linux.release.64.so"
windows.64.debug = "bin/libtextserver_fallback.windows.debug.64.dll"
windows.64.release = "bin/libtextserver_fallback.windows.release.64.dll"
-macos.debug = "bin/libtextserver_fallback.osx.debug.framework"
-macos.release = "bin/libtextserver_fallback.osx.release.framework"
+macos.debug = "bin/libtextserver_fallback.macos.debug.framework"
+macos.release = "bin/libtextserver_fallback.macos.release.framework"
diff --git a/modules/text_server_fb/register_types.cpp b/modules/text_server_fb/register_types.cpp
index 1044c3f872..fa7b87fc17 100644
--- a/modules/text_server_fb/register_types.cpp
+++ b/modules/text_server_fb/register_types.cpp
@@ -32,7 +32,11 @@
#include "text_server_fb.h"
-void preregister_text_server_fb_types() {
+void initialize_text_server_fb_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ return;
+ }
+
GDREGISTER_CLASS(TextServerFallback);
TextServerManager *tsman = TextServerManager::get_singleton();
if (tsman) {
@@ -42,10 +46,10 @@ void preregister_text_server_fb_types() {
}
}
-void register_text_server_fb_types() {
-}
-
-void unregister_text_server_fb_types() {
+void uninitialize_text_server_fb_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ return;
+ }
}
#ifdef GDEXTENSION
@@ -61,8 +65,9 @@ extern "C" {
GDNativeBool GDN_EXPORT textserver_fallback_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) {
GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
- init_obj.register_server_initializer(&preregister_text_server_fb_types);
- init_obj.register_server_terminator(&unregister_text_server_fb_types);
+ init_obj.register_initializer(&initialize_text_server_fb_module);
+ init_obj.register_terminator(&uninitialize_text_server_fb_module);
+ init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SERVERS);
return init_obj.init();
}
diff --git a/modules/text_server_fb/register_types.h b/modules/text_server_fb/register_types.h
index 8652a8e9db..229aec2266 100644
--- a/modules/text_server_fb/register_types.h
+++ b/modules/text_server_fb/register_types.h
@@ -31,10 +31,14 @@
#ifndef TEXT_SERVER_FB_REGISTER_TYPES_H
#define TEXT_SERVER_FB_REGISTER_TYPES_H
-#define MODULE_TEXT_SERVER_FB_HAS_PREREGISTER
+#ifdef GDEXTENSION
+#include <godot_cpp/core/class_db.hpp>
+using namespace godot;
+#else
+#include "modules/register_module_types.h"
+#endif
-void preregister_text_server_fb_types();
-void register_text_server_fb_types();
-void unregister_text_server_fb_types();
+void initialize_text_server_fb_module(ModuleInitializationLevel p_level);
+void uninitialize_text_server_fb_module(ModuleInitializationLevel p_level);
#endif // TEXT_SERVER_FB_REGISTER_TYPES_H
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index d84e9e581a..50ea4677b1 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -104,7 +104,7 @@ int64_t TextServerFallback::get_features() const {
void TextServerFallback::free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataFallback *fd = font_owner.get_or_null(p_rid);
+ FontFallback *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
@@ -199,7 +199,7 @@ String TextServerFallback::tag_to_name(int64_t p_tag) const {
/* Font Glyph Rendering */
/*************************************************************************/
-_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
+_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
FontTexturePosition ret;
ret.index = -1;
@@ -322,8 +322,8 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
struct MSContext {
msdfgen::Point2 position;
- msdfgen::Shape *shape;
- msdfgen::Contour *contour;
+ msdfgen::Shape *shape = nullptr;
+ msdfgen::Contour *contour = nullptr;
};
class DistancePixelConversion {
@@ -397,7 +397,7 @@ void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
}
}
-_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
msdfgen::Shape shape;
shape.contours.clear();
@@ -461,10 +461,8 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
- if (p_font_data->work_pool.get_thread_count() == 0) {
- p_font_data->work_pool.init();
- }
- p_font_data->work_pool.do_work(h, this, &TextServerFallback::_generateMTSDF_threaded, &td);
+ WorkerThreadPool::GroupID group_id = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, SNAME("TextServerFBRenderMSDF"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_id);
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
@@ -502,7 +500,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
int w = bitmap.width;
int h = bitmap.rows;
@@ -579,12 +577,12 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
/* Font Cache */
/*************************************************************************/
-_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.
- FontDataForSizeFallback *fd = p_font_data->cache[p_size];
+ FontForSizeFallback *fd = p_font_data->cache[p_size];
if (fd->glyph_map.has(p_glyph)) {
return fd->glyph_map[p_glyph].found;
}
@@ -705,13 +703,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
return false;
}
-_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const {
+_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const {
ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
- FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback);
+ FontForSizeFallback *fd = memnew(FontForSizeFallback);
fd->size = p_size;
if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
@@ -733,7 +731,16 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -782,13 +789,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
}
p_font_data->style_flags = 0;
if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
- p_font_data->style_flags |= FONT_BOLD;
+ p_font_data->style_flags.set_flag(FONT_BOLD);
}
if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
- p_font_data->style_flags |= FONT_ITALIC;
+ p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
- p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);
}
// Read OpenType variations.
p_font_data->supported_varaitions.clear();
@@ -842,8 +849,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
return true;
}
-_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) {
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : p_font_data->cache) {
+_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {
memdelete(E.value);
}
@@ -853,13 +860,15 @@ _FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_fo
}
RID TextServerFallback::create_font() {
- FontDataFallback *fd = memnew(FontDataFallback);
+ _THREAD_SAFE_METHOD_
+
+ FontFallback *fd = memnew(FontFallback);
return font_owner.make_rid(fd);
}
void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -870,7 +879,7 @@ void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteAr
}
void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -880,8 +889,8 @@ void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
-void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+void TextServerFallback::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -890,8 +899,71 @@ void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontSty
fd->style_flags = p_style;
}
-int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+void TextServerFallback::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerFallback::font_get_face_index(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
+BitField<TextServer::FontStyle> TextServerFallback::font_get_style(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -901,7 +973,7 @@ int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid)
}
void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String &p_name) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -911,7 +983,7 @@ void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String
}
String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -921,7 +993,7 @@ String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_name) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -931,7 +1003,7 @@ void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_na
}
String TextServerFallback::font_get_name(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -941,7 +1013,7 @@ String TextServerFallback::font_get_name(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -952,7 +1024,7 @@ void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_anti
}
bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -960,12 +1032,12 @@ bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
if (fd->mipmaps != p_generate_mipmaps) {
- for (KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
}
@@ -975,7 +1047,7 @@ void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p
}
bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -983,7 +1055,7 @@ bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const
}
void TextServerFallback::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -994,7 +1066,7 @@ void TextServerFallback::font_set_multichannel_signed_distance_field(const RID &
}
bool TextServerFallback::font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1002,7 +1074,7 @@ bool TextServerFallback::font_is_multichannel_signed_distance_field(const RID &p
}
void TextServerFallback::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1013,7 +1085,7 @@ void TextServerFallback::font_set_msdf_pixel_range(const RID &p_font_rid, int64_
}
int64_t TextServerFallback::font_get_msdf_pixel_range(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1021,7 +1093,7 @@ int64_t TextServerFallback::font_get_msdf_pixel_range(const RID &p_font_rid) con
}
void TextServerFallback::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1032,7 +1104,7 @@ void TextServerFallback::font_set_msdf_size(const RID &p_font_rid, int64_t p_msd
}
int64_t TextServerFallback::font_get_msdf_size(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1040,7 +1112,7 @@ int64_t TextServerFallback::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1048,7 +1120,7 @@ void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
}
int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1056,7 +1128,7 @@ int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1067,7 +1139,7 @@ void TextServerFallback::font_set_force_autohinter(const RID &p_font_rid, bool p
}
bool TextServerFallback::font_is_force_autohinter(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1075,7 +1147,7 @@ bool TextServerFallback::font_is_force_autohinter(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1086,7 +1158,7 @@ void TextServerFallback::font_set_hinting(const RID &p_font_rid, TextServer::Hin
}
TextServer::Hinting TextServerFallback::font_get_hinting(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -1094,7 +1166,7 @@ TextServer::Hinting TextServerFallback::font_get_hinting(const RID &p_font_rid)
}
void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1102,7 +1174,7 @@ void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, Te
}
TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positioning(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
MutexLock lock(fd->mutex);
@@ -1110,7 +1182,7 @@ TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positionin
}
void TextServerFallback::font_set_embolden(const RID &p_font_rid, double p_strength) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1121,7 +1193,7 @@ void TextServerFallback::font_set_embolden(const RID &p_font_rid, double p_stren
}
double TextServerFallback::font_get_embolden(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1129,7 +1201,7 @@ double TextServerFallback::font_get_embolden(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1140,7 +1212,7 @@ void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transfo
}
Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
MutexLock lock(fd->mutex);
@@ -1148,7 +1220,7 @@ Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const
}
void TextServerFallback::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1159,7 +1231,7 @@ void TextServerFallback::font_set_variation_coordinates(const RID &p_font_rid, c
}
Dictionary TextServerFallback::font_get_variation_coordinates(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -1167,7 +1239,7 @@ Dictionary TextServerFallback::font_get_variation_coordinates(const RID &p_font_
}
void TextServerFallback::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1178,7 +1250,7 @@ void TextServerFallback::font_set_oversampling(const RID &p_font_rid, double p_o
}
double TextServerFallback::font_get_oversampling(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1186,30 +1258,30 @@ double TextServerFallback::font_get_oversampling(const RID &p_font_rid) const {
}
Array TextServerFallback::font_get_size_cache_list(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerFallback::font_clear_size_cache(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
memdelete(E.value);
}
fd->cache.clear();
}
void TextServerFallback::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1220,7 +1292,7 @@ void TextServerFallback::font_remove_size_cache(const RID &p_font_rid, const Vec
}
void TextServerFallback::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1231,7 +1303,7 @@ void TextServerFallback::font_set_ascent(const RID &p_font_rid, int64_t p_size,
}
double TextServerFallback::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1247,7 +1319,7 @@ double TextServerFallback::font_get_ascent(const RID &p_font_rid, int64_t p_size
}
void TextServerFallback::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -1257,7 +1329,7 @@ void TextServerFallback::font_set_descent(const RID &p_font_rid, int64_t p_size,
}
double TextServerFallback::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1273,7 +1345,7 @@ double TextServerFallback::font_get_descent(const RID &p_font_rid, int64_t p_siz
}
void TextServerFallback::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1284,7 +1356,7 @@ void TextServerFallback::font_set_underline_position(const RID &p_font_rid, int6
}
double TextServerFallback::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1300,7 +1372,7 @@ double TextServerFallback::font_get_underline_position(const RID &p_font_rid, in
}
void TextServerFallback::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1311,7 +1383,7 @@ void TextServerFallback::font_set_underline_thickness(const RID &p_font_rid, int
}
double TextServerFallback::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1327,18 +1399,23 @@ double TextServerFallback::font_get_underline_thickness(const RID &p_font_rid, i
}
void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ return; // Do not override scale for dynamic fonts, it's calculated automatically.
+ }
+#endif
fd->cache[size]->scale = p_scale;
}
double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1353,60 +1430,8 @@ double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-void TextServerFallback::font_set_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing, int64_t p_value) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND(!fd);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- fd->cache[size]->spacing_glyph = p_value;
- } break;
- case TextServer::SPACING_SPACE: {
- fd->cache[size]->spacing_space = p_value;
- } break;
- default: {
- ERR_FAIL_MSG("Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
-}
-
-int64_t TextServerFallback::font_get_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, 0);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
-
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_glyph;
- }
- } break;
- case TextServer::SPACING_SPACE: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_space * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_space;
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(0, "Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
- return 0;
-}
-
int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1418,7 +1443,7 @@ int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const
}
void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
@@ -1428,7 +1453,7 @@ void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector
}
void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1440,7 +1465,7 @@ void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector
}
void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -1466,14 +1491,12 @@ void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vec
img->generate_mipmaps();
}
- tex.texture = Ref<ImageTexture>();
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
tex.dirty = false;
}
Ref<Image> TextServerFallback::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -1490,7 +1513,7 @@ Ref<Image> TextServerFallback::font_get_texture_image(const RID &p_font_rid, con
}
void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1506,7 +1529,7 @@ void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const V
}
PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -1519,7 +1542,7 @@ PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_
}
Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1528,15 +1551,14 @@ Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vecto
Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- ret.push_back(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ ret.push_back(E.key);
}
return ret;
}
void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1547,7 +1569,7 @@ void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1558,7 +1580,7 @@ void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i
}
Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1586,7 +1608,7 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_
}
void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1601,7 +1623,7 @@ void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p
}
Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1622,7 +1644,7 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V
}
void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1637,7 +1659,7 @@ void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vect
}
Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1658,7 +1680,7 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec
}
void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1673,7 +1695,7 @@ void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector
}
Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -1689,7 +1711,7 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
}
void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1704,7 +1726,7 @@ void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
}
int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -1720,7 +1742,7 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co
}
void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1735,7 +1757,7 @@ void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const
}
RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
MutexLock lock(fd->mutex);
@@ -1760,8 +1782,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -1775,7 +1796,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
MutexLock lock(fd->mutex);
@@ -1800,8 +1821,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -1815,7 +1835,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
}
Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -1832,13 +1852,22 @@ Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, in
int error = FT_Load_Glyph(fd->cache[size]->face, FT_Get_Char_Index(fd->cache[size]->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
ERR_FAIL_COND_V(error, Dictionary());
- double h = fd->cache[size]->ascent;
+ if (fd->embolden != 0.f) {
+ FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64).
+ FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength);
+ }
+
+ if (fd->transform != Transform2D()) {
+ FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).
+ FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat);
+ }
+
double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
scale = scale * (double)p_size / (double)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
@@ -1856,7 +1885,7 @@ Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, in
}
Array TextServerFallback::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1872,7 +1901,7 @@ Array TextServerFallback::font_get_kerning_list(const RID &p_font_rid, int64_t p
}
void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1883,7 +1912,7 @@ void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p
}
void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1894,7 +1923,7 @@ void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_si
}
void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1905,7 +1934,7 @@ void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size,
}
Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1913,7 +1942,7 @@ Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -1945,7 +1974,7 @@ int64_t TextServerFallback::font_get_glyph_index(const RID &p_font_rid, int64_t
}
bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
@@ -1953,7 +1982,7 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeFallback *at_size = fd->cache.front()->get();
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -1964,14 +1993,14 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
}
String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeFallback *at_size = fd->cache.front()->get();
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -1989,16 +2018,15 @@ String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const
#endif
if (at_size) {
const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
- const int32_t *E = nullptr;
- while ((E = gl.next(E))) {
- chars = chars + String::chr(*E);
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ chars = chars + String::chr(E.key);
}
}
return chars;
}
void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");
ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");
@@ -2031,7 +2059,7 @@ void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2060,7 +2088,7 @@ void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2106,8 +2134,7 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2139,7 +2166,7 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
}
void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2185,8 +2212,7 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2218,7 +2244,7 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
}
bool TextServerFallback::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2230,7 +2256,7 @@ bool TextServerFallback::font_is_language_supported(const RID &p_font_rid, const
}
void TextServerFallback::font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2238,7 +2264,7 @@ void TextServerFallback::font_set_language_support_override(const RID &p_font_ri
}
bool TextServerFallback::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2246,7 +2272,7 @@ bool TextServerFallback::font_get_language_support_override(const RID &p_font_ri
}
void TextServerFallback::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2254,7 +2280,7 @@ void TextServerFallback::font_remove_language_support_override(const RID &p_font
}
PackedStringArray TextServerFallback::font_get_language_support_overrides(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -2266,7 +2292,7 @@ PackedStringArray TextServerFallback::font_get_language_support_overrides(const
}
bool TextServerFallback::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2278,7 +2304,7 @@ bool TextServerFallback::font_is_script_supported(const RID &p_font_rid, const S
}
void TextServerFallback::font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2286,7 +2312,7 @@ void TextServerFallback::font_set_script_support_override(const RID &p_font_rid,
}
bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2294,7 +2320,7 @@ bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid,
}
void TextServerFallback::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2304,7 +2330,7 @@ void TextServerFallback::font_remove_script_support_override(const RID &p_font_r
}
PackedStringArray TextServerFallback::font_get_script_support_overrides(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -2316,7 +2342,7 @@ PackedStringArray TextServerFallback::font_get_script_support_overrides(const RI
}
void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2326,7 +2352,7 @@ void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_r
}
Dictionary TextServerFallback::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2338,7 +2364,7 @@ Dictionary TextServerFallback::font_supported_feature_list(const RID &p_font_rid
}
Dictionary TextServerFallback::font_supported_variation_list(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2417,6 +2443,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
sd->orientation = p_orientation;
@@ -2543,6 +2570,31 @@ bool TextServerFallback::shaped_text_get_preserve_control(const RID &p_shaped) c
return sd->preserve_control;
}
+void TextServerFallback::shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->extra_spacing[p_spacing] != p_value) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->extra_spacing[p_spacing] = p_value;
+ invalidate(sd);
+ }
+}
+
+int64_t TextServerFallback::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+
+ MutexLock lock(sd->mutex);
+ return sd->extra_spacing[p_spacing];
+}
+
int64_t TextServerFallback::shaped_get_span_count(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
@@ -2795,6 +2847,8 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
}
RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -2823,6 +2877,9 @@ RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start,
new_sd->sort_valid = false;
new_sd->upos = sd->upos;
new_sd->uthk = sd->uthk;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
if (p_length > 0) {
new_sd->text = sd->text.substr(p_start - sd->start, p_length);
@@ -2891,7 +2948,7 @@ RID TextServerFallback::shaped_text_get_parent(const RID &p_shaped) const {
return sd->parent;
}
-double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) {
+double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<JustificationFlag> p_jst_flags) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
@@ -2906,7 +2963,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
int start_pos = 0;
int end_pos = sd->glyphs.size() - 1;
- if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
start = sd->glyphs.size() - 1;
@@ -2932,7 +2989,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
}
double justification_width;
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
if (sd->overrun_trim_data.trim_pos >= 0) {
end_pos = sd->overrun_trim_data.trim_pos;
justification_width = sd->width_trimmed;
@@ -2943,7 +3000,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
justification_width = sd->width;
}
- if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
@@ -2975,7 +3032,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
- if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {
double delta_width_per_space = (p_width - justification_width) / space_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
@@ -2993,7 +3050,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
sd->fit_width_minimum_reached = true;
}
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
sd->width = justification_width;
}
@@ -3128,7 +3185,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(const RID &p_shape
return true;
}
-void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) {
+void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
@@ -3140,14 +3197,14 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
- bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
- bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
- bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
- bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+ bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);
+ bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);
+ bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);
+ bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
sd->overrun_trim_data.trim_pos = -1;
sd->overrun_trim_data.ellipsis_pos = -1;
return;
@@ -3200,7 +3257,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -3402,10 +3459,13 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
}
- if (font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE) && is_whitespace(sd->text[j - sd->start])) {
- gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE);
- } else {
- gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_GLYPH);
+ if (j < sd->end - 1) {
+ // Do not add extra spacing to the last glyph of the string.
+ if (is_whitespace(sd->text[j - sd->start])) {
+ gl.advance += sd->extra_spacing[SPACING_SPACE];
+ } else {
+ gl.advance += sd->extra_spacing[SPACING_GLYPH];
+ }
}
sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
@@ -3532,9 +3592,9 @@ Size2 TextServerFallback::shaped_text_get_size(const RID &p_shaped) const {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2(sd->width, sd->ascent + sd->descent).ceil();
+ return Size2(sd->width, sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
} else {
- return Size2(sd->ascent + sd->descent, sd->width).ceil();
+ return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], sd->width).ceil();
}
}
@@ -3546,7 +3606,7 @@ double TextServerFallback::shaped_text_get_ascent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->ascent;
+ return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
double TextServerFallback::shaped_text_get_descent(const RID &p_shaped) const {
@@ -3557,7 +3617,7 @@ double TextServerFallback::shaped_text_get_descent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->descent;
+ return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
double TextServerFallback::shaped_text_get_width(const RID &p_shaped) const {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index c837029623..adb5cbb817 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXT_SERVER_FALLBACK_H
-#define TEXT_SERVER_FALLBACK_H
+#ifndef TEXT_SERVER_FB_H
+#define TEXT_SERVER_FB_H
/*************************************************************************/
/* Fallback Text Server provides simplified TS functionality, without */
@@ -67,9 +67,8 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
-#include <godot_cpp/templates/map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
@@ -80,8 +79,9 @@ using namespace godot;
#include "servers/text/text_server_extension.h"
+#include "core/object/worker_thread_pool.h"
+#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
-#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
@@ -106,8 +106,8 @@ class TextServerFallback : public TextServerExtension {
GDCLASS(TextServerFallback, TextServerExtension);
_THREAD_SAFE_CLASS_
- Map<StringName, int32_t> feature_sets;
- Map<int32_t, StringName> feature_sets_inv;
+ HashMap<StringName, int32_t> feature_sets;
+ HashMap<int32_t, StringName> feature_sets_inv;
void _insert_feature_sets();
_FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag);
@@ -144,7 +144,7 @@ class TextServerFallback : public TextServerExtension {
Vector2 advance;
};
- struct FontDataForSizeFallback {
+ struct FontForSizeFallback {
double ascent = 0.0;
double descent = 0.0;
double underline_position = 0.0;
@@ -152,21 +152,18 @@ class TextServerFallback : public TextServerExtension {
double scale = 1.0;
double oversampling = 1.0;
- int spacing_glyph = 0;
- int spacing_space = 0;
-
Vector2i size;
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- Map<Vector2i, Vector2> kerning_map;
+ HashMap<Vector2i, Vector2> kerning_map;
#ifdef MODULE_FREETYPE_ENABLED
FT_Face face = nullptr;
FT_StreamRec stream;
#endif
- ~FontDataForSizeFallback() {
+ ~FontForSizeFallback() {
#ifdef MODULE_FREETYPE_ENABLED
if (face != nullptr) {
FT_Done_Face(face);
@@ -175,7 +172,7 @@ class TextServerFallback : public TextServerExtension {
}
};
- struct FontDataFallback {
+ struct FontFallback {
Mutex mutex;
bool antialiased = true;
@@ -192,48 +189,46 @@ class TextServerFallback : public TextServerExtension {
double embolden = 0.0;
Transform2D transform;
- uint32_t style_flags = 0;
+ BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
- Map<Vector2i, FontDataForSizeFallback *> cache;
+ HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
bool face_init = false;
Dictionary supported_varaitions;
Dictionary feature_overrides;
// Language/script support override.
- Map<String, bool> language_support_overrides;
- Map<String, bool> script_support_overrides;
+ HashMap<String, bool> language_support_overrides;
+ HashMap<String, bool> script_support_overrides;
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
+ int face_index = 0;
- mutable ThreadWorkPool work_pool;
-
- ~FontDataFallback() {
- work_pool.finish();
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : cache) {
+ ~FontFallback() {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : cache) {
memdelete(E.value);
}
cache.clear();
}
};
- _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
#ifdef MODULE_MSDFGEN_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
#endif
- _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
- _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const;
- _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data);
+ _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontFallback *p_font_data);
void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
- _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size(const FontFallback *p_font_data, int p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -243,7 +238,7 @@ class TextServerFallback : public TextServerExtension {
}
}
- _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontFallback *p_font_data, const Vector2i &p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -294,7 +289,7 @@ class TextServerFallback : public TextServerExtension {
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
};
- Map<Variant, EmbeddedObject> objects;
+ HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
@@ -311,6 +306,7 @@ class TextServerFallback : public TextServerExtension {
double descent = 0.0; // Descent for horizontal layout, 1/2 of width for vertical.
double width = 0.0; // Width for horizontal layout, height for vertical.
double width_trimmed = 0.0;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
double upos = 0.0;
double uthk = 0.0;
@@ -325,7 +321,7 @@ class TextServerFallback : public TextServerExtension {
// Common data.
double oversampling = 1.0;
- mutable RID_PtrOwner<FontDataFallback> font_owner;
+ mutable RID_PtrOwner<FontFallback> font_owner;
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
void _realign(ShapedTextDataFallback *p_sd) const;
@@ -364,8 +360,13 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
- virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
- virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
+ virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override;
+ virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override;
virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override;
virtual String font_get_style_name(const RID &p_font_rid) const override;
@@ -431,9 +432,6 @@ public:
virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override;
virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_set_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing, int64_t p_value) override;
- virtual int64_t font_get_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing) const override;
-
virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override;
@@ -530,6 +528,9 @@ public:
virtual void shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(const RID &p_shaped) const override;
+ virtual void shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) override;
+ virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const override;
+
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
@@ -541,7 +542,7 @@ public:
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;
virtual RID shaped_text_get_parent(const RID &p_shaped) const override;
- virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(const RID &p_shaped) override;
@@ -553,7 +554,7 @@ public:
virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override;
virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override;
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
@@ -582,4 +583,4 @@ public:
~TextServerFallback();
};
-#endif // TEXT_SERVER_FALLBACK_H
+#endif // TEXT_SERVER_FB_H
diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp
index 35f9d10956..520ed5f799 100644
--- a/modules/tga/register_types.cpp
+++ b/modules/tga/register_types.cpp
@@ -34,11 +34,19 @@
static ImageLoaderTGA *image_loader_tga = nullptr;
-void register_tga_types() {
+void initialize_tga_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
image_loader_tga = memnew(ImageLoaderTGA);
ImageLoader::add_image_format_loader(image_loader_tga);
}
-void unregister_tga_types() {
+void uninitialize_tga_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_tga);
}
diff --git a/modules/tga/register_types.h b/modules/tga/register_types.h
index ae91aa560f..37cdab70dd 100644
--- a/modules/tga/register_types.h
+++ b/modules/tga/register_types.h
@@ -31,7 +31,9 @@
#ifndef TGA_REGISTER_TYPES_H
#define TGA_REGISTER_TYPES_H
-void register_tga_types();
-void unregister_tga_types();
+#include "modules/register_module_types.h"
+
+void initialize_tga_module(ModuleInitializationLevel p_level);
+void uninitialize_tga_module(ModuleInitializationLevel p_level);
#endif // TGA_REGISTER_TYPES_H
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index f658627574..9ed8a86415 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -34,14 +34,22 @@
static Ref<ResourceFormatLoaderTheora> resource_loader_theora;
-void register_theora_types() {
+void initialize_theora_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
resource_loader_theora.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_theora, true);
GDREGISTER_CLASS(VideoStreamTheora);
}
-void unregister_theora_types() {
+void uninitialize_theora_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
ResourceLoader::remove_resource_format_loader(resource_loader_theora);
resource_loader_theora.unref();
}
diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h
index d0c089ef49..2529b09306 100644
--- a/modules/theora/register_types.h
+++ b/modules/theora/register_types.h
@@ -31,7 +31,9 @@
#ifndef THEORA_REGISTER_TYPES_H
#define THEORA_REGISTER_TYPES_H
-void register_theora_types();
-void unregister_theora_types();
+#include "modules/register_module_types.h"
+
+void initialize_theora_module(ModuleInitializationLevel p_level);
+void uninitialize_theora_module(ModuleInitializationLevel p_level);
#endif // THEORA_REGISTER_TYPES_H
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 3f5140cc8c..c4462ba687 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -331,7 +331,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
Ref<Image> img;
img.instantiate();
img->create(w, h, false, Image::FORMAT_RGBA8);
- texture->create_from_image(img);
+ texture->set_image(img);
} else {
/* tear down the partial theora setup */
@@ -669,13 +669,13 @@ void VideoStreamTheora::_bind_methods() {
////////////
-RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+Ref<Resource> ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
if (f.is_null()) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
- return RES();
+ return Ref<Resource>();
}
VideoStreamTheora *stream = memnew(VideoStreamTheora);
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 6fa7313fad..00d799dc24 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -99,7 +99,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
Ref<ImageTexture> texture;
- AudioMixCallback mix_callback;
+ AudioMixCallback mix_callback = nullptr;
void *mix_udata = nullptr;
bool paused = false;
@@ -170,7 +170,7 @@ protected:
static void _bind_methods();
public:
- Ref<VideoStreamPlayback> instance_playback() override {
+ Ref<VideoStreamPlayback> instantiate_playback() override {
Ref<VideoStreamPlaybackTheora> pb = memnew(VideoStreamPlaybackTheora);
pb->set_audio_track(audio_track);
pb->set_file(file);
@@ -186,10 +186,10 @@ public:
class ResourceFormatLoaderTheora : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
};
-#endif
+#endif // VIDEO_STREAM_THEORA_H
diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h
index c147861c26..0d3de93956 100644
--- a/modules/tinyexr/image_loader_tinyexr.h
+++ b/modules/tinyexr/image_loader_tinyexr.h
@@ -40,4 +40,4 @@ public:
ImageLoaderTinyEXR();
};
-#endif
+#endif // IMAGE_LOADER_TINYEXR_H
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 5fa6ace827..661fe343af 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -141,13 +141,14 @@ static int get_channel_count(Image::Format p_format) {
}
}
-Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale) {
+Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale) {
Image::Format format = p_img->get_format();
if (!is_supported_format(format)) {
// Format not supported
print_error("Image format not supported for saving as EXR. Consider saving as PNG.");
- return ERR_UNAVAILABLE;
+
+ return Vector<uint8_t>();
}
EXRHeader header;
@@ -175,15 +176,15 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
};
int channel_count = get_channel_count(format);
- ERR_FAIL_COND_V(channel_count < 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(p_grayscale && channel_count != 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(channel_count < 0, Vector<uint8_t>());
+ ERR_FAIL_COND_V(p_grayscale && channel_count != 1, Vector<uint8_t>());
int target_pixel_type = get_target_pixel_type(format);
- ERR_FAIL_COND_V(target_pixel_type < 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(target_pixel_type < 0, Vector<uint8_t>());
int target_pixel_type_size = get_pixel_type_size(target_pixel_type);
- ERR_FAIL_COND_V(target_pixel_type_size < 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(target_pixel_type_size < 0, Vector<uint8_t>());
SrcPixelType src_pixel_type = get_source_pixel_type(format);
- ERR_FAIL_COND_V(src_pixel_type == SRC_UNSUPPORTED, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(src_pixel_type == SRC_UNSUPPORTED, Vector<uint8_t>());
const int pixel_count = p_img->get_width() * p_img->get_height();
const int *channel_mapping = channel_mappings[channel_count - 1];
@@ -270,15 +271,25 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
const char *err = nullptr;
size_t bytes = SaveEXRImageToMemory(&image, &header, &mem, &err);
+ if (err && *err != OK) {
+ return Vector<uint8_t>();
+ }
+ Vector<uint8_t> buffer;
+ buffer.resize(bytes);
+ memcpy(buffer.ptrw(), mem, bytes);
+ free(mem);
+ return buffer;
+}
- if (bytes == 0) {
- print_error(String("Saving EXR failed. Error: {0}").format(varray(err)));
+Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale) {
+ const Vector<uint8_t> buffer = save_exr_buffer(p_img, p_grayscale);
+ if (buffer.size() == 0) {
+ print_error(String("Saving EXR failed."));
return ERR_FILE_CANT_WRITE;
} else {
Ref<FileAccess> ref = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_V(ref.is_null(), ERR_FILE_CANT_WRITE);
- ref->store_buffer(mem, bytes);
- free(mem);
+ ref->store_buffer(buffer.ptr(), buffer.size());
}
return OK;
diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h
index 5bf95b265e..8f97f55408 100644
--- a/modules/tinyexr/image_saver_tinyexr.h
+++ b/modules/tinyexr/image_saver_tinyexr.h
@@ -34,5 +34,6 @@
#include "core/os/os.h"
Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
+Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale);
#endif // IMAGE_SAVER_TINYEXR_H
diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp
index 0879a55b6e..c5897f37c3 100644
--- a/modules/tinyexr/register_types.cpp
+++ b/modules/tinyexr/register_types.cpp
@@ -35,14 +35,23 @@
static ImageLoaderTinyEXR *image_loader_tinyexr = nullptr;
-void register_tinyexr_types() {
+void initialize_tinyexr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
image_loader_tinyexr = memnew(ImageLoaderTinyEXR);
ImageLoader::add_image_format_loader(image_loader_tinyexr);
Image::save_exr_func = save_exr;
+ Image::save_exr_buffer_func = save_exr_buffer;
}
-void unregister_tinyexr_types() {
+void uninitialize_tinyexr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_tinyexr);
Image::save_exr_func = nullptr;
diff --git a/modules/tinyexr/register_types.h b/modules/tinyexr/register_types.h
index 4c34757d8b..bb50e024cb 100644
--- a/modules/tinyexr/register_types.h
+++ b/modules/tinyexr/register_types.h
@@ -31,7 +31,9 @@
#ifndef TINYEXR_REGISTER_TYPES_H
#define TINYEXR_REGISTER_TYPES_H
-void register_tinyexr_types();
-void unregister_tinyexr_types();
+#include "modules/register_module_types.h"
+
+void initialize_tinyexr_module(ModuleInitializationLevel p_level);
+void uninitialize_tinyexr_module(ModuleInitializationLevel p_level);
#endif // TINYEXR_REGISTER_TYPES_H
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
index 7345901829..9e041a0c6e 100644
--- a/modules/upnp/register_types.cpp
+++ b/modules/upnp/register_types.cpp
@@ -35,10 +35,17 @@
#include "upnp.h"
#include "upnp_device.h"
-void register_upnp_types() {
+void initialize_upnp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_CLASS(UPNP);
GDREGISTER_CLASS(UPNPDevice);
}
-void unregister_upnp_types() {
+void uninitialize_upnp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h
index e4482314f8..ef559cc4e8 100644
--- a/modules/upnp/register_types.h
+++ b/modules/upnp/register_types.h
@@ -31,7 +31,9 @@
#ifndef UPNP_REGISTER_TYPES_H
#define UPNP_REGISTER_TYPES_H
-void register_upnp_types();
-void unregister_upnp_types();
+#include "modules/register_module_types.h"
+
+void initialize_upnp_module(ModuleInitializationLevel p_level);
+void uninitialize_upnp_module(ModuleInitializationLevel p_level);
#endif // UPNP_REGISTER_TYPES_H
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
index 6d87b42c56..be7ede3ee0 100644
--- a/modules/upnp/upnp.h
+++ b/modules/upnp/upnp.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GODOT_UPNP_H
-#define GODOT_UPNP_H
+#ifndef UPNP_H
+#define UPNP_H
#include "core/object/ref_counted.h"
@@ -121,4 +121,4 @@ public:
VARIANT_ENUM_CAST(UPNP::UPNPResult)
-#endif // GODOT_UPNP_H
+#endif // UPNP_H
diff --git a/modules/upnp/upnp_device.h b/modules/upnp/upnp_device.h
index 18491dce27..1c9dc82df5 100644
--- a/modules/upnp/upnp_device.h
+++ b/modules/upnp/upnp_device.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GODOT_UPNP_DEVICE_H
-#define GODOT_UPNP_DEVICE_H
+#ifndef UPNP_DEVICE_H
+#define UPNP_DEVICE_H
#include "core/object/ref_counted.h"
@@ -90,4 +90,4 @@ private:
VARIANT_ENUM_CAST(UPNPDevice::IGDStatus)
-#endif // GODOT_UPNP_DEVICE_H
+#endif // UPNP_DEVICE_H
diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp
index 6242c8faa3..8bb7e780de 100644
--- a/modules/vhacd/register_types.cpp
+++ b/modules/vhacd/register_types.cpp
@@ -89,10 +89,18 @@ static Vector<Vector<Vector3>> convex_decompose(const real_t *p_vertices, int p_
return ret;
}
-void register_vhacd_types() {
+void initialize_vhacd_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
Mesh::convex_decomposition_function = convex_decompose;
}
-void unregister_vhacd_types() {
+void uninitialize_vhacd_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
Mesh::convex_decomposition_function = nullptr;
}
diff --git a/modules/vhacd/register_types.h b/modules/vhacd/register_types.h
index e0e34d494d..04ec180cd6 100644
--- a/modules/vhacd/register_types.h
+++ b/modules/vhacd/register_types.h
@@ -31,7 +31,9 @@
#ifndef VHACD_REGISTER_TYPES_H
#define VHACD_REGISTER_TYPES_H
-void register_vhacd_types();
-void unregister_vhacd_types();
+#include "modules/register_module_types.h"
+
+void initialize_vhacd_module(ModuleInitializationLevel p_level);
+void uninitialize_vhacd_module(ModuleInitializationLevel p_level);
#endif // VHACD_REGISTER_TYPES_H
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index 06fa90eb29..1e9755f45f 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -376,6 +376,12 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::VECTOR3I:
color = Color(0.84, 0.49, 0.93);
break;
+ case Variant::VECTOR4:
+ color = Color(0.84, 0.49, 0.94);
+ break;
+ case Variant::VECTOR4I:
+ color = Color(0.84, 0.49, 0.94);
+ break;
case Variant::TRANSFORM2D:
color = Color(0.77, 0.93, 0.41);
break;
@@ -647,6 +653,14 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Control::get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
};
+ // Visual script specific theme for MSDF font.
+ Ref<Theme> vstheme;
+ vstheme.instantiate();
+ Ref<Font> label_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_msdf", "EditorFonts");
+ vstheme->set_font("font", "Label", label_font);
+ vstheme->set_font("font", "LineEdit", label_font);
+ vstheme->set_font("font", "Button", label_font);
+
Ref<Texture2D> seq_port = Control::get_theme_icon(SNAME("VisualShaderPort"), SNAME("EditorIcons"));
List<int> node_ids;
script->get_node_list(&node_ids);
@@ -673,8 +687,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_meta("__vnode", node);
gnode->set_name(itos(E));
- gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E));
- gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E), CONNECT_DEFERRED);
+ gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved).bind(E));
+ gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node).bind(E), CONNECT_DEFERRED);
{
Ref<VisualScriptFunction> v = node;
@@ -694,7 +708,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *btn = memnew(Button);
btn->set_text(TTR("Add Input Port"));
hbnc->add_child(btn);
- btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E), CONNECT_DEFERRED);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port).bind(E), CONNECT_DEFERRED);
}
if (nd_list->is_output_port_editable()) {
if (nd_list->is_input_port_editable()) {
@@ -704,7 +718,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *btn = memnew(Button);
btn->set_text(TTR("Add Output Port"));
hbnc->add_child(btn);
- btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E), CONNECT_DEFERRED);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port).bind(E), CONNECT_DEFERRED);
}
gnode->add_child(hbnc);
} else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
@@ -714,7 +728,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
line_edit->set_expand_to_text_length_enabled(true);
line_edit->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts")));
gnode->add_child(line_edit);
- line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E));
+ line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed).bind(E));
} else {
String text = node->get_text();
if (!text.is_empty()) {
@@ -730,7 +744,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
gnode->set_comment(true);
gnode->set_resizable(true);
gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE);
- gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E));
+ gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized).bind(E));
}
if (node_styles.has(node->get_category())) {
@@ -833,8 +847,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(left_name);
name_box->set_expand_to_text_length_enabled(true);
- name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E));
- name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E, i, true));
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size).bind(E));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out).bind(name_box, E, i, true));
} else {
hbc->add_child(memnew(Label(left_name)));
}
@@ -847,13 +861,13 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
opbtn->select(left_type);
opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
hbc->add_child(opbtn);
- opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E, i, true), CONNECT_DEFERRED);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type).bind(E, i, true), CONNECT_DEFERRED);
}
Button *rmbtn = memnew(Button);
rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
hbc->add_child(rmbtn);
- rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E, i), CONNECT_DEFERRED);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port).bind(E, i), CONNECT_DEFERRED);
} else {
hbc->add_child(memnew(Label(left_name)));
}
@@ -872,7 +886,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
if (left_type == Variant::COLOR) {
button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
- button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value));
+ button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button).bind(button, value));
} else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
Ref<Resource> res = value;
Array arr;
@@ -881,11 +895,53 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
} else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
- button->set_text(pi.hint_string.get_slice(",", value));
+ bool found = false;
+ const Vector<String> options = pi.hint_string.split(",");
+ int64_t current_val = 0;
+ for (const String &option : options) {
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != 1) {
+ current_val = text_split[1].to_int();
+ }
+ if (value.operator int() == current_val) {
+ button->set_text(text_split[0]);
+ found = true;
+ break;
+ }
+ current_val += 1;
+ }
+ if (!found) {
+ button->set_text(value);
+ }
+ } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_FLAGS) {
+ Vector<String> value_texts;
+ const Vector<String> options = pi.hint_string.split(",");
+ uint32_t v = value;
+ for (const String &option : options) {
+ uint32_t current_val;
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != -1) {
+ current_val = text_split[1].to_int();
+ } else {
+ current_val = 1 << i;
+ }
+ if ((v & current_val) == current_val) {
+ value_texts.push_back(text_split[0]);
+ }
+ }
+ if (value_texts.size() != 0) {
+ String value_text = value_texts[0];
+ for (const String &text : value_texts) {
+ value_text += " | " + text;
+ }
+ button->set_text(value_text);
+ } else {
+ button->set_text(value);
+ }
} else {
button->set_text(value);
}
- button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E, i));
+ button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited).bind(button, E, i));
hbc2->add_child(button);
}
} else {
@@ -909,7 +965,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Button *rmbtn = memnew(Button);
rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
hbc->add_child(rmbtn);
- rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E, i), CONNECT_DEFERRED);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port).bind(E, i), CONNECT_DEFERRED);
if (nd_list->is_output_port_type_editable()) {
OptionButton *opbtn = memnew(OptionButton);
@@ -919,7 +975,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
opbtn->select(right_type);
opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
hbc->add_child(opbtn);
- opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E, i, false), CONNECT_DEFERRED);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type).bind(E, i, false), CONNECT_DEFERRED);
}
if (nd_list->is_output_port_name_editable()) {
@@ -928,8 +984,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
name_box->set_text(right_name);
name_box->set_expand_to_text_length_enabled(true);
- name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E));
- name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E, i, false));
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size).bind(E));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out).bind(name_box, E, i, false));
} else {
hbc->add_child(memnew(Label(right_name)));
}
@@ -960,9 +1016,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
slot_idx++;
}
-
graph->add_child(gnode);
-
+ gnode->set_theme(vstheme);
if (gnode->is_comment()) {
graph->move_child(gnode, 0);
}
@@ -973,6 +1028,9 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
+ float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature");
+ graph->set_connection_lines_curvature(graph_lines_curvature);
+
// Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything.
graph->call_deferred(SNAME("set_scroll_ofs"), script->get_scroll() * EDSCALE);
updating_graph = false;
@@ -1175,7 +1233,7 @@ void VisualScriptEditor::_member_selected() {
selected = ti->get_metadata(0);
if (ti->get_parent() == members->get_root()->get_first_child()) {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
@@ -1412,7 +1470,7 @@ void VisualScriptEditor::_add_func_input() {
func_input_vbox->add_child(hbox);
hbox->set_meta("id", hbox->get_index());
- delete_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_func_input), varray(hbox));
+ delete_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_func_input).bind(hbox));
name_box->select_all();
name_box->grab_focus();
@@ -1433,7 +1491,11 @@ void VisualScriptEditor::_deselect_input_names() {
}
}
-void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) {
+void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button) {
+ if (p_mouse_button != MouseButton::LEFT) {
+ return;
+ }
+
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
TreeItem *root = members->get_root();
@@ -1603,12 +1665,12 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
List<VisualScript::DataConnection> data_connections;
script->get_data_connection_list(&data_connections);
- HashMap<int, Set<int>> conn_map;
+ HashMap<int, RBSet<int>> conn_map;
for (const VisualScript::DataConnection &E : data_connections) {
if (E.from_node == p_id && E.from_port == p_port) {
// Push into the connections map.
if (!conn_map.has(E.to_node)) {
- conn_map.set(E.to_node, Set<int>());
+ conn_map.insert(E.to_node, RBSet<int>());
}
conn_map[E.to_node].insert(E.to_port);
}
@@ -1617,11 +1679,9 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port);
undo_redo->add_do_method(this, "_update_graph", p_id);
- List<int> keys;
- conn_map.get_key_list(&keys);
- for (const int &E : keys) {
- for (const Set<int>::Element *F = conn_map[E].front(); F; F = F->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E, F->get());
+ for (const KeyValue<int, RBSet<int>> &E : conn_map) {
+ for (const int &F : E.value) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F);
}
}
@@ -1762,14 +1822,14 @@ void VisualScriptEditor::_on_nodes_paste() {
return;
}
- Map<int, int> remap;
+ HashMap<int, int> remap;
undo_redo->create_action(TTR("Paste VisualScript Nodes"));
int idc = script->get_available_id() + 1;
- Set<int> to_select;
+ RBSet<int> to_select;
- Set<Vector2> existing_positions;
+ RBSet<Vector2> existing_positions;
{
List<int> nodes;
@@ -1808,14 +1868,14 @@ void VisualScriptEditor::_on_nodes_paste() {
undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
}
- for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
+ for (const VisualScript::SequenceConnection &E : clipboard->sequence_connections) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E.from_node], E.from_output, remap[E.to_node]);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E.from_node], E.from_output, remap[E.to_node]);
}
- for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
+ for (const VisualScript::DataConnection &E : clipboard->data_connections) {
+ undo_redo->add_do_method(script.ptr(), "data_connect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port);
}
undo_redo->add_do_method(this, "_update_graph");
@@ -1883,7 +1943,7 @@ void VisualScriptEditor::_on_nodes_delete() {
}
void VisualScriptEditor::_on_nodes_duplicate() {
- Set<int> to_duplicate;
+ RBSet<int> to_duplicate;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
@@ -1902,20 +1962,20 @@ void VisualScriptEditor::_on_nodes_duplicate() {
undo_redo->create_action(TTR("Duplicate VisualScript Nodes"));
int idc = script->get_available_id() + 1;
- Set<int> to_select;
+ RBSet<int> to_select;
HashMap<int, int> remap;
- for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) {
+ for (const int &F : to_duplicate) {
// Duplicate from the specific function but place it into the default func as it would lack the connections.
- Ref<VisualScriptNode> node = script->get_node(F->get());
+ Ref<VisualScriptNode> node = script->get_node(F);
Ref<VisualScriptNode> dupe = node->duplicate(true);
int new_id = idc++;
- remap.set(F->get(), new_id);
+ remap.insert(F, new_id);
to_select.insert(new_id);
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20));
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F) + Vector2(20, 20));
undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
}
@@ -1961,15 +2021,6 @@ void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) {
}
new_connect_node_select->select_from_visual_script(script, false); // do not reset text
-
- // Ensure that the dialog fits inside the graph.
- Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
- pos.x = pos.x > bounds.x ? bounds.x : pos.x;
- pos.y = pos.y > bounds.y ? bounds.y : pos.y;
-
- if (pos != Vector2()) {
- new_connect_node_select->set_position(pos);
- }
}
void VisualScriptEditor::input(const Ref<InputEvent> &p_event) {
@@ -2163,7 +2214,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
String(d["type"]) == "files" ||
String(d["type"]) == "nodes")) {
if (String(d["type"]) == "obj_property") {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."));
@@ -2171,7 +2222,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
}
if (String(d["type"]) == "nodes") {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node."));
@@ -2179,7 +2230,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
}
if (String(d["type"]) == "visual_script_variable_drag") {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter."));
@@ -2242,7 +2293,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "visual_script_variable_drag") {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool use_set = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool use_set = Input::get_singleton()->is_key_pressed(Key::CTRL);
@@ -2351,7 +2402,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "files") {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool use_preload = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool use_preload = Input::get_singleton()->is_key_pressed(Key::CTRL);
@@ -2414,7 +2465,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
return;
}
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool use_node = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool use_node = Input::get_singleton()->is_key_pressed(Key::CTRL);
@@ -2479,7 +2530,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
Node *node = Object::cast_to<Node>(obj);
Vector2 pos = _get_pos_in_graph(p_point);
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool use_get = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool use_get = Input::get_singleton()->is_key_pressed(Key::CTRL);
@@ -2611,11 +2662,11 @@ void VisualScriptEditor::_button_resource_previewed(const String &p_path, const
void VisualScriptEditor::apply_code() {
}
-RES VisualScriptEditor::get_edited_resource() const {
+Ref<Resource> VisualScriptEditor::get_edited_resource() const {
return script;
}
-void VisualScriptEditor::set_edited_resource(const RES &p_res) {
+void VisualScriptEditor::set_edited_resource(const Ref<Resource> &p_res) {
ERR_FAIL_COND(script.is_valid());
ERR_FAIL_COND(p_res.is_null());
script = p_res;
@@ -3177,7 +3228,7 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro
}
}
-VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) {
+VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, RBSet<int> &visited_nodes) {
VisualScriptNode::TypeGuess tg;
tg.type = Variant::NIL;
@@ -3228,7 +3279,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac
}
void VisualScriptEditor::_port_action_menu(int p_option) {
- Set<int> vn;
+ RBSet<int> vn;
switch (p_option) {
case CREATE_CALL_SET_GET: {
@@ -3333,14 +3384,14 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua
}
void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
-#ifdef OSX_ENABLED
+#ifdef MACOS_ENABLED
bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META);
#else
bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
Vector2 pos = _get_pos_in_graph(port_action_pos);
- Set<int> vn;
+ RBSet<int> vn;
bool port_node_exists = true;
if (drop_position != Vector2()) {
@@ -3930,13 +3981,16 @@ void VisualScriptEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
+ graph->set_minimap_opacity(EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"));
+ graph->set_connection_lines_curvature(EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"));
+ _update_graph();
} break;
case NOTIFICATION_READY: {
variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
- variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph), varray(-1), CONNECT_DEFERRED);
+ variable_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph).bind(-1), CONNECT_DEFERRED);
signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
- signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph), varray(-1), CONNECT_DEFERRED);
+ signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph).bind(-1), CONNECT_DEFERRED);
[[fallthrough]];
}
case NOTIFICATION_THEME_CHANGED: {
@@ -4096,8 +4150,8 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_CREATE_FUNCTION: {
// Create Function.
- Map<int, Ref<VisualScriptNode>> nodes;
- Set<int> selections;
+ HashMap<int, Ref<VisualScriptNode>> nodes;
+ RBSet<int> selections;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
@@ -4120,18 +4174,18 @@ void VisualScriptEditor::_menu_option(int p_what) {
return; // nothing to be done if there are no valid nodes selected
}
- Set<VisualScript::SequenceConnection> seqmove;
- Set<VisualScript::DataConnection> datamove;
+ RBSet<VisualScript::SequenceConnection> seqmove;
+ RBSet<VisualScript::DataConnection> datamove;
- Set<VisualScript::SequenceConnection> seqext;
- Set<VisualScript::DataConnection> dataext;
+ RBSet<VisualScript::SequenceConnection> seqext;
+ RBSet<VisualScript::DataConnection> dataext;
int start_node = -1;
- Set<int> end_nodes;
+ RBSet<int> end_nodes;
if (nodes.size() == 1) {
- Ref<VisualScriptNode> nd = script->get_node(nodes.front()->key());
+ Ref<VisualScriptNode> nd = script->get_node(nodes.begin()->key);
if (nd.is_valid() && nd->has_input_sequence_port()) {
- start_node = nodes.front()->key();
+ start_node = nodes.begin()->key;
} else {
EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
return;
@@ -4169,8 +4223,8 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
} else {
// Pick the node with input sequence.
- Set<int> nodes_from;
- Set<int> nodes_to;
+ RBSet<int> nodes_from;
+ RBSet<int> nodes_to;
for (const VisualScript::SequenceConnection &E : seqs) {
if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
seqmove.insert(E);
@@ -4196,9 +4250,9 @@ void VisualScriptEditor::_menu_option(int p_what) {
// If we still don't have a start node then,
// run through the nodes and select the first tree node,
// i.e. node without any input sequence but output sequence.
- for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) {
- if (!nodes_to.has(E->get())) {
- start_node = E->get();
+ for (const int &E : nodes_from) {
+ if (!nodes_to.has(E)) {
+ start_node = E;
}
}
}
@@ -4267,13 +4321,13 @@ void VisualScriptEditor::_menu_option(int p_what) {
// Move the nodes.
// Handles reconnection of sequence connections on undo, start here in case of issues.
- for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E->get().from_node, E->get().from_output, E->get().to_node);
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node);
+ for (const VisualScript::SequenceConnection &E : seqext) {
+ undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E.from_node, E.from_output, E.to_node);
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
}
- for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "data_disconnect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ for (const VisualScript::DataConnection &E : dataext) {
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", E.from_node, E.from_port, E.to_node, E.to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
}
// I don't really think we need support for non sequenced functions at this moment.
@@ -4281,24 +4335,24 @@ void VisualScriptEditor::_menu_option(int p_what) {
// Could fail with the new changes, start here when searching for bugs in create function shortcut.
int m = 1;
- for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) {
+ for (const int &G : end_nodes) {
Ref<VisualScriptReturn> ret_node;
ret_node.instantiate();
int ret_id = fn_id + (m++);
selections.insert(ret_id);
- Vector2 posi = _get_available_pos(false, script->get_node_position(G->get()) + Vector2(80, -100));
+ Vector2 posi = _get_available_pos(false, script->get_node_position(G) + Vector2(80, -100));
undo_redo->add_do_method(script.ptr(), "add_node", ret_id, ret_node, posi);
undo_redo->add_undo_method(script.ptr(), "remove_node", ret_id);
- undo_redo->add_do_method(script.ptr(), "sequence_connect", G->get(), 0, ret_id);
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", G, 0, ret_id);
// Add data outputs from each of the end_nodes.
- Ref<VisualScriptNode> vsn = script->get_node(G->get());
+ Ref<VisualScriptNode> vsn = script->get_node(G);
if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) {
ret_node->set_enable_return_value(true);
// Use the zeroth data port cause that's the likely one that is planned to be used.
ret_node->set_return_type(vsn->get_output_value_port_info(0).type);
- undo_redo->add_do_method(script.ptr(), "data_connect", G->get(), 0, ret_id, 0);
+ undo_redo->add_do_method(script.ptr(), "data_connect", G, 0, ret_id, 0);
}
}
@@ -4333,7 +4387,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
// This is likely going to be very slow and I am not sure if I should keep it,
// but I hope that it will not be a problem considering that we won't be creating functions so frequently,
// and cyclic connections would be a problem but hopefully we won't let them get to this point.
-void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) {
+void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const RBSet<int> &p_selected, RBSet<int> &r_end_nodes) {
for (const VisualScript::SequenceConnection &E : p_seqs) {
int from = E.from_node;
int to = E.to_node;
@@ -4347,7 +4401,11 @@ void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::Sequence
}
}
-void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
+void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {
+ if (p_button != MouseButton::RIGHT) {
+ return;
+ }
+
TreeItem *ti = members->get_selected();
ERR_FAIL_COND(!ti);
@@ -4548,11 +4606,11 @@ VisualScriptEditor::VisualScriptEditor() {
members_section->add_margin_child(TTR("Members:"), members, true);
members->set_custom_minimum_size(Size2(0, 50 * EDSCALE));
members->set_hide_root(true);
- members->connect("button_pressed", callable_mp(this, &VisualScriptEditor::_member_button));
+ members->connect("button_clicked", callable_mp(this, &VisualScriptEditor::_member_button));
members->connect("item_edited", callable_mp(this, &VisualScriptEditor::_member_edited));
- members->connect("cell_selected", callable_mp(this, &VisualScriptEditor::_member_selected), varray(), CONNECT_DEFERRED);
+ members->connect("cell_selected", callable_mp(this, &VisualScriptEditor::_member_selected), CONNECT_DEFERRED);
members->connect("gui_input", callable_mp(this, &VisualScriptEditor::_members_gui_input));
- members->connect("item_rmb_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected));
+ members->connect("item_mouse_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected));
members->set_allow_rmb_select(true);
members->set_allow_reselect(true);
members->set_hide_folding(true);
@@ -4576,7 +4634,8 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ graph->set_show_zoom_label(true);
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));
@@ -4588,6 +4647,8 @@ VisualScriptEditor::VisualScriptEditor() {
graph->set_drag_forwarding(this);
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
+ float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature");
+ graph->set_connection_lines_curvature(graph_lines_curvature);
graph->hide();
graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed));
@@ -4662,7 +4723,7 @@ VisualScriptEditor::VisualScriptEditor() {
function_create_dialog = memnew(ConfirmationDialog);
function_create_dialog->set_title(TTR("Create Function"));
function_create_dialog->add_child(function_vb);
- function_create_dialog->get_ok_button()->set_text(TTR("Create"));
+ function_create_dialog->set_ok_button_text(TTR("Create"));
function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
add_child(function_create_dialog);
@@ -4706,7 +4767,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
edit_signal_dialog = memnew(AcceptDialog);
- edit_signal_dialog->get_ok_button()->set_text(TTR("Close"));
+ edit_signal_dialog->set_ok_button_text(TTR("Close"));
add_child(edit_signal_dialog);
signal_editor = memnew(VisualScriptEditorSignalEdit);
@@ -4716,7 +4777,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_signal_edit->edit(signal_editor);
edit_variable_dialog = memnew(AcceptDialog);
- edit_variable_dialog->get_ok_button()->set_text(TTR("Close"));
+ edit_variable_dialog->set_ok_button_text(TTR("Close"));
add_child(edit_variable_dialog);
variable_editor = memnew(VisualScriptEditorVariableEdit);
@@ -4766,12 +4827,15 @@ VisualScriptEditor::VisualScriptEditor() {
base_type_map.insert("Rect2i", Variant::RECT2I);
base_type_map.insert("Vector3", Variant::VECTOR3);
base_type_map.insert("Vector3i", Variant::VECTOR3I);
+ base_type_map.insert("Vector4", Variant::VECTOR4);
+ base_type_map.insert("Vector4i", Variant::VECTOR4I);
base_type_map.insert("Transform2D", Variant::TRANSFORM2D);
base_type_map.insert("Plane", Variant::PLANE);
base_type_map.insert("Quaternion", Variant::QUATERNION);
base_type_map.insert("AABB", Variant::AABB);
base_type_map.insert("Basis", Variant::BASIS);
base_type_map.insert("Transform3D", Variant::TRANSFORM3D);
+ base_type_map.insert("Projection", Variant::PROJECTION);
base_type_map.insert("Color", Variant::COLOR);
base_type_map.insert("NodePath", Variant::NODE_PATH);
base_type_map.insert("RID", Variant::RID);
@@ -4795,7 +4859,7 @@ VisualScriptEditor::~VisualScriptEditor() {
memdelete(variable_editor);
}
-static ScriptEditorBase *create_editor(const RES &p_resource) {
+static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) {
if (Object::cast_to<VisualScript>(*p_resource)) {
return memnew(VisualScriptEditor);
}
@@ -4839,7 +4903,7 @@ Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &
}
VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr;
-Map<String, REF> VisualScriptCustomNodes::custom_nodes;
+HashMap<String, Ref<RefCounted>> VisualScriptCustomNodes::custom_nodes;
VisualScriptCustomNodes::VisualScriptCustomNodes() {
singleton = this;
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index fcfd44cecd..a6df7bba73 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VISUALSCRIPT_EDITOR_H
-#define VISUALSCRIPT_EDITOR_H
+#ifndef VISUAL_SCRIPT_EDITOR_H
+#define VISUAL_SCRIPT_EDITOR_H
#include "../visual_script.h"
#include "editor/create_dialog.h"
@@ -142,9 +142,9 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String>> args;
};
- Map<StringName, Color> node_colors;
+ HashMap<StringName, Color> node_colors;
HashMap<StringName, Ref<StyleBox>> node_styles;
- Map<StringName, Variant::Type> base_type_map;
+ HashMap<StringName, Variant::Type> base_type_map;
void _update_graph_connections();
void _update_graph(int p_only_id = -1);
@@ -159,11 +159,11 @@ class VisualScriptEditor : public ScriptEditorBase {
String _validate_name(const String &p_name) const;
struct Clipboard {
- Map<int, Ref<VisualScriptNode>> nodes;
- Map<int, Vector2> nodes_positions;
+ HashMap<int, Ref<VisualScriptNode>> nodes;
+ HashMap<int, Vector2> nodes_positions;
- Set<VisualScript::SequenceConnection> sequence_connections;
- Set<VisualScript::DataConnection> data_connections;
+ RBSet<VisualScript::SequenceConnection> sequence_connections;
+ RBSet<VisualScript::DataConnection> data_connections;
};
static Clipboard *clipboard;
@@ -213,7 +213,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _end_node_move();
void _move_node(int p_id, const Vector2 &p_to);
- void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes);
+ void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const RBSet<int> &p_selected, RBSet<int> &r_end_nodes);
void _node_moved(Vector2 p_from, Vector2 p_to, int p_id);
void _remove_node(int p_id);
@@ -226,7 +226,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _update_available_nodes();
- void _member_button(Object *p_item, int p_column, int p_button);
+ void _member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button);
void _expression_text_changed(const String &p_text, int p_id);
void _add_input_port(int p_id);
@@ -287,9 +287,9 @@ class VisualScriptEditor : public ScriptEditorBase {
void _draw_color_over_button(Object *obj, Color p_color);
void _button_resource_previewed(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud);
- VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &p_visited_nodes);
+ VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, RBSet<int> &p_visited_nodes);
- void _member_rmb_selected(const Vector2 &p_pos);
+ void _member_rmb_selected(const Vector2 &p_pos, MouseButton p_button);
void _member_option(int p_option);
void _toggle_scripts_pressed();
@@ -303,8 +303,8 @@ public:
virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
virtual void apply_code() override;
- virtual RES get_edited_resource() const override;
- virtual void set_edited_resource(const RES &p_res) override;
+ virtual Ref<Resource> get_edited_resource() const override;
+ virtual void set_edited_resource(const Ref<Resource> &p_res) override;
virtual void enable_editor() override;
virtual Vector<String> get_functions() override;
virtual void reload_text() override;
@@ -359,7 +359,7 @@ protected:
static void _bind_methods();
static VisualScriptCustomNodes *singleton;
- static Map<String, REF> custom_nodes;
+ static HashMap<String, Ref<RefCounted>> custom_nodes;
static Ref<VisualScriptNode> create_node_custom(const String &p_name);
public:
@@ -374,4 +374,4 @@ public:
#endif
-#endif // VISUALSCRIPT_EDITOR_H
+#endif // VISUAL_SCRIPT_EDITOR_H
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index 07929e5c0e..f98ce5bb26 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -109,11 +109,7 @@ void VisualScriptPropertySelector::_confirmed() {
}
void VisualScriptPropertySelector::_item_selected() {
- if (results_tree->get_selected()->has_meta("description")) {
- help_bit->set_text(results_tree->get_selected()->get_meta("description"));
- } else {
- help_bit->set_text("No description available");
- }
+ help_bit->set_text(results_tree->get_selected()->get_meta("description", "No description available"));
}
void VisualScriptPropertySelector::_hide_requested() {
@@ -186,7 +182,7 @@ void VisualScriptPropertySelector::select_method_from_base_type(const String &p_
void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_base_script, bool p_virtuals_only, const bool p_connecting, bool clear_text) {
set_title(TTR("Select from base type"));
base_type = p_base;
- base_script = p_base_script.lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_base_script.trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
type = Variant::NIL;
connecting = p_connecting;
@@ -221,7 +217,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip
ERR_FAIL_COND(p_script.is_null());
base_type = p_script->get_instance_base_type();
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
type = Variant::NIL;
script = p_script->get_instance_id();
connecting = p_connecting;
@@ -316,7 +312,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons
if (p_script == nullptr) {
base_script = "";
} else {
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
}
type = Variant::NIL;
@@ -350,7 +346,7 @@ void VisualScriptPropertySelector::select_from_visual_script(const Ref<Script> &
if (p_script == nullptr) {
base_script = "";
} else {
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
}
type = Variant::NIL;
connecting = false;
@@ -525,10 +521,19 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() {
results_tree->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
vbox->add_child(results_tree);
+ ScrollContainer *scroller = memnew(ScrollContainer);
+ scroller->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ scroller->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ scroller->set_custom_minimum_size(Size2(600, 400) * EDSCALE);
+ vbox->add_child(scroller);
+
help_bit = memnew(EditorHelpBit);
- vbox->add_child(help_bit);
+ help_bit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ help_bit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ scroller->add_child(help_bit);
+
help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
- get_ok_button()->set_text(TTR("Open"));
+ set_ok_button_text(TTR("Open"));
get_ok_button()->set_disabled(true);
set_hide_on_ok(false);
}
@@ -730,7 +735,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes_init() {
combined_docs.insert(class_doc.name, class_doc);
}
}
- iterator_doc = combined_docs.front();
+ iterator_doc = combined_docs.begin();
return true;
}
@@ -791,7 +796,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_build() {
}
bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
- DocData::ClassDoc &class_doc = iterator_doc->value();
+ DocData::ClassDoc &class_doc = iterator_doc->value;
if (
(!_is_class_disabled_by_feature_profile(class_doc.name) && !_is_class_disabled_by_scope(class_doc.name)) ||
_match_visual_script(class_doc)) {
@@ -913,13 +918,13 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
}
}
- iterator_doc = iterator_doc->next();
+ ++iterator_doc;
return !iterator_doc;
}
bool VisualScriptPropertySelector::SearchRunner::_phase_class_items_init() {
results_tree->clear();
- iterator_match = matches.front();
+ iterator_match = matches.begin();
root_item = results_tree->create_item();
class_items.clear();
@@ -932,7 +937,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
return true;
}
- ClassMatch &match = iterator_match->value();
+ ClassMatch &match = iterator_match->value;
if (search_flags & SEARCH_SHOW_HIERARCHY) {
if (match.required()) {
@@ -944,12 +949,12 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
}
}
- iterator_match = iterator_match->next();
+ ++iterator_match;
return !iterator_match;
}
bool VisualScriptPropertySelector::SearchRunner::_phase_member_items_init() {
- iterator_match = matches.front();
+ iterator_match = matches.begin();
return true;
}
@@ -959,7 +964,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
return true;
}
- ClassMatch &match = iterator_match->value();
+ ClassMatch &match = iterator_match->value;
TreeItem *parent = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
bool constructor_created = false;
@@ -990,7 +995,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
_create_theme_property_item(parent, match.doc, match.theme_properties[i]);
}
- iterator_match = iterator_match->next();
+ ++iterator_match;
return !iterator_match;
}
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 1b32ee2967..41f8eea735 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H
-#define VISUALSCRIPT_PROPERTYSELECTOR_H
+#ifndef VISUAL_SCRIPT_PROPERTY_SELECTOR_H
+#define VISUAL_SCRIPT_PROPERTY_SELECTOR_H
#include "../visual_script.h"
#include "editor/editor_help.h"
@@ -173,21 +173,21 @@ class VisualScriptPropertySelector::SearchRunner : public RefCounted {
Control *ui_service = nullptr;
Tree *results_tree = nullptr;
String term;
- int search_flags;
- int scope_flags;
+ int search_flags = 0;
+ int scope_flags = 0;
Ref<Texture2D> empty_icon;
Color disabled_color;
- Map<String, DocData::ClassDoc>::Element *iterator_doc = nullptr;
- Map<String, ClassMatch> matches;
- Map<String, ClassMatch>::Element *iterator_match = nullptr;
+ HashMap<String, DocData::ClassDoc>::Iterator iterator_doc;
+ HashMap<String, ClassMatch> matches;
+ HashMap<String, ClassMatch>::Iterator iterator_match;
TreeItem *root_item = nullptr;
- Map<String, TreeItem *> class_items;
+ HashMap<String, TreeItem *> class_items;
TreeItem *matched_item = nullptr;
float match_highest_score = 0;
- Map<String, DocData::ClassDoc> combined_docs;
+ HashMap<String, DocData::ClassDoc> combined_docs;
List<String> vs_nodes;
bool _is_class_disabled_by_feature_profile(const StringName &p_class);
@@ -226,4 +226,4 @@ public:
SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree);
};
-#endif // VISUALSCRIPT_PROPERTYSELECTOR_H
+#endif // VISUAL_SCRIPT_PROPERTY_SELECTOR_H
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index d19870921d..04a7442d0a 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -47,95 +47,104 @@ VisualScriptLanguage *visual_script_language = nullptr;
static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr;
#endif
-void register_visual_script_types() {
- visual_script_language = memnew(VisualScriptLanguage);
- //script_language_gd->init();
- ScriptServer::register_language(visual_script_language);
-
- GDREGISTER_CLASS(VisualScript);
- GDREGISTER_ABSTRACT_CLASS(VisualScriptNode);
- GDREGISTER_CLASS(VisualScriptFunctionState);
- GDREGISTER_CLASS(VisualScriptFunction);
- GDREGISTER_ABSTRACT_CLASS(VisualScriptLists);
- GDREGISTER_CLASS(VisualScriptComposeArray);
- GDREGISTER_CLASS(VisualScriptOperator);
- GDREGISTER_CLASS(VisualScriptVariableSet);
- GDREGISTER_CLASS(VisualScriptVariableGet);
- GDREGISTER_CLASS(VisualScriptConstant);
- GDREGISTER_CLASS(VisualScriptIndexGet);
- GDREGISTER_CLASS(VisualScriptIndexSet);
- GDREGISTER_CLASS(VisualScriptGlobalConstant);
- GDREGISTER_CLASS(VisualScriptClassConstant);
- GDREGISTER_CLASS(VisualScriptMathConstant);
- GDREGISTER_CLASS(VisualScriptBasicTypeConstant);
- GDREGISTER_CLASS(VisualScriptEngineSingleton);
- GDREGISTER_CLASS(VisualScriptSceneNode);
- GDREGISTER_CLASS(VisualScriptSceneTree);
- GDREGISTER_CLASS(VisualScriptResourcePath);
- GDREGISTER_CLASS(VisualScriptSelf);
- GDREGISTER_CLASS(VisualScriptCustomNode);
- GDREGISTER_CLASS(VisualScriptSubCall);
- GDREGISTER_CLASS(VisualScriptComment);
- GDREGISTER_CLASS(VisualScriptConstructor);
- GDREGISTER_CLASS(VisualScriptLocalVar);
- GDREGISTER_CLASS(VisualScriptLocalVarSet);
- GDREGISTER_CLASS(VisualScriptInputAction);
- GDREGISTER_CLASS(VisualScriptDeconstruct);
- GDREGISTER_CLASS(VisualScriptPreload);
- GDREGISTER_CLASS(VisualScriptTypeCast);
-
- GDREGISTER_CLASS(VisualScriptFunctionCall);
- GDREGISTER_CLASS(VisualScriptPropertySet);
- GDREGISTER_CLASS(VisualScriptPropertyGet);
- //ClassDB::register_type<VisualScriptScriptCall>();
- GDREGISTER_CLASS(VisualScriptEmitSignal);
-
- GDREGISTER_CLASS(VisualScriptReturn);
- GDREGISTER_CLASS(VisualScriptCondition);
- GDREGISTER_CLASS(VisualScriptWhile);
- GDREGISTER_CLASS(VisualScriptIterator);
- GDREGISTER_CLASS(VisualScriptSequence);
- //GDREGISTER_CLASS(VisualScriptInputFilter);
- GDREGISTER_CLASS(VisualScriptSwitch);
- GDREGISTER_CLASS(VisualScriptSelect);
-
- GDREGISTER_CLASS(VisualScriptYield);
- GDREGISTER_CLASS(VisualScriptYieldSignal);
-
- GDREGISTER_CLASS(VisualScriptBuiltinFunc);
-
- GDREGISTER_CLASS(VisualScriptExpression);
-
- register_visual_script_nodes();
- register_visual_script_func_nodes();
- register_visual_script_builtin_func_node();
- register_visual_script_flow_control_nodes();
- register_visual_script_yield_nodes();
- register_visual_script_expression_node();
+void initialize_visual_script_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ visual_script_language = memnew(VisualScriptLanguage);
+ //script_language_gd->init();
+ ScriptServer::register_language(visual_script_language);
+
+ GDREGISTER_CLASS(VisualScript);
+ GDREGISTER_ABSTRACT_CLASS(VisualScriptNode);
+ GDREGISTER_CLASS(VisualScriptFunctionState);
+ GDREGISTER_CLASS(VisualScriptFunction);
+ GDREGISTER_ABSTRACT_CLASS(VisualScriptLists);
+ GDREGISTER_CLASS(VisualScriptComposeArray);
+ GDREGISTER_CLASS(VisualScriptOperator);
+ GDREGISTER_CLASS(VisualScriptVariableSet);
+ GDREGISTER_CLASS(VisualScriptVariableGet);
+ GDREGISTER_CLASS(VisualScriptConstant);
+ GDREGISTER_CLASS(VisualScriptIndexGet);
+ GDREGISTER_CLASS(VisualScriptIndexSet);
+ GDREGISTER_CLASS(VisualScriptGlobalConstant);
+ GDREGISTER_CLASS(VisualScriptClassConstant);
+ GDREGISTER_CLASS(VisualScriptMathConstant);
+ GDREGISTER_CLASS(VisualScriptBasicTypeConstant);
+ GDREGISTER_CLASS(VisualScriptEngineSingleton);
+ GDREGISTER_CLASS(VisualScriptSceneNode);
+ GDREGISTER_CLASS(VisualScriptSceneTree);
+ GDREGISTER_CLASS(VisualScriptResourcePath);
+ GDREGISTER_CLASS(VisualScriptSelf);
+ GDREGISTER_CLASS(VisualScriptCustomNode);
+ GDREGISTER_CLASS(VisualScriptSubCall);
+ GDREGISTER_CLASS(VisualScriptComment);
+ GDREGISTER_CLASS(VisualScriptConstructor);
+ GDREGISTER_CLASS(VisualScriptLocalVar);
+ GDREGISTER_CLASS(VisualScriptLocalVarSet);
+ GDREGISTER_CLASS(VisualScriptInputAction);
+ GDREGISTER_CLASS(VisualScriptDeconstruct);
+ GDREGISTER_CLASS(VisualScriptPreload);
+ GDREGISTER_CLASS(VisualScriptTypeCast);
+
+ GDREGISTER_CLASS(VisualScriptFunctionCall);
+ GDREGISTER_CLASS(VisualScriptPropertySet);
+ GDREGISTER_CLASS(VisualScriptPropertyGet);
+ //ClassDB::register_type<VisualScriptScriptCall>();
+ GDREGISTER_CLASS(VisualScriptEmitSignal);
+
+ GDREGISTER_CLASS(VisualScriptReturn);
+ GDREGISTER_CLASS(VisualScriptCondition);
+ GDREGISTER_CLASS(VisualScriptWhile);
+ GDREGISTER_CLASS(VisualScriptIterator);
+ GDREGISTER_CLASS(VisualScriptSequence);
+ //GDREGISTER_CLASS(VisualScriptInputFilter);
+ GDREGISTER_CLASS(VisualScriptSwitch);
+ GDREGISTER_CLASS(VisualScriptSelect);
+
+ GDREGISTER_CLASS(VisualScriptYield);
+ GDREGISTER_CLASS(VisualScriptYieldSignal);
+
+ GDREGISTER_CLASS(VisualScriptBuiltinFunc);
+
+ GDREGISTER_CLASS(VisualScriptExpression);
+
+ register_visual_script_nodes();
+ register_visual_script_func_nodes();
+ register_visual_script_builtin_func_node();
+ register_visual_script_flow_control_nodes();
+ register_visual_script_yield_nodes();
+ register_visual_script_expression_node();
+ }
#ifdef TOOLS_ENABLED
- ClassDB::set_current_api(ClassDB::API_EDITOR);
- GDREGISTER_CLASS(VisualScriptCustomNodes);
- ClassDB::set_current_api(ClassDB::API_CORE);
- vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes);
- Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptCustomNodes", VisualScriptCustomNodes::get_singleton()));
-
- VisualScriptEditor::register_editor();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
+ GDREGISTER_CLASS(VisualScriptCustomNodes);
+ ClassDB::set_current_api(ClassDB::API_CORE);
+ vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptCustomNodes", VisualScriptCustomNodes::get_singleton()));
+
+ VisualScriptEditor::register_editor();
+ }
#endif
}
-void unregister_visual_script_types() {
- unregister_visual_script_nodes();
+void uninitialize_visual_script_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
+ unregister_visual_script_nodes();
- ScriptServer::unregister_language(visual_script_language);
+ ScriptServer::unregister_language(visual_script_language);
+
+ if (visual_script_language) {
+ memdelete(visual_script_language);
+ }
+ }
#ifdef TOOLS_ENABLED
- VisualScriptEditor::free_clipboard();
- if (vs_custom_nodes_singleton) {
- memdelete(vs_custom_nodes_singleton);
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ VisualScriptEditor::free_clipboard();
+ if (vs_custom_nodes_singleton) {
+ memdelete(vs_custom_nodes_singleton);
+ }
}
#endif
- if (visual_script_language) {
- memdelete(visual_script_language);
- }
}
diff --git a/modules/visual_script/register_types.h b/modules/visual_script/register_types.h
index 29768da67f..90f84de11c 100644
--- a/modules/visual_script/register_types.h
+++ b/modules/visual_script/register_types.h
@@ -31,7 +31,9 @@
#ifndef VISUAL_SCRIPT_REGISTER_TYPES_H
#define VISUAL_SCRIPT_REGISTER_TYPES_H
-void register_visual_script_types();
-void unregister_visual_script_types();
+#include "modules/register_module_types.h"
+
+void initialize_visual_script_module(ModuleInitializationLevel p_level);
+void uninitialize_visual_script_module(ModuleInitializationLevel p_level);
#endif // VISUAL_SCRIPT_REGISTER_TYPES_H
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index e31550b203..4215a979e0 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -209,8 +209,9 @@ Vector2 VisualScript::get_scroll() const {
}
void VisualScript::get_function_list(List<StringName> *r_functions) const {
- functions.get_key_list(r_functions);
- // r_functions->sort_custom<StringName::AlphCompare>(); // Don't force sorting.
+ for (const KeyValue<StringName, Function> &E : functions) {
+ r_functions->push_back(E.key);
+ }
}
int VisualScript::get_function_node_id(const StringName &p_name) const {
@@ -229,12 +230,12 @@ void VisualScript::_node_ports_changed(int p_id) {
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id && E->get().from_output >= vsn->get_output_sequence_port_count()) {
- to_remove.push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ if (E.from_node == p_id && E.from_output >= vsn->get_output_sequence_port_count()) {
+ to_remove.push_back(E);
}
- if (E->get().to_node == p_id && !vsn->has_input_sequence_port()) {
- to_remove.push_back(E->get());
+ if (E.to_node == p_id && !vsn->has_input_sequence_port()) {
+ to_remove.push_back(E);
}
}
@@ -247,12 +248,12 @@ void VisualScript::_node_ports_changed(int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id && E->get().from_port >= vsn->get_output_value_port_count()) {
- to_remove.push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ if (E.from_node == p_id && E.from_port >= vsn->get_output_value_port_count()) {
+ to_remove.push_back(E);
}
- if (E->get().to_node == p_id && E->get().to_port >= vsn->get_input_value_port_count()) {
- to_remove.push_back(E->get());
+ if (E.to_node == p_id && E.to_port >= vsn->get_input_value_port_count()) {
+ to_remove.push_back(E);
}
}
@@ -278,7 +279,7 @@ void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const
nd.pos = p_pos;
Ref<VisualScriptNode> vsn = p_node;
- vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed), varray(p_id));
+ vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed).bind(p_id));
vsn->script_used = Ref<VisualScript>(this);
vsn->validate_input_default_values(); // Validate when fully loaded.
@@ -291,9 +292,9 @@ void VisualScript::remove_node(int p_id) {
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- to_remove.push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ to_remove.push_back(E);
}
}
@@ -306,9 +307,9 @@ void VisualScript::remove_node(int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- to_remove.push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ to_remove.push_back(E);
}
}
@@ -346,7 +347,9 @@ Point2 VisualScript::get_node_position(int p_id) const {
}
void VisualScript::get_node_list(List<int> *r_nodes) const {
- nodes.get_key_list(r_nodes);
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ r_nodes->push_back(E.key);
+ }
}
void VisualScript::sequence_connect(int p_from_node, int p_from_output, int p_to_node) {
@@ -381,8 +384,8 @@ bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, i
}
void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const {
- for (const Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- r_connection->push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ r_connection->push_back(E);
}
}
@@ -423,8 +426,8 @@ bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_t
}
bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().to_node == p_node && E->get().to_port == p_port) {
+ for (const DataConnection &E : data_connections) {
+ if (E.to_node == p_node && E.to_port == p_port) {
return true;
}
}
@@ -432,10 +435,10 @@ bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
}
bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().to_node == p_node && E->get().to_port == p_port) {
- *r_node = E->get().from_node;
- *r_port = E->get().from_port;
+ for (const DataConnection &E : data_connections) {
+ if (E.to_node == p_node && E.to_port == p_port) {
+ *r_node = E.from_node;
+ *r_port = E.from_port;
return true;
}
}
@@ -443,8 +446,8 @@ bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port
}
void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- r_connection->push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ r_connection->push_back(E);
}
}
@@ -563,8 +566,9 @@ Dictionary VisualScript::_get_variable_info(const StringName &p_name) const {
}
void VisualScript::get_variable_list(List<StringName> *r_variables) const {
- variables.get_key_list(r_variables);
- // r_variables->sort_custom<StringName::AlphCompare>(); // Don't force it.
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ r_variables->push_back(E.key);
+ }
}
void VisualScript::set_instance_base_type(const StringName &p_type) {
@@ -713,12 +717,11 @@ int VisualScript::get_available_id() const {
// This is infinitely increasing,
// so one might want to implement a better solution,
// if the there is a case for huge number of nodes to be added to visual script.
- List<int> nds;
- nodes.get_key_list(&nds);
+
int max = -1;
- for (const int &E : nds) {
- if (E > max) {
- max = E;
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ if (E.key > max) {
+ max = E.key;
}
}
return (max + 1);
@@ -748,24 +751,21 @@ void VisualScript::_update_placeholders() {
return; // No bother if no placeholders.
}
List<PropertyInfo> pinfo;
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
- List<StringName> keys;
- variables.get_key_list(&keys);
-
- for (const StringName &E : keys) {
- if (!variables[E]._export) {
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ if (!variables[E.key]._export) {
continue;
}
- PropertyInfo p = variables[E].info;
- p.name = String(E);
+ PropertyInfo p = variables[E.key].info;
+ p.name = String(E.key);
pinfo.push_back(p);
- values[p.name] = variables[E].default_value;
+ values[p.name] = variables[E.key].default_value;
}
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(pinfo, values);
+ for (PlaceHolderScriptInstance *E : placeholders) {
+ E->update(pinfo, values);
}
}
@@ -779,20 +779,17 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) {
placeholders.insert(sins);
List<PropertyInfo> pinfo;
- Map<StringName, Variant> values;
-
- List<StringName> keys;
- variables.get_key_list(&keys);
+ HashMap<StringName, Variant> values;
- for (const StringName &E : keys) {
- if (!variables[E]._export) {
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ if (!variables[E.key]._export) {
continue;
}
- PropertyInfo p = variables[E].info;
- p.name = String(E);
+ PropertyInfo p = variables[E.key].info;
+ p.name = String(E.key);
pinfo.push_back(p);
- values[p.name] = variables[E].default_value;
+ values[p.name] = variables[E.key].default_value;
}
sins->update(pinfo, values);
@@ -872,14 +869,11 @@ bool VisualScript::get_property_default_value(const StringName &p_property, Vari
}
void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
- List<StringName> funcs;
- functions.get_key_list(&funcs);
-
- for (const StringName &E : funcs) {
+ for (const KeyValue<StringName, Function> &E : functions) {
MethodInfo mi;
- mi.name = E;
- if (functions[E].func_id >= 0) {
- Ref<VisualScriptFunction> func = nodes[functions[E].func_id].node;
+ mi.name = E.key;
+ if (functions[E.key].func_id >= 0) {
+ Ref<VisualScriptFunction> func = nodes[functions[E.key].func_id].node;
if (func.is_valid()) {
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arg;
@@ -930,6 +924,9 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
get_variable_list(&vars);
for (const StringName &E : vars) {
+ if (!variables[E]._export) {
+ continue;
+ }
PropertyInfo pi = variables[E].info;
pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
p_list->push_back(pi);
@@ -942,10 +939,8 @@ int VisualScript::get_member_line(const StringName &p_member) const {
#ifdef TOOLS_ENABLED
bool VisualScript::are_subnodes_edited() const {
- List<int> keys;
- nodes.get_key_list(&keys);
- for (const int &F : keys) {
- if (nodes[F].node->is_edited()) {
+ for (const KeyValue<int, NodeData> &F : nodes) {
+ if (F.value.node->is_edited()) {
return true;
}
}
@@ -953,7 +948,7 @@ bool VisualScript::are_subnodes_edited() const {
}
#endif
-const Vector<Multiplayer::RPCConfig> VisualScript::get_rpc_methods() const {
+const Variant VisualScript::get_rpc_config() const {
return rpc_functions;
}
@@ -1014,27 +1009,19 @@ void VisualScript::_set_data(const Dictionary &p_data) {
// Takes all the rpc methods.
rpc_functions.clear();
- List<StringName> fns;
- functions.get_key_list(&fns);
- for (const StringName &E : fns) {
- if (functions[E].func_id >= 0 && nodes.has(functions[E].func_id)) {
- Ref<VisualScriptFunction> vsf = nodes[functions[E].func_id].node;
- if (vsf.is_valid()) {
- if (vsf->get_rpc_mode() != Multiplayer::RPC_MODE_DISABLED) {
- Multiplayer::RPCConfig nd;
- nd.name = E;
- nd.rpc_mode = vsf->get_rpc_mode();
- nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO
- if (rpc_functions.find(nd) == -1) {
- rpc_functions.push_back(nd);
- }
- }
+ for (const KeyValue<StringName, Function> &E : functions) {
+ if (E.value.func_id >= 0 && nodes.has(E.value.func_id)) {
+ Ref<VisualScriptFunction> vsf = nodes[E.value.func_id].node;
+ if (!vsf.is_valid() || vsf->get_rpc_mode() == MultiplayerAPI::RPC_MODE_DISABLED) {
+ continue;
}
+ Dictionary nd;
+ nd["rpc_mode"] = vsf->get_rpc_mode();
+ nd["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_RELIABLE; // TODO
+ nd["call_local"] = false; // TODO
+ rpc_functions[E.key] = nd;
}
}
-
- // Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
Dictionary VisualScript::_get_data() const {
@@ -1042,13 +1029,11 @@ Dictionary VisualScript::_get_data() const {
d["base_type"] = base_type;
Array vars;
- List<StringName> var_names;
- variables.get_key_list(&var_names);
- for (const StringName &E : var_names) {
- Dictionary var = _get_variable_info(E);
- var["name"] = E; // Make sure it's the right one.
- var["default_value"] = variables[E].default_value;
- var["export"] = variables[E]._export;
+ for (const KeyValue<StringName, Variable> &E : variables) {
+ Dictionary var = _get_variable_info(E.key);
+ var["name"] = E.key; // Make sure it's the right one.
+ var["default_value"] = E.value.default_value;
+ var["export"] = E.value._export;
vars.push_back(var);
}
d["variables"] = vars;
@@ -1070,40 +1055,36 @@ Dictionary VisualScript::_get_data() const {
d["signals"] = sigs;
Array funcs;
- List<StringName> func_names;
- functions.get_key_list(&func_names);
- for (const StringName &E : func_names) {
+ for (const KeyValue<StringName, Function> &E : functions) {
Dictionary func;
- func["name"] = E;
- func["function_id"] = functions[E].func_id;
+ func["name"] = E.key;
+ func["function_id"] = E.value.func_id;
funcs.push_back(func);
}
d["functions"] = funcs;
Array nds;
- List<int> node_ids;
- nodes.get_key_list(&node_ids);
- for (const int &F : node_ids) {
- nds.push_back(F);
- nds.push_back(nodes[F].pos);
- nds.push_back(nodes[F].node);
+ for (const KeyValue<int, NodeData> &F : nodes) {
+ nds.push_back(F.key);
+ nds.push_back(F.value.pos);
+ nds.push_back(F.value.node);
}
d["nodes"] = nds;
Array seqconns;
- for (const Set<SequenceConnection>::Element *F = sequence_connections.front(); F; F = F->next()) {
- seqconns.push_back(F->get().from_node);
- seqconns.push_back(F->get().from_output);
- seqconns.push_back(F->get().to_node);
+ for (const SequenceConnection &F : sequence_connections) {
+ seqconns.push_back(F.from_node);
+ seqconns.push_back(F.from_output);
+ seqconns.push_back(F.to_node);
}
d["sequence_connections"] = seqconns;
Array dataconns;
- for (const Set<DataConnection>::Element *F = data_connections.front(); F; F = F->next()) {
- dataconns.push_back(F->get().from_node);
- dataconns.push_back(F->get().from_port);
- dataconns.push_back(F->get().to_node);
- dataconns.push_back(F->get().to_port);
+ for (const DataConnection &F : data_connections) {
+ dataconns.push_back(F.from_node);
+ dataconns.push_back(F.from_port);
+ dataconns.push_back(F.to_node);
+ dataconns.push_back(F.to_port);
}
d["data_connections"] = dataconns;
@@ -1181,10 +1162,10 @@ bool VisualScript::inherits_script(const Ref<Script> &p_script) const {
return this == p_script.ptr(); // There is no inheritance in visual scripts, so this is enough.
}
-Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
+RBSet<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>);
get_sequence_connection_list(sc);
- Set<int> connected;
+ RBSet<int> connected;
for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) {
if (E->get().from_node == from_node) {
connected.insert(E->get().from_output);
@@ -1196,45 +1177,41 @@ Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
VisualScript::~VisualScript() {
// Remove all nodes and stuff that hold data refs.
- List<int> nds;
- nodes.get_key_list(&nds);
- for (const int &E : nds) {
- remove_node(E);
+ for (const KeyValue<int, NodeData> &E : nodes) {
+ remove_node(E.key);
}
}
////////////////////////////////////////////
bool VisualScriptInstance::set(const StringName &p_name, const Variant &p_value) {
- Map<StringName, Variant>::Element *E = variables.find(p_name);
+ HashMap<StringName, Variant>::Iterator E = variables.find(p_name);
if (!E) {
return false;
}
- E->get() = p_value;
+ E->value = p_value;
return true;
}
bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
- const Map<StringName, Variant>::Element *E = variables.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = variables.find(p_name);
if (!E) {
return false;
}
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- List<StringName> vars;
- script->variables.get_key_list(&vars);
- for (const StringName &E : vars) {
- if (!script->variables[E]._export) {
+ for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
+ if (!E.value._export) {
continue;
}
- PropertyInfo p = script->variables[E].info;
- p.name = String(E);
+ PropertyInfo p = E.value.info;
+ p.name = String(E.key);
p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
p_properties->push_back(p);
}
@@ -1256,13 +1233,11 @@ Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name,
}
void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
- List<StringName> fns;
- script->functions.get_key_list(&fns);
- for (const StringName &E : fns) {
+ for (const KeyValue<StringName, VisualScript::Function> &E : script->functions) {
MethodInfo mi;
- mi.name = E;
- if (script->functions[E].func_id >= 0 && script->nodes.has(script->functions[E].func_id)) {
- Ref<VisualScriptFunction> vsf = script->nodes[script->functions[E].func_id].node;
+ mi.name = E.key;
+ if (E.value.func_id >= 0 && script->nodes.has(E.value.func_id)) {
+ Ref<VisualScriptFunction> vsf = script->nodes[E.value.func_id].node;
if (vsf.is_valid()) {
for (int i = 0; i < vsf->get_argument_count(); i++) {
PropertyInfo arg;
@@ -1334,9 +1309,9 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int
}
Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p_stack, int p_stack_size, VisualScriptNodeInstance *p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield, Callable::CallError &r_error) {
- Map<StringName, Function>::Element *F = functions.find(p_method);
+ HashMap<StringName, Function>::Iterator F = functions.find(p_method);
ERR_FAIL_COND_V(!F, Variant());
- Function *f = &F->get();
+ Function *f = &F->value;
// This call goes separate, so it can be yielded and suspended.
Variant *variant_stack = (Variant *)p_stack;
@@ -1547,7 +1522,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// If no exit bit was set, and has sequence outputs, guess next node.
if (output >= node->sequence_output_count) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Node returned an invalid sequence output: ") + itos(output);
+ error_str = RTR("Node returned an invalid sequence output:") + " " + itos(output);
error = true;
break;
}
@@ -1601,7 +1576,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
if (!found) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Found sequence bit but not the node in the stack, report bug!");
+ error_str = RTR("Found sequence bit but not the node in the stack (please report).");
error = true;
break;
}
@@ -1613,7 +1588,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// Check for stack overflow.
if (flow_stack_pos + 1 >= flow_max) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Stack overflow with stack depth: ") + itos(output);
+ error_str = vformat(RTR("Stack overflow (stack size: %s). Check for infinite recursion in your script."), output);
error = true;
break;
}
@@ -1674,6 +1649,8 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
error_str += "Expected " + itos(r_error.argument) + " arguments.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
error_str += "Invalid Call.";
+ } else if (r_error.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ error_str += "Method not const in a const instance.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
error_str += "Base Instance is null";
}
@@ -1708,7 +1685,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK; //ok by default
- Map<StringName, Function>::Element *F = functions.find(p_method);
+ HashMap<StringName, Function>::Iterator F = functions.find(p_method);
if (!F) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
@@ -1716,7 +1693,7 @@ Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **
VSDEBUG("CALLING: " + String(p_method));
- Function *f = &F->get();
+ Function *f = &F->value;
int total_stack_size = 0;
@@ -1750,14 +1727,14 @@ Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **
memset(pass_stack, 0, f->pass_stack_size * sizeof(int));
- Map<int, VisualScriptNodeInstance *>::Element *E = instances.find(f->node);
+ HashMap<int, VisualScriptNodeInstance *>::Iterator E = instances.find(f->node);
if (!E) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "No VisualScriptFunction node in function.");
}
- VisualScriptNodeInstance *node = E->get();
+ VisualScriptNodeInstance *node = E->value;
if (flow_stack) {
flow_stack[0] = node->get_id();
@@ -1828,8 +1805,8 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
-const Vector<Multiplayer::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
- return script->get_rpc_methods();
+const Variant VisualScriptInstance::get_rpc_config() const {
+ return script->get_rpc_config();
}
void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) {
@@ -1842,19 +1819,15 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
// Setup variables.
{
- List<StringName> keys;
- script->variables.get_key_list(&keys);
- for (const StringName &E : keys) {
- variables[E] = script->variables[E].default_value;
+ for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
+ variables[E.key] = E.value.default_value;
}
}
// Setup functions from sequence trees.
{
- List<StringName> keys;
- script->functions.get_key_list(&keys);
- for (const StringName &E : keys) {
- const VisualScript::Function vsfn = p_script->functions[E];
+ for (const KeyValue<StringName, VisualScript::Function> &E : script->functions) {
+ const VisualScript::Function &vsfn = E.value;
Function function;
function.node = vsfn.func_id;
function.max_stack = 0;
@@ -1862,10 +1835,10 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
function.pass_stack_size = 0;
function.node_count = 0;
- Map<StringName, int> local_var_indices;
+ HashMap<StringName, int> local_var_indices;
if (function.node < 0) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E));
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E.key));
ERR_CONTINUE(function.node < 0);
}
@@ -1873,7 +1846,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
Ref<VisualScriptFunction> func_node = script->get_node(vsfn.func_id);
if (func_node.is_null()) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E));
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E.key));
}
ERR_CONTINUE(!func_node.is_valid());
@@ -1884,42 +1857,41 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
max_input_args = MAX(max_input_args, function.argument_count);
}
// Function nodes graphs.
- Set<VisualScript::SequenceConnection> seqconns;
- Set<VisualScript::DataConnection> dataconns;
- Set<int> node_ids;
+ RBSet<VisualScript::SequenceConnection> seqconns;
+ RBSet<VisualScript::DataConnection> dataconns;
+ RBSet<int> node_ids;
node_ids.insert(function.node);
{
List<int> nd_queue;
nd_queue.push_back(function.node);
while (!nd_queue.is_empty()) {
- for (const Set<VisualScript::SequenceConnection>::Element *F = script->sequence_connections.front(); F; F = F->next()) {
- if (nd_queue.front()->get() == F->get().from_node && !node_ids.has(F->get().to_node)) {
- nd_queue.push_back(F->get().to_node);
- node_ids.insert(F->get().to_node);
+ for (const VisualScript::SequenceConnection &F : script->sequence_connections) {
+ if (nd_queue.front()->get() == F.from_node && !node_ids.has(F.to_node)) {
+ nd_queue.push_back(F.to_node);
+ node_ids.insert(F.to_node);
}
- if (nd_queue.front()->get() == F->get().from_node && !seqconns.has(F->get())) {
- seqconns.insert(F->get());
+ if (nd_queue.front()->get() == F.from_node && !seqconns.has(F)) {
+ seqconns.insert(F);
}
}
nd_queue.pop_front();
}
HashMap<int, HashMap<int, Pair<int, int>>> dc_lut; // :: to -> to_port -> (from, from_port)
- for (const Set<VisualScript::DataConnection>::Element *F = script->data_connections.front(); F; F = F->next()) {
- dc_lut[F->get().to_node][F->get().to_port] = Pair<int, int>(F->get().from_node, F->get().from_port);
+ for (const VisualScript::DataConnection &F : script->data_connections) {
+ dc_lut[F.to_node][F.to_port] = Pair<int, int>(F.from_node, F.from_port);
}
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- nd_queue.push_back(F->get());
+ for (const int &F : node_ids) {
+ nd_queue.push_back(F);
}
List<int> dc_keys;
while (!nd_queue.is_empty()) {
int ky = nd_queue.front()->get();
- dc_lut[ky].get_key_list(&dc_keys);
- for (const int &F : dc_keys) {
+ for (const KeyValue<int, Pair<int, int>> &F : dc_lut[ky]) {
VisualScript::DataConnection dc;
- dc.from_node = dc_lut[ky][F].first;
- dc.from_port = dc_lut[ky][F].second;
+ dc.from_node = F.value.first;
+ dc.from_port = F.value.second;
dc.to_node = ky;
- dc.to_port = F;
+ dc.to_port = F.key;
dataconns.insert(dc);
nd_queue.push_back(dc.from_node);
node_ids.insert(dc.from_node);
@@ -1931,15 +1903,15 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
//Multiple passes are required to set up this complex thing..
//First create the nodes.
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- Ref<VisualScriptNode> node = script->nodes[F->get()].node;
+ for (const int &F : node_ids) {
+ Ref<VisualScriptNode> node = script->nodes[F].node;
VisualScriptNodeInstance *instance = node->instantiate(this); // Create instance.
ERR_FAIL_COND(!instance);
instance->base = node.ptr();
- instance->id = F->get();
+ instance->id = F;
instance->input_port_count = node->get_input_value_port_count();
instance->input_ports = nullptr;
instance->output_port_count = node->get_output_value_port_count();
@@ -1999,14 +1971,14 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
max_input_args = MAX(max_input_args, instance->input_port_count);
max_output_args = MAX(max_output_args, instance->output_port_count);
- instances[F->get()] = instance;
+ instances[F] = instance;
}
function.trash_pos = function.max_stack++; // create pos for trash
// Second pass, do data connections.
- for (const Set<VisualScript::DataConnection>::Element *F = dataconns.front(); F; F = F->next()) {
- VisualScript::DataConnection dc = F->get();
+ for (const VisualScript::DataConnection &F : dataconns) {
+ VisualScript::DataConnection dc = F;
ERR_CONTINUE(!instances.has(dc.from_node));
VisualScriptNodeInstance *from = instances[dc.from_node];
ERR_CONTINUE(!instances.has(dc.to_node));
@@ -2032,8 +2004,8 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
}
// Third pass, do sequence connections.
- for (const Set<VisualScript::SequenceConnection>::Element *F = seqconns.front(); F; F = F->next()) {
- VisualScript::SequenceConnection sc = F->get();
+ for (const VisualScript::SequenceConnection &F : seqconns) {
+ VisualScript::SequenceConnection sc = F;
ERR_CONTINUE(!instances.has(sc.from_node));
VisualScriptNodeInstance *from = instances[sc.from_node];
ERR_CONTINUE(!instances.has(sc.to_node));
@@ -2046,11 +2018,11 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
//fourth pass:
// 1) unassigned input ports to default values
// 2) connect unassigned output ports to trash
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- ERR_CONTINUE(!instances.has(F->get()));
+ for (const int &F : node_ids) {
+ ERR_CONTINUE(!instances.has(F));
- Ref<VisualScriptNode> node = script->nodes[F->get()].node;
- VisualScriptNodeInstance *instance = instances[F->get()];
+ Ref<VisualScriptNode> node = script->nodes[F].node;
+ VisualScriptNodeInstance *instance = instances[F];
// Connect to default values.
for (int i = 0; i < instance->input_port_count; i++) {
@@ -2069,7 +2041,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
}
}
- functions[E] = function;
+ functions[E.key] = function;
}
}
}
@@ -2150,7 +2122,14 @@ void VisualScriptFunctionState::connect_to_signal(Object *p_obj, const String &p
binds.push_back(p_binds[i]);
}
binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
- p_obj->connect(p_signal, Callable(this, "_signal_callback"), binds, CONNECT_ONESHOT);
+
+ Vector<const Variant *> bind_ptrs;
+ bind_ptrs.resize(p_binds.size());
+ for (int i = 0; i < bind_ptrs.size(); i++) {
+ bind_ptrs.write[i] = &binds.write[i];
+ }
+
+ p_obj->connect(p_signal, Callable(this, "_signal_callback").bindp((const Variant **)bind_ptrs.ptr(), bind_ptrs.size()), CONNECT_ONESHOT);
}
bool VisualScriptFunctionState::is_valid() const {
@@ -2247,7 +2226,7 @@ Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const
return script;
}
-bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const {
return false;
}
@@ -2457,6 +2436,9 @@ void VisualScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) c
void VisualScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const {
}
+void VisualScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
+}
+
void VisualScriptLanguage::profiling_start() {
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 6b27af15f6..14cb14e8d9 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -36,6 +36,7 @@
#include "core/doc_data.h"
#include "core/object/script_language.h"
#include "core/os/thread.h"
+#include "core/templates/rb_set.h"
class VisualScriptInstance;
class VisualScriptNodeInstance;
@@ -214,8 +215,8 @@ private:
HashMap<int, NodeData> nodes; // Can be a sparse map.
- Set<SequenceConnection> sequence_connections;
- Set<DataConnection> data_connections;
+ RBSet<SequenceConnection> sequence_connections;
+ RBSet<DataConnection> data_connections;
Vector2 scroll;
@@ -233,15 +234,15 @@ private:
HashMap<StringName, Function> functions;
HashMap<StringName, Variable> variables;
- Map<StringName, Vector<Argument>> custom_signals;
- Vector<Multiplayer::RPCConfig> rpc_functions;
+ HashMap<StringName, Vector<Argument>> custom_signals;
+ Dictionary rpc_functions;
- Map<Object *, VisualScriptInstance *> instances;
+ HashMap<Object *, VisualScriptInstance *> instances;
bool is_tool_script;
#ifdef TOOLS_ENABLED
- Set<PlaceHolderScriptInstance *> placeholders;
+ RBSet<PlaceHolderScriptInstance *> placeholders;
// void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
void _update_placeholders();
@@ -283,7 +284,7 @@ public:
void sequence_disconnect(int p_from_node, int p_from_output, int p_to_node);
bool has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const;
void get_sequence_connection_list(List<SequenceConnection> *r_connection) const;
- Set<int> get_output_sequence_ports_connected(int from_node);
+ RBSet<int> get_output_sequence_ports_connected(int from_node);
void data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
@@ -317,7 +318,7 @@ public:
void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx);
void remove_custom_signal(const StringName &p_name);
void rename_custom_signal(const StringName &p_name, const StringName &p_new_name);
- Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node);
+ RBSet<int> get_output_sequence_ports_connected(const String &edited_func, int from_node);
void get_custom_signal_list(List<StringName> *r_custom_signals) const;
@@ -362,7 +363,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
+ virtual const Variant get_rpc_config() const override;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
@@ -376,8 +377,8 @@ class VisualScriptInstance : public ScriptInstance {
Object *owner = nullptr;
Ref<VisualScript> script;
- Map<StringName, Variant> variables; // Using variable path, not script.
- Map<int, VisualScriptNodeInstance *> instances;
+ HashMap<StringName, Variant> variables; // Using variable path, not script.
+ HashMap<int, VisualScriptNodeInstance *> instances;
struct Function {
int node = 0;
@@ -389,7 +390,7 @@ class VisualScriptInstance : public ScriptInstance {
int argument_count = 0;
};
- Map<StringName, Function> functions;
+ HashMap<StringName, Function> functions;
Vector<Variant> default_values;
int max_input_args = 0;
@@ -415,22 +416,22 @@ public:
String to_string(bool *r_valid);
bool set_variable(const StringName &p_variable, const Variant &p_value) {
- Map<StringName, Variant>::Element *E = variables.find(p_variable);
+ HashMap<StringName, Variant>::Iterator E = variables.find(p_variable);
if (!E) {
return false;
}
- E->get() = p_value;
+ E->value = p_value;
return true;
}
bool get_variable(const StringName &p_variable, Variant *r_variable) const {
- const Map<StringName, Variant>::Element *E = variables.find(p_variable);
+ HashMap<StringName, Variant>::ConstIterator E = variables.find(p_variable);
if (!E) {
return false;
}
- *r_variable = E->get();
+ *r_variable = E->value;
return true;
}
@@ -443,7 +444,7 @@ public:
virtual ScriptLanguage *get_language();
- virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
+ virtual const Variant get_rpc_config() const;
VisualScriptInstance();
~VisualScriptInstance();
@@ -480,7 +481,7 @@ public:
typedef Ref<VisualScriptNode> (*VisualScriptNodeRegisterFunc)(const String &p_type);
class VisualScriptLanguage : public ScriptLanguage {
- Map<String, VisualScriptNodeRegisterFunc> register_funcs;
+ HashMap<String, VisualScriptNodeRegisterFunc> register_funcs;
struct CallLevel {
Variant *stack = nullptr;
@@ -521,7 +522,7 @@ public:
if (_debug_call_stack_pos >= _debug_max_call_stack) {
// Stack overflow.
- _debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
+ _debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -544,7 +545,7 @@ public:
}
if (_debug_call_stack_pos == 0) {
- _debug_error = "Stack Underflow (Engine Bug)";
+ _debug_error = "Stack underflow (engine bug), please report.";
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -570,7 +571,7 @@ public:
virtual void get_string_delimiters(List<String> *p_delimiters) const override;
virtual bool is_using_templates() override;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override;
virtual Script *create_script() const override;
virtual bool has_named_classes() const override;
virtual bool supports_builtin_mode() const override;
@@ -598,6 +599,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override;
virtual void profiling_stop() override;
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 7e2df23618..44e792869d 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -989,7 +989,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
}
if (p_inputs[0]->is_ref_counted()) {
- REF r = *p_inputs[0];
+ Ref<RefCounted> r = *p_inputs[0];
if (!r.is_valid()) {
return;
}
@@ -1180,8 +1180,8 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
public:
- VisualScriptBuiltinFunc *node;
- VisualScriptInstance *instance;
+ VisualScriptBuiltinFunc *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
VisualScriptBuiltinFunc::BuiltinFunc func;
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index bd36a9ceec..e0f6436094 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -1299,8 +1299,8 @@ bool VisualScriptExpression::_compile_expression() {
class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
- VisualScriptExpression *expression;
+ VisualScriptInstance *instance = nullptr;
+ VisualScriptExpression *expression = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
//execute by parsing the tree directly
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
index c93eb0686b..7e10f98f36 100644
--- a/modules/visual_script/visual_script_expression.h
+++ b/modules/visual_script/visual_script_expression.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VISUALSCRIPTEXPRESSION_H
-#define VISUALSCRIPTEXPRESSION_H
+#ifndef VISUAL_SCRIPT_EXPRESSION_H
+#define VISUAL_SCRIPT_EXPRESSION_H
#include "visual_script.h"
#include "visual_script_builtin_funcs.h"
@@ -281,4 +281,4 @@ public:
void register_visual_script_expression_node();
-#endif // VISUALSCRIPTEXPRESSION_H
+#endif // VISUAL_SCRIPT_EXPRESSION_H
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 7946d96b29..19bbd834cc 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -119,9 +119,9 @@ void VisualScriptReturn::_bind_methods() {
class VisualScriptNodeInstanceReturn : public VisualScriptNodeInstance {
public:
- VisualScriptReturn *node;
- VisualScriptInstance *instance;
- bool with_value;
+ VisualScriptReturn *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
+ bool with_value = false;
virtual int get_working_memory_size() const override { return 1; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -213,8 +213,8 @@ void VisualScriptCondition::_bind_methods() {
class VisualScriptNodeInstanceCondition : public VisualScriptNodeInstance {
public:
- VisualScriptCondition *node;
- VisualScriptInstance *instance;
+ VisualScriptCondition *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 1; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -293,8 +293,8 @@ void VisualScriptWhile::_bind_methods() {
class VisualScriptNodeInstanceWhile : public VisualScriptNodeInstance {
public:
- VisualScriptWhile *node;
- VisualScriptInstance *instance;
+ VisualScriptWhile *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 1; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -376,8 +376,8 @@ void VisualScriptIterator::_bind_methods() {
class VisualScriptNodeInstanceIterator : public VisualScriptNodeInstance {
public:
- VisualScriptIterator *node;
- VisualScriptInstance *instance;
+ VisualScriptIterator *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
virtual int get_working_memory_size() const override { return 2; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -391,7 +391,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Input type not iterable: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Input type not iterable:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
@@ -414,7 +414,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Iterator became invalid: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Iterator became invalid:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
@@ -508,9 +508,9 @@ void VisualScriptSequence::_bind_methods() {
class VisualScriptNodeInstanceSequence : public VisualScriptNodeInstance {
public:
- VisualScriptSequence *node;
- VisualScriptInstance *instance;
- int steps;
+ VisualScriptSequence *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
+ int steps = 0;
virtual int get_working_memory_size() const override { return 1; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -596,8 +596,8 @@ String VisualScriptSwitch::get_text() const {
class VisualScriptNodeInstanceSwitch : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
- int case_count;
+ VisualScriptInstance *instance = nullptr;
+ int case_count = 0;
//virtual int get_working_memory_size() const override { return 0; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -774,7 +774,7 @@ VisualScriptTypeCast::TypeGuess VisualScriptTypeCast::guess_output_type(TypeGues
class VisualScriptNodeInstanceTypeCast : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
StringName base_type;
String script;
@@ -803,7 +803,7 @@ public:
//if the script is not in use by anyone, we can safely assume whatever we got is not casting to it.
return 1;
}
- Ref<Script> cast_script = Ref<Resource>(ResourceCache::get(script));
+ Ref<Script> cast_script = ResourceCache::get_ref(script);
if (!cast_script.is_valid()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_error_str = "Script path is not a script: " + script;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index 5cc9236a9a..b16358ae38 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -377,7 +377,7 @@ void VisualScriptFunctionCall::_update_method_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -587,7 +587,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_METHOD_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
@@ -720,15 +720,15 @@ class VisualScriptNodeInstanceFunctionCall : public VisualScriptNodeInstance {
public:
VisualScriptFunctionCall::CallMode call_mode;
NodePath node_path;
- int input_args;
- bool validate;
- int returns;
+ int input_args = 0;
+ bool validate = false;
+ int returns = 0;
VisualScriptFunctionCall::RPCCallMode rpc_mode;
StringName function;
StringName singleton;
- VisualScriptFunctionCall *node;
- VisualScriptInstance *instance;
+ VisualScriptFunctionCall *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -1178,7 +1178,7 @@ void VisualScriptPropertySet::_update_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -1338,7 +1338,7 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
@@ -1462,11 +1462,11 @@ public:
NodePath node_path;
StringName property;
- VisualScriptPropertySet *node;
- VisualScriptInstance *instance;
+ VisualScriptPropertySet *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
VisualScriptPropertySet::AssignOp assign_op;
StringName index;
- bool needs_get;
+ bool needs_get = false;
//virtual int get_working_memory_size() const override { return 0; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -1864,7 +1864,7 @@ void VisualScriptPropertyGet::_update_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -2044,7 +2044,7 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
@@ -2152,8 +2152,8 @@ public:
StringName property;
StringName index;
- VisualScriptPropertyGet *node;
- VisualScriptInstance *instance;
+ VisualScriptPropertyGet *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
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) override {
switch (call_mode) {
@@ -2362,9 +2362,9 @@ void VisualScriptEmitSignal::_bind_methods() {
class VisualScriptNodeInstanceEmitSignal : public VisualScriptNodeInstance {
public:
- VisualScriptEmitSignal *node;
- VisualScriptInstance *instance;
- int argcount;
+ VisualScriptEmitSignal *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
+ int argcount = 0;
StringName name;
//virtual int get_working_memory_size() const override { return 0; }
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 6e10b72013..5907e6a489 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -90,7 +90,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
}
if (p_name == "rpc/mode") {
- rpc_mode = Multiplayer::RPCMode(int(p_value));
+ rpc_mode = MultiplayerAPI::RPCMode(int(p_value));
return true;
}
@@ -261,18 +261,18 @@ int VisualScriptFunction::get_argument_count() const {
return arguments.size();
}
-void VisualScriptFunction::set_rpc_mode(Multiplayer::RPCMode p_mode) {
+void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) {
rpc_mode = p_mode;
}
-Multiplayer::RPCMode VisualScriptFunction::get_rpc_mode() const {
+MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const {
return rpc_mode;
}
class VisualScriptNodeInstanceFunction : public VisualScriptNodeInstance {
public:
- VisualScriptFunction *node;
- VisualScriptInstance *instance;
+ VisualScriptFunction *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
@@ -311,14 +311,14 @@ void VisualScriptFunction::reset_state() {
stack_size = 256;
stack_less = false;
sequenced = true;
- rpc_mode = Multiplayer::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
}
VisualScriptFunction::VisualScriptFunction() {
stack_size = 256;
stack_less = false;
sequenced = true;
- rpc_mode = Multiplayer::RPC_MODE_DISABLED;
+ rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
}
void VisualScriptFunction::set_stack_less(bool p_enable) {
@@ -1097,7 +1097,7 @@ void VisualScriptOperator::_bind_methods() {
class VisualScriptNodeInstanceOperator : public VisualScriptNodeInstance {
public:
- bool unary;
+ bool unary = false;
Variant::Operator op;
//virtual int get_working_memory_size() const override { return 0; }
@@ -1116,9 +1116,9 @@ public:
r_error_str = *p_outputs[0];
} else {
if (unary) {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid argument of type:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
} else {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid arguments:") + " A: " + Variant::get_type_name(p_inputs[0]->get_type()) + ", B: " + Variant::get_type_name(p_inputs[1]->get_type());
}
}
}
@@ -1328,14 +1328,14 @@ void VisualScriptVariableGet::_bind_methods() {
class VisualScriptNodeInstanceVariableGet : public VisualScriptNodeInstance {
public:
- VisualScriptVariableGet *node;
- VisualScriptInstance *instance;
+ VisualScriptVariableGet *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
StringName variable;
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) override {
if (!instance->get_variable(variable, p_outputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableGet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableGet not found in script:") + " '" + String(variable) + "'";
return 0;
}
return 0;
@@ -1438,8 +1438,8 @@ void VisualScriptVariableSet::_bind_methods() {
class VisualScriptNodeInstanceVariableSet : public VisualScriptNodeInstance {
public:
- VisualScriptVariableSet *node;
- VisualScriptInstance *instance;
+ VisualScriptVariableSet *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
StringName variable;
//virtual int get_working_memory_size() const override { return 0; }
@@ -1447,7 +1447,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) override {
if (!instance->set_variable(variable, *p_inputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableSet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableSet not found in script:") + " '" + String(variable) + "'";
}
return 0;
@@ -1851,8 +1851,7 @@ int VisualScriptGlobalConstant::get_global_constant() {
class VisualScriptNodeInstanceGlobalConstant : public VisualScriptNodeInstance {
public:
- int index;
- //virtual int get_working_memory_size() const override { return 0; }
+ int index = 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) override {
*p_outputs[0] = CoreConstants::get_global_constant_value(index);
@@ -1963,9 +1962,8 @@ StringName VisualScriptClassConstant::get_base_type() {
class VisualScriptNodeInstanceClassConstant : public VisualScriptNodeInstance {
public:
- int value;
- bool valid;
- //virtual int get_working_memory_size() const override { return 0; }
+ int value = 0;
+ bool valid = false;
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) override {
if (!valid) {
@@ -2098,8 +2096,7 @@ Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const {
class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance {
public:
Variant value;
- bool valid;
- //virtual int get_working_memory_size() const override { return 0; }
+ bool valid = false;
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) override {
if (!valid) {
@@ -2227,8 +2224,7 @@ VisualScriptMathConstant::MathConstant VisualScriptMathConstant::get_math_consta
class VisualScriptNodeInstanceMathConstant : public VisualScriptNodeInstance {
public:
- float value;
- //virtual int get_working_memory_size() const override { return 0; }
+ float value = 0.0f;
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) override {
*p_outputs[0] = value;
@@ -2320,7 +2316,7 @@ String VisualScriptEngineSingleton::get_singleton() {
class VisualScriptNodeInstanceEngineSingleton : public VisualScriptNodeInstance {
public:
- Object *singleton;
+ Object *singleton = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
@@ -2429,8 +2425,8 @@ NodePath VisualScriptSceneNode::get_node_path() {
class VisualScriptNodeInstanceSceneNode : public VisualScriptNodeInstance {
public:
- VisualScriptSceneNode *node;
- VisualScriptInstance *instance;
+ VisualScriptSceneNode *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
NodePath path;
//virtual int get_working_memory_size() const override { return 0; }
@@ -2610,8 +2606,8 @@ String VisualScriptSceneTree::get_caption() const {
class VisualScriptNodeInstanceSceneTree : public VisualScriptNodeInstance {
public:
- VisualScriptSceneTree *node;
- VisualScriptInstance *instance;
+ VisualScriptSceneTree *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
@@ -2779,7 +2775,7 @@ String VisualScriptSelf::get_caption() const {
class VisualScriptNodeInstanceSelf : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
@@ -2965,11 +2961,11 @@ String VisualScriptCustomNode::get_category() const {
class VisualScriptNodeInstanceCustomNode : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
- VisualScriptCustomNode *node;
- int in_count;
- int out_count;
- int work_mem_size;
+ VisualScriptInstance *instance = nullptr;
+ VisualScriptCustomNode *node = nullptr;
+ int in_count = 0;
+ int out_count = 0;
+ int work_mem_size = 0;
virtual int get_working_memory_size() const override { return work_mem_size; }
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) override {
@@ -3161,10 +3157,10 @@ String VisualScriptSubCall::get_category() const {
class VisualScriptNodeInstanceSubCall : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
- VisualScriptSubCall *subcall;
- int input_args;
- bool valid;
+ VisualScriptInstance *instance = nullptr;
+ VisualScriptSubCall *subcall = nullptr;
+ int input_args = 0;
+ bool valid = false;
//virtual int get_working_memory_size() const override { return 0; }
@@ -3281,7 +3277,7 @@ String VisualScriptComment::get_category() const {
class VisualScriptNodeInstanceComment : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
//virtual int get_working_memory_size() const override { return 0; }
@@ -3380,9 +3376,9 @@ Dictionary VisualScriptConstructor::get_constructor() const {
class VisualScriptNodeInstanceConstructor : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
Variant::Type type;
- int argcount;
+ int argcount = 0;
//virtual int get_working_memory_size() const override { return 0; }
@@ -3420,7 +3416,7 @@ VisualScriptConstructor::VisualScriptConstructor() {
type = Variant::NIL;
}
-static Map<String, Pair<Variant::Type, MethodInfo>> constructor_map;
+static HashMap<String, Pair<Variant::Type, MethodInfo>> constructor_map;
static Ref<VisualScriptNode> create_constructor_node(const String &p_name) {
ERR_FAIL_COND_V(!constructor_map.has(p_name), Ref<VisualScriptNode>());
@@ -3497,7 +3493,7 @@ Variant::Type VisualScriptLocalVar::get_var_type() const {
class VisualScriptNodeInstanceLocalVar : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
StringName name;
virtual int get_working_memory_size() const override { return 1; }
@@ -3604,7 +3600,7 @@ Variant::Type VisualScriptLocalVarSet::get_var_type() const {
class VisualScriptNodeInstanceLocalVarSet : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
StringName name;
virtual int get_working_memory_size() const override { return 1; }
@@ -3728,7 +3724,7 @@ VisualScriptInputAction::Mode VisualScriptInputAction::get_action_mode() const {
class VisualScriptNodeInstanceInputAction : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
StringName action;
VisualScriptInputAction::Mode mode;
@@ -3906,7 +3902,7 @@ Array VisualScriptDeconstruct::_get_elem_cache() const {
class VisualScriptNodeInstanceDeconstruct : public VisualScriptNodeInstance {
public:
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
Vector<StringName> outputs;
//virtual int get_working_memory_size() const override { return 0; }
@@ -4029,6 +4025,8 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR2I), create_node_deconst_typed<Variant::Type::VECTOR2I>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3), create_node_deconst_typed<Variant::Type::VECTOR3>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR3I), create_node_deconst_typed<Variant::Type::VECTOR3I>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR4), create_node_deconst_typed<Variant::Type::VECTOR4>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR4I), create_node_deconst_typed<Variant::Type::VECTOR4I>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::COLOR), create_node_deconst_typed<Variant::Type::COLOR>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2), create_node_deconst_typed<Variant::Type::RECT2>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2I), create_node_deconst_typed<Variant::Type::RECT2I>);
@@ -4038,6 +4036,7 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::AABB), create_node_deconst_typed<Variant::Type::AABB>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::BASIS), create_node_deconst_typed<Variant::Type::BASIS>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM3D), create_node_deconst_typed<Variant::Type::TRANSFORM3D>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PROJECTION), create_node_deconst_typed<Variant::Type::PROJECTION>);
VisualScriptLanguage::singleton->add_register_func("functions/compose_array", create_node_generic<VisualScriptComposeArray>);
for (int i = 1; i < Variant::VARIANT_MAX; i++) {
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index 18573f8682..35e3c490cd 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -33,6 +33,7 @@
#include "core/object/gdvirtual.gen.inc"
#include "core/object/script_language.h"
+#include "scene/main/multiplayer_api.h"
#include "visual_script.h"
class VisualScriptFunction : public VisualScriptNode {
@@ -49,7 +50,7 @@ class VisualScriptFunction : public VisualScriptNode {
bool stack_less;
int stack_size;
- Multiplayer::RPCMode rpc_mode;
+ MultiplayerAPI::RPCMode rpc_mode;
bool sequenced;
protected:
@@ -90,8 +91,8 @@ public:
void set_stack_size(int p_size);
int get_stack_size() const;
- void set_rpc_mode(Multiplayer::RPCMode p_mode);
- Multiplayer::RPCMode get_rpc_mode() const;
+ void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
+ MultiplayerAPI::RPCMode get_rpc_mode() const;
virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp
index e6e7f79d1f..96e91a0baf 100644
--- a/modules/visual_script/visual_script_yield_nodes.cpp
+++ b/modules/visual_script/visual_script_yield_nodes.cpp
@@ -93,7 +93,7 @@ String VisualScriptYield::get_text() const {
class VisualScriptNodeInstanceYield : public VisualScriptNodeInstance {
public:
VisualScriptYield::YieldMode mode;
- double wait_time;
+ double wait_time = 0.0;
virtual int get_working_memory_size() const override { return 1; } //yield needs at least 1
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -500,11 +500,11 @@ class VisualScriptNodeInstanceYieldSignal : public VisualScriptNodeInstance {
public:
VisualScriptYieldSignal::CallMode call_mode;
NodePath node_path;
- int output_args;
+ int output_args = 0;
StringName signal;
- VisualScriptYieldSignal *node;
- VisualScriptInstance *instance;
+ VisualScriptYieldSignal *node = nullptr;
+ VisualScriptInstance *instance = nullptr;
virtual int get_working_memory_size() const override { return 1; }
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 5ff5b2339c..8315eea614 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -34,52 +34,114 @@
#include "core/variant/typed_array.h"
#include "thirdparty/libogg/ogg/ogg.h"
-int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
+int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, 0);
- ERR_FAIL_COND_V(!active, 0);
- int todo = p_frames;
+ if (!active) {
+ return 0;
+ }
- int start_buffer = 0;
+ int todo = p_frames;
- int frames_mixed_this_step = p_frames;
+ int beat_length_frames = -1;
+ bool beat_loop = vorbis_stream->has_loop();
+ if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
+ beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
+ }
- while (todo && active) {
+ while (todo > 0 && active) {
AudioFrame *buffer = p_buffer;
- if (start_buffer > 0) {
- buffer = buffer + start_buffer;
- }
- int mixed = _mix_frames_vorbis(buffer, todo);
- if (mixed < 0) {
- return 0;
+ buffer += p_frames - todo;
+
+ int to_mix = todo;
+ if (beat_length_frames >= 0 && (beat_length_frames - (int)frames_mixed) < to_mix) {
+ to_mix = MAX(0, beat_length_frames - (int)frames_mixed);
}
+
+ int mixed = _mix_frames_vorbis(buffer, to_mix);
+ ERR_FAIL_COND_V(mixed < 0, 0);
todo -= mixed;
frames_mixed += mixed;
- start_buffer += mixed;
+
+ if (loop_fade_remaining < FADE_SIZE) {
+ int to_fade = loop_fade_remaining + MIN(FADE_SIZE - loop_fade_remaining, mixed);
+ for (int i = loop_fade_remaining; i < to_fade; i++) {
+ buffer[i - loop_fade_remaining] += loop_fade[i] * (float(FADE_SIZE - i) / float(FADE_SIZE));
+ }
+ loop_fade_remaining = to_fade;
+ }
+
+ if (beat_length_frames >= 0) {
+ /**
+ * Length determined by beat length
+ * This code is commented out because, in practice, it is prefered that the fade
+ * is done by the transitioner and this stream just goes on until it ends while fading out.
+ *
+ * End fade implementation is left here for reference in case at some point this feature
+ * is desired.
+
+ if (!beat_loop && (int)frames_mixed > beat_length_frames - FADE_SIZE) {
+ print_line("beat length fade/after mix?");
+ //No loop, just fade and finish
+ for (int i = 0; i < mixed; i++) {
+ int idx = frames_mixed + i - mixed;
+ buffer[i] *= 1.0 - float(MAX(0, (idx - (beat_length_frames - FADE_SIZE)))) / float(FADE_SIZE);
+ }
+ if ((int)frames_mixed == beat_length_frames) {
+ for (int i = p_frames - todo; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0, 0);
+ }
+ active = false;
+ break;
+ }
+ } else
+ **/
+
+ if (beat_loop && beat_length_frames <= (int)frames_mixed) {
+ // End of file when doing beat-based looping. <= used instead of == because importer editing
+ if (!have_packets_left && !have_samples_left) {
+ //Nothing remaining, so do nothing.
+ loop_fade_remaining = FADE_SIZE;
+ } else {
+ // Add some loop fade;
+ int faded_mix = _mix_frames_vorbis(loop_fade, FADE_SIZE);
+
+ for (int i = faded_mix; i < FADE_SIZE; i++) {
+ // In case lesss was mixed, pad with zeros
+ loop_fade[i] = AudioFrame(0, 0);
+ }
+ loop_fade_remaining = 0;
+ }
+
+ seek(vorbis_stream->loop_offset);
+ loops++;
+ // We still have buffer to fill, start from this element in the next iteration.
+ continue;
+ }
+ }
+
if (!have_packets_left && !have_samples_left) {
- //end of file!
+ // Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
if (vorbis_stream->loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
loops++;
- // we still have buffer to fill, start from this element in the next iteration.
- start_buffer = p_frames - todo;
+ // We still have buffer to fill, start from this element in the next iteration.
+
} else {
- frames_mixed_this_step = p_frames - todo;
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
active = false;
- todo = 0;
}
}
}
- return frames_mixed_this_step;
+ return p_frames - todo;
}
-int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
+int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, 0);
if (!have_samples_left) {
ogg_packet *packet = nullptr;
@@ -122,18 +184,18 @@ int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p
return frames;
}
-float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {
+float AudioStreamPlaybackOggVorbis::get_stream_sampling_rate() {
return vorbis_data->get_sampling_rate();
}
-bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() {
+bool AudioStreamPlaybackOggVorbis::_alloc_vorbis() {
vorbis_info_init(&info);
info_is_allocated = true;
vorbis_comment_init(&comment);
comment_is_allocated = true;
ERR_FAIL_COND_V(vorbis_data.is_null(), false);
- vorbis_data_playback = vorbis_data->instance_playback();
+ vorbis_data_playback = vorbis_data->instantiate_playback();
ogg_packet *packet;
int err;
@@ -161,31 +223,36 @@ bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() {
return true;
}
-void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {
+void AudioStreamPlaybackOggVorbis::start(float p_from_pos) {
ERR_FAIL_COND(!ready);
+ loop_fade_remaining = FADE_SIZE;
active = true;
seek(p_from_pos);
loops = 0;
begin_resample();
}
-void AudioStreamPlaybackOGGVorbis::stop() {
+void AudioStreamPlaybackOggVorbis::stop() {
active = false;
}
-bool AudioStreamPlaybackOGGVorbis::is_playing() const {
+bool AudioStreamPlaybackOggVorbis::is_playing() const {
return active;
}
-int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
+int AudioStreamPlaybackOggVorbis::get_loop_count() const {
return loops;
}
-float AudioStreamPlaybackOGGVorbis::get_playback_position() const {
+float AudioStreamPlaybackOggVorbis::get_playback_position() const {
return float(frames_mixed) / vorbis_data->get_sampling_rate();
}
-void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
+void AudioStreamPlaybackOggVorbis::tag_used_streams() {
+ vorbis_stream->tag_used(get_playback_position());
+}
+
+void AudioStreamPlaybackOggVorbis::seek(float p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
if (!active) {
@@ -303,7 +370,7 @@ void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
}
}
-AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
+AudioStreamPlaybackOggVorbis::~AudioStreamPlaybackOggVorbis() {
if (block_is_allocated) {
vorbis_block_clear(&block);
}
@@ -318,13 +385,13 @@ AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
}
}
-Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
- Ref<AudioStreamPlaybackOGGVorbis> ovs;
+Ref<AudioStreamPlayback> AudioStreamOggVorbis::instantiate_playback() {
+ Ref<AudioStreamPlaybackOggVorbis> ovs;
ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr);
ovs.instantiate();
- ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this);
+ ovs->vorbis_stream = Ref<AudioStreamOggVorbis>(this);
ovs->vorbis_data = packet_sequence;
ovs->frames_mixed = 0;
ovs->active = false;
@@ -336,11 +403,11 @@ Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
return nullptr;
}
-String AudioStreamOGGVorbis::get_stream_name() const {
+String AudioStreamOggVorbis::get_stream_name() const {
return ""; //return stream_name;
}
-void AudioStreamOGGVorbis::maybe_update_info() {
+void AudioStreamOggVorbis::maybe_update_info() {
ERR_FAIL_COND(packet_sequence.is_null());
vorbis_info info;
@@ -350,8 +417,7 @@ void AudioStreamOGGVorbis::maybe_update_info() {
vorbis_info_init(&info);
vorbis_comment_init(&comment);
- int packet_count = 0;
- Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback();
+ Ref<OggPacketSequencePlayback> packet_sequence_playback = packet_sequence->instantiate_playback();
for (int i = 0; i < 3; i++) {
ogg_packet *packet;
@@ -369,8 +435,6 @@ void AudioStreamOGGVorbis::maybe_update_info() {
err = vorbis_synthesis_headerin(&info, &comment, packet);
ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err));
-
- packet_count++;
}
packet_sequence->set_sampling_rate(info.rate);
@@ -379,57 +443,99 @@ void AudioStreamOGGVorbis::maybe_update_info() {
vorbis_info_clear(&info);
}
-void AudioStreamOGGVorbis::set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence) {
+void AudioStreamOggVorbis::set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence) {
packet_sequence = p_packet_sequence;
if (packet_sequence.is_valid()) {
maybe_update_info();
}
}
-Ref<OGGPacketSequence> AudioStreamOGGVorbis::get_packet_sequence() const {
+Ref<OggPacketSequence> AudioStreamOggVorbis::get_packet_sequence() const {
return packet_sequence;
}
-void AudioStreamOGGVorbis::set_loop(bool p_enable) {
+void AudioStreamOggVorbis::set_loop(bool p_enable) {
loop = p_enable;
}
-bool AudioStreamOGGVorbis::has_loop() const {
+bool AudioStreamOggVorbis::has_loop() const {
return loop;
}
-void AudioStreamOGGVorbis::set_loop_offset(float p_seconds) {
+void AudioStreamOggVorbis::set_loop_offset(float p_seconds) {
loop_offset = p_seconds;
}
-float AudioStreamOGGVorbis::get_loop_offset() const {
+float AudioStreamOggVorbis::get_loop_offset() const {
return loop_offset;
}
-float AudioStreamOGGVorbis::get_length() const {
+float AudioStreamOggVorbis::get_length() const {
ERR_FAIL_COND_V(packet_sequence.is_null(), 0);
return packet_sequence->get_length();
}
-bool AudioStreamOGGVorbis::is_monophonic() const {
+void AudioStreamOggVorbis::set_bpm(double p_bpm) {
+ ERR_FAIL_COND(p_bpm < 0);
+ bpm = p_bpm;
+ emit_changed();
+}
+
+double AudioStreamOggVorbis::get_bpm() const {
+ return bpm;
+}
+
+void AudioStreamOggVorbis::set_beat_count(int p_beat_count) {
+ ERR_FAIL_COND(p_beat_count < 0);
+ beat_count = p_beat_count;
+ emit_changed();
+}
+
+int AudioStreamOggVorbis::get_beat_count() const {
+ return beat_count;
+}
+
+void AudioStreamOggVorbis::set_bar_beats(int p_bar_beats) {
+ ERR_FAIL_COND(p_bar_beats < 2);
+ bar_beats = p_bar_beats;
+ emit_changed();
+}
+
+int AudioStreamOggVorbis::get_bar_beats() const {
+ return bar_beats;
+}
+
+bool AudioStreamOggVorbis::is_monophonic() const {
return false;
}
-void AudioStreamOGGVorbis::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOGGVorbis::set_packet_sequence);
- ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOGGVorbis::get_packet_sequence);
+void AudioStreamOggVorbis::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence);
+ ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOggVorbis::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOggVorbis::has_loop);
+
+ ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOggVorbis::set_loop_offset);
+ ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOggVorbis::get_loop_offset);
+
+ ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamOggVorbis::set_bpm);
+ ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamOggVorbis::get_bpm);
- ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
- ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);
+ ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamOggVorbis::set_beat_count);
+ ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamOggVorbis::get_beat_count);
- ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
- ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
+ ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamOggVorbis::set_bar_beats);
+ ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamOggVorbis::get_bar_beats);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
-AudioStreamOGGVorbis::AudioStreamOGGVorbis() {}
+AudioStreamOggVorbis::AudioStreamOggVorbis() {}
-AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {}
+AudioStreamOggVorbis::~AudioStreamOggVorbis() {}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index b09ef9ff5d..0350e1f761 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -28,23 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef AUDIO_STREAM_LIBVORBIS_H
-#define AUDIO_STREAM_LIBVORBIS_H
+#ifndef AUDIO_STREAM_OGG_VORBIS_H
+#define AUDIO_STREAM_OGG_VORBIS_H
#include "core/variant/variant.h"
#include "modules/ogg/ogg_packet_sequence.h"
#include "servers/audio/audio_stream.h"
#include "thirdparty/libvorbis/vorbis/codec.h"
-class AudioStreamOGGVorbis;
+class AudioStreamOggVorbis;
-class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
- GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled);
+class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
+ GDCLASS(AudioStreamPlaybackOggVorbis, AudioStreamPlaybackResampled);
uint32_t frames_mixed = 0;
bool active = false;
int loops = 0;
+ enum {
+ FADE_SIZE = 256
+ };
+ AudioFrame loop_fade[FADE_SIZE];
+ int loop_fade_remaining = FADE_SIZE;
+
vorbis_info info;
vorbis_comment comment;
vorbis_dsp_state dsp_state;
@@ -60,12 +66,13 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
bool have_samples_left = false;
bool have_packets_left = false;
- friend class AudioStreamOGGVorbis;
+ friend class AudioStreamOggVorbis;
- Ref<OGGPacketSequence> vorbis_data;
- Ref<OGGPacketSequencePlayback> vorbis_data_playback;
- Ref<AudioStreamOGGVorbis> vorbis_stream;
+ Ref<OggPacketSequence> vorbis_data;
+ Ref<OggPacketSequencePlayback> vorbis_data_playback;
+ Ref<AudioStreamOggVorbis> vorbis_stream;
+ int _mix_frames(AudioFrame *p_buffer, int p_frames);
int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames);
// Allocates vorbis data structures. Returns true upon success, false on failure.
@@ -85,16 +92,18 @@ public:
virtual float get_playback_position() const override;
virtual void seek(float p_time) override;
- AudioStreamPlaybackOGGVorbis() {}
- ~AudioStreamPlaybackOGGVorbis();
+ virtual void tag_used_streams() override;
+
+ AudioStreamPlaybackOggVorbis() {}
+ ~AudioStreamPlaybackOggVorbis();
};
-class AudioStreamOGGVorbis : public AudioStream {
- GDCLASS(AudioStreamOGGVorbis, AudioStream);
+class AudioStreamOggVorbis : public AudioStream {
+ GDCLASS(AudioStreamOggVorbis, AudioStream);
OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged.
RES_BASE_EXTENSION("oggvorbisstr");
- friend class AudioStreamPlaybackOGGVorbis;
+ friend class AudioStreamPlaybackOggVorbis;
int channels = 1;
float length = 0.0;
@@ -105,30 +114,43 @@ class AudioStreamOGGVorbis : public AudioStream {
// Also causes allocation and deallocation.
void maybe_update_info();
- Ref<OGGPacketSequence> packet_sequence;
+ Ref<OggPacketSequence> packet_sequence;
+
+ double bpm = 0;
+ int beat_count = 0;
+ int bar_beats = 4;
protected:
static void _bind_methods();
public:
void set_loop(bool p_enable);
- bool has_loop() const;
+ virtual bool has_loop() const override;
void set_loop_offset(float p_seconds);
float get_loop_offset() const;
- virtual Ref<AudioStreamPlayback> instance_playback() override;
+ void set_bpm(double p_bpm);
+ virtual double get_bpm() const override;
+
+ void set_beat_count(int p_beat_count);
+ virtual int get_beat_count() const override;
+
+ void set_bar_beats(int p_bar_beats);
+ virtual int get_bar_beats() const override;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
- void set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence);
- Ref<OGGPacketSequence> get_packet_sequence() const;
+ void set_packet_sequence(Ref<OggPacketSequence> p_packet_sequence);
+ Ref<OggPacketSequence> get_packet_sequence() const;
virtual float get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
- AudioStreamOGGVorbis();
- virtual ~AudioStreamOGGVorbis();
+ AudioStreamOggVorbis();
+ virtual ~AudioStreamOggVorbis();
};
-#endif // AUDIO_STREAM_LIBVORBIS_H
+#endif // AUDIO_STREAM_OGG_VORBIS_H
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 978eccb29f..7ce885a37a 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -8,8 +8,8 @@ def configure(env):
def get_doc_classes():
return [
- "AudioStreamOGGVorbis",
- "AudioStreamPlaybackOGGVorbis",
+ "AudioStreamOggVorbis",
+ "AudioStreamPlaybackOggVorbis",
]
diff --git a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
index 2f210a6cb4..225ea4e6ae 100644
--- a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AudioStreamOGGVorbis" inherits="AudioStream" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="AudioStreamOggVorbis" inherits="AudioStream" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
@@ -7,14 +7,20 @@
<tutorials>
</tutorials>
<members>
+ <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4">
+ </member>
+ <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0">
+ </member>
+ <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0">
+ </member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
If [code]true[/code], the stream will automatically loop when it reaches the end.
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0">
Time in seconds at which the stream starts after being looped.
</member>
- <member name="packet_sequence" type="OGGPacketSequence" setter="set_packet_sequence" getter="get_packet_sequence">
- Contains the raw OGG data for this stream.
+ <member name="packet_sequence" type="OggPacketSequence" setter="set_packet_sequence" getter="get_packet_sequence">
+ Contains the raw Ogg data for this stream.
</member>
</members>
</class>
diff --git a/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml
index 68aa46147f..0879c773ac 100644
--- a/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml
+++ b/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AudioStreamPlaybackOGGVorbis" inherits="AudioStreamPlaybackResampled" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="AudioStreamPlaybackOggVorbis" inherits="AudioStreamPlaybackResampled" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
index 1baf7b2edc..84a71fe82d 100644
--- a/modules/vorbis/register_types.cpp
+++ b/modules/vorbis/register_types.cpp
@@ -33,16 +33,24 @@
#include "audio_stream_ogg_vorbis.h"
#include "resource_importer_ogg_vorbis.h"
-void register_vorbis_types() {
+void initialize_vorbis_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- Ref<ResourceImporterOGGVorbis> ogg_vorbis_importer;
+ Ref<ResourceImporterOggVorbis> ogg_vorbis_importer;
ogg_vorbis_importer.instantiate();
ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer);
}
#endif
- GDREGISTER_CLASS(AudioStreamOGGVorbis);
- GDREGISTER_CLASS(AudioStreamPlaybackOGGVorbis);
+ GDREGISTER_CLASS(AudioStreamOggVorbis);
+ GDREGISTER_CLASS(AudioStreamPlaybackOggVorbis);
}
-void unregister_vorbis_types() {}
+void uninitialize_vorbis_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/vorbis/register_types.h b/modules/vorbis/register_types.h
index 666c7e5b9f..74c18b9c04 100644
--- a/modules/vorbis/register_types.h
+++ b/modules/vorbis/register_types.h
@@ -31,7 +31,9 @@
#ifndef VORBIS_REGISTER_TYPES_H
#define VORBIS_REGISTER_TYPES_H
-void register_vorbis_types();
-void unregister_vorbis_types();
+#include "modules/register_module_types.h"
+
+void initialize_vorbis_module(ModuleInitializationLevel p_level);
+void uninitialize_vorbis_module(ModuleInitializationLevel p_level);
#endif // VORBIS_REGISTER_TYPES_H
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 03e145216a..bf5f7206b8 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -30,56 +30,59 @@
#include "resource_importer_ogg_vorbis.h"
-#include "audio_stream_ogg_vorbis.h"
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#include "scene/resources/texture.h"
#include "thirdparty/libogg/ogg/ogg.h"
#include "thirdparty/libvorbis/vorbis/codec.h"
-String ResourceImporterOGGVorbis::get_importer_name() const {
+#ifdef TOOLS_ENABLED
+#include "editor/import/audio_stream_import_settings.h"
+#endif
+
+String ResourceImporterOggVorbis::get_importer_name() const {
return "oggvorbisstr";
}
-String ResourceImporterOGGVorbis::get_visible_name() const {
+String ResourceImporterOggVorbis::get_visible_name() const {
return "oggvorbisstr";
}
-void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
+void ResourceImporterOggVorbis::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("ogg");
}
-String ResourceImporterOGGVorbis::get_save_extension() const {
+String ResourceImporterOggVorbis::get_save_extension() const {
return "oggvorbisstr";
}
-String ResourceImporterOGGVorbis::get_resource_type() const {
- return "AudioStreamOGGVorbis";
+String ResourceImporterOggVorbis::get_resource_type() const {
+ return "AudioStreamOggVorbis";
}
-bool ResourceImporterOGGVorbis::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const {
+bool ResourceImporterOggVorbis::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
-int ResourceImporterOGGVorbis::get_preset_count() const {
+int ResourceImporterOggVorbis::get_preset_count() const {
return 0;
}
-String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const {
+String ResourceImporterOggVorbis::get_preset_name(int p_idx) const {
return String();
}
-void ResourceImporterOGGVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
+void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4));
}
-Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
-
- Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
- ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
+Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'.");
uint64_t len = f->get_length();
@@ -89,10 +92,10 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
f->get_buffer(w, len);
- Ref<AudioStreamOGGVorbis> ogg_vorbis_stream;
+ Ref<AudioStreamOggVorbis> ogg_vorbis_stream;
ogg_vorbis_stream.instantiate();
- Ref<OGGPacketSequence> ogg_packet_sequence;
+ Ref<OggPacketSequence> ogg_packet_sequence;
ogg_packet_sequence.instantiate();
ogg_stream_state stream_state;
@@ -107,16 +110,16 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
size_t packet_count = 0;
bool done = false;
while (!done) {
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
while (ogg_sync_pageout(&sync_state, &page) != 1) {
if (cursor >= len) {
done = true;
break;
}
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
- ERR_FAIL_COND_V(cursor > len, Error::ERR_INVALID_DATA);
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOggVorbis>());
size_t copy_size = len - cursor;
if (copy_size > OGG_SYNC_BUFFER_SIZE) {
copy_size = OGG_SYNC_BUFFER_SIZE;
@@ -124,22 +127,22 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
memcpy(sync_buf, &file_data[cursor], copy_size);
ogg_sync_wrote(&sync_state, copy_size);
cursor += copy_size;
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
}
if (done) {
break;
}
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
// Have a page now.
if (!initialized_stream) {
if (ogg_stream_init(&stream_state, ogg_page_serialno(&page))) {
- ERR_FAIL_V_MSG(Error::ERR_OUT_OF_MEMORY, "Failed allocating memory for OGG Vorbis stream.");
+ ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Failed allocating memory for Ogg Vorbis stream.");
}
initialized_stream = true;
}
ogg_stream_pagein(&stream_state, &page);
- ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Ref<AudioStreamOggVorbis>(), "Ogg stream error " + itos(err));
int desync_iters = 0;
Vector<Vector<uint8_t>> packet_data;
@@ -150,7 +153,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
if (err == -1) {
// According to the docs this is usually recoverable, but don't sit here spinning forever.
desync_iters++;
- ERR_FAIL_COND_V_MSG(desync_iters > 100, Error::ERR_INVALID_DATA, "Packet sync issue during ogg import");
+ ERR_FAIL_COND_V_MSG(desync_iters > 100, Ref<AudioStreamOggVorbis>(), "Packet sync issue during Ogg import");
continue;
} else if (err == 0) {
// Not enough data to fully reconstruct a packet. Go on to the next page.
@@ -183,15 +186,48 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
ogg_sync_clear(&sync_state);
if (ogg_packet_sequence->get_packet_granule_positions().is_empty()) {
- ERR_FAIL_V_MSG(Error::ERR_FILE_CORRUPT, "OGG Vorbis decoding failed. Check that your data is a valid OGG Vorbis audio stream.");
+ ERR_FAIL_V_MSG(Ref<AudioStreamOggVorbis>(), "Ogg Vorbis decoding failed. Check that your data is a valid Ogg Vorbis audio stream.");
}
ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence);
+
+ return ogg_vorbis_stream;
+}
+
+#ifdef TOOLS_ENABLED
+
+bool ResourceImporterOggVorbis::has_advanced_options() const {
+ return true;
+}
+
+void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
+ Ref<AudioStreamOggVorbis> ogg_stream = import_ogg_vorbis(p_path);
+ if (ogg_stream.is_valid()) {
+ AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream);
+ }
+}
+#endif
+
+Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+ double bpm = p_options["bpm"];
+ int beat_count = p_options["beat_count"];
+ int bar_beats = p_options["bar_beats"];
+
+ Ref<AudioStreamOggVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file);
+ if (ogg_vorbis_stream.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
ogg_vorbis_stream->set_loop(loop);
ogg_vorbis_stream->set_loop_offset(loop_offset);
+ ogg_vorbis_stream->set_bpm(bpm);
+ ogg_vorbis_stream->set_beat_count(beat_count);
+ ogg_vorbis_stream->set_bar_beats(bar_beats);
- return ResourceSaver::save(p_save_path + ".oggvorbisstr", ogg_vorbis_stream);
+ return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr");
}
-ResourceImporterOGGVorbis::ResourceImporterOGGVorbis() {
+ResourceImporterOggVorbis::ResourceImporterOggVorbis() {
}
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 07291803a1..a1a970e2cc 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -31,10 +31,11 @@
#ifndef RESOURCE_IMPORTER_OGG_VORBIS_H
#define RESOURCE_IMPORTER_OGG_VORBIS_H
+#include "audio_stream_ogg_vorbis.h"
#include "core/io/resource_importer.h"
-class ResourceImporterOGGVorbis : public ResourceImporter {
- GDCLASS(ResourceImporterOGGVorbis, ResourceImporter);
+class ResourceImporterOggVorbis : public ResourceImporter {
+ GDCLASS(ResourceImporterOggVorbis, ResourceImporter);
enum {
OGG_SYNC_BUFFER_SIZE = 8192,
@@ -43,7 +44,13 @@ class ResourceImporterOGGVorbis : public ResourceImporter {
private:
// virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0;
+ static Ref<AudioStreamOggVorbis> import_ogg_vorbis(const String &p_path);
+
public:
+#ifdef TOOLS_ENABLED
+ virtual bool has_advanced_options() const override;
+ virtual void show_advanced_options(const String &p_path) override;
+#endif
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;
@@ -52,11 +59,11 @@ public:
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
- virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- ResourceImporterOGGVorbis();
+ ResourceImporterOggVorbis();
};
#endif // RESOURCE_IMPORTER_OGG_VORBIS_H
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 0e41f6c973..778d562278 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -34,184 +34,21 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include "webp_common.h"
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
-static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- uint8_t *dst_buff = nullptr;
- size_t dst_size = 0;
- if (img->get_format() == Image::FORMAT_RGB8) {
- dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- } else {
- dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- }
-
- ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
- Vector<uint8_t> dst;
- dst.resize(4 + dst_size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], dst_buff, dst_size);
- WebPFree(dst_buff);
-
- return dst;
-}
-
-static Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
- compression_level = CLAMP(compression_level, 0, 9);
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- // we need to use the more complex API in order to access the 'exact' flag...
-
- WebPConfig config;
- WebPPicture pic;
- if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
- ERR_FAIL_V(Vector<uint8_t>());
- }
-
- WebPMemoryWriter wrt;
- config.exact = 1;
- pic.use_argb = 1;
- pic.width = s.width;
- pic.height = s.height;
- pic.writer = WebPMemoryWrite;
- pic.custom_ptr = &wrt;
- WebPMemoryWriterInit(&wrt);
-
- bool success_import = false;
- if (img->get_format() == Image::FORMAT_RGB8) {
- success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
- } else {
- success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
- }
- bool success_encode = false;
- if (success_import) {
- success_encode = WebPEncode(&config, &pic);
- }
- WebPPictureFree(&pic);
-
- if (!success_encode) {
- WebPMemoryWriterClear(&wrt);
- ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
- }
-
- // copy from wrt
- Vector<uint8_t> dst;
- dst.resize(4 + wrt.size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], wrt.mem, wrt.size);
- WebPMemoryWriterClear(&wrt);
-
- return dst;
-}
-
-static Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
- int size = p_buffer.size() - 4;
- ERR_FAIL_COND_V(size <= 0, Ref<Image>());
- const uint8_t *r = p_buffer.ptr();
-
- ERR_FAIL_COND_V(r[0] != 'W' || r[1] != 'E' || r[2] != 'B' || r[3] != 'P', Ref<Image>());
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(&r[4], size, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
- }
-
- /*
- print_line("width: "+itos(features.width));
- print_line("height: "+itos(features.height));
- print_line("alpha: "+itos(features.has_alpha));
- */
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
-
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(&r[4], size, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(&r[4], size, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
-
- Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
- return img;
-}
-
-Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
-
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V(ERR_FILE_CORRUPT);
- }
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
-
- p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
-
- return OK;
-}
-
static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
img.instantiate();
- Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
+ Error err = WebPCommon::webp_load_image_from_buffer(img.ptr(), p_png, p_size);
ERR_FAIL_COND_V(err, Ref<Image>());
return img;
}
-Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
@@ -221,18 +58,18 @@ Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_
f->get_buffer(&w[0], src_image_len);
- Error err = webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
+ Error err = WebPCommon::webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
return err;
}
-void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
+void ImageLoaderWebP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("webp");
}
-ImageLoaderWEBP::ImageLoaderWEBP() {
+ImageLoaderWebP::ImageLoaderWebP() {
Image::_webp_mem_loader_func = _webp_mem_loader_func;
- Image::webp_lossy_packer = _webp_lossy_pack;
- Image::webp_lossless_packer = _webp_lossless_pack;
- Image::webp_unpacker = _webp_unpack;
+ Image::webp_lossy_packer = WebPCommon::_webp_lossy_pack;
+ Image::webp_lossless_packer = WebPCommon::_webp_lossless_pack;
+ Image::webp_unpacker = WebPCommon::_webp_unpack;
}
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index 1acd1459a0..9a5dc6cd7c 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -33,11 +33,11 @@
#include "core/io/image_loader.h"
-class ImageLoaderWEBP : public ImageFormatLoader {
+class ImageLoaderWebP : public ImageFormatLoader {
public:
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
- ImageLoaderWEBP();
+ ImageLoaderWebP();
};
-#endif
+#endif // IMAGE_LOADER_WEBP_H
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 462c0a0b3d..29f633743e 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -31,14 +31,28 @@
#include "register_types.h"
#include "image_loader_webp.h"
+#include "resource_saver_webp.h"
-static ImageLoaderWEBP *image_loader_webp = nullptr;
+static ImageLoaderWebP *image_loader_webp = nullptr;
+static Ref<ResourceSaverWebP> resource_saver_webp;
-void register_webp_types() {
- image_loader_webp = memnew(ImageLoaderWEBP);
+void initialize_webp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
+ image_loader_webp = memnew(ImageLoaderWebP);
+ resource_saver_webp.instantiate();
ImageLoader::add_image_format_loader(image_loader_webp);
+ ResourceSaver::add_resource_format_saver(resource_saver_webp);
}
-void unregister_webp_types() {
+void uninitialize_webp_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
memdelete(image_loader_webp);
+ ResourceSaver::remove_resource_format_saver(resource_saver_webp);
+ resource_saver_webp.unref();
}
diff --git a/modules/webp/register_types.h b/modules/webp/register_types.h
index 828ef07f9a..6e37dcfb61 100644
--- a/modules/webp/register_types.h
+++ b/modules/webp/register_types.h
@@ -31,7 +31,9 @@
#ifndef WEBP_REGISTER_TYPES_H
#define WEBP_REGISTER_TYPES_H
-void register_webp_types();
-void unregister_webp_types();
+#include "modules/register_module_types.h"
+
+void initialize_webp_module(ModuleInitializationLevel p_level);
+void uninitialize_webp_module(ModuleInitializationLevel p_level);
#endif // WEBP_REGISTER_TYPES_H
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
new file mode 100644
index 0000000000..bd71c2869a
--- /dev/null
+++ b/modules/webp/resource_saver_webp.cpp
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* resource_saver_webp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_saver_webp.h"
+
+#include "core/io/file_access.h"
+#include "core/io/image.h"
+#include "scene/resources/texture.h"
+#include "webp_common.h"
+
+Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
+ Ref<ImageTexture> texture = p_resource;
+
+ ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP.");
+ ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP.");
+
+ Ref<Image> img = texture->get_image();
+
+ Error err = save_image(p_path, img);
+
+ return err;
+}
+
+Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality);
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path));
+
+ const uint8_t *reader = buffer.ptr();
+
+ file->store_buffer(reader, buffer.size());
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+Vector<uint8_t> ResourceSaverWebP::save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer;
+ if (p_lossy) {
+ buffer = WebPCommon::_webp_lossy_pack(p_img, p_quality);
+ } else {
+ buffer = WebPCommon::_webp_lossless_pack(p_img);
+ }
+ return buffer;
+}
+
+bool ResourceSaverWebP::recognize(const Ref<Resource> &p_resource) const {
+ return (p_resource.is_valid() && p_resource->is_class("ImageTexture"));
+}
+
+void ResourceSaverWebP::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ if (Object::cast_to<ImageTexture>(*p_resource)) {
+ p_extensions->push_back("webp");
+ }
+}
+
+ResourceSaverWebP::ResourceSaverWebP() {
+ Image::save_webp_func = &save_image;
+ Image::save_webp_buffer_func = &save_image_to_buffer;
+}
diff --git a/modules/webp/resource_saver_webp.h b/modules/webp/resource_saver_webp.h
new file mode 100644
index 0000000000..cbd5864463
--- /dev/null
+++ b/modules/webp/resource_saver_webp.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* resource_saver_webp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_SAVER_WEBP_H
+#define RESOURCE_SAVER_WEBP_H
+
+#include "core/io/image.h"
+#include "core/io/resource_saver.h"
+
+class ResourceSaverWebP : public ResourceFormatSaver {
+public:
+ static Error save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+ static Vector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+
+ virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+
+ ResourceSaverWebP();
+};
+
+#endif // RESOURCE_SAVER_WEBP_H
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
new file mode 100644
index 0000000000..8657a98853
--- /dev/null
+++ b/modules/webp/webp_common.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* webp_common.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "webp_common.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+
+#include <string.h>
+#include <webp/decode.h>
+#include <webp/encode.h>
+
+namespace WebPCommon {
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ uint8_t *dst_buff = nullptr;
+ size_t dst_size = 0;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ } else {
+ dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ }
+
+ ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
+ Vector<uint8_t> dst;
+ dst.resize(dst_size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, dst_buff, dst_size);
+ WebPFree(dst_buff);
+
+ return dst;
+}
+
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
+ compression_level = CLAMP(compression_level, 0, 9);
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ // we need to use the more complex API in order to access the 'exact' flag...
+
+ WebPConfig config;
+ WebPPicture pic;
+ if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
+ ERR_FAIL_V(Vector<uint8_t>());
+ }
+
+ WebPMemoryWriter wrt;
+ config.exact = 1;
+ pic.use_argb = 1;
+ pic.width = s.width;
+ pic.height = s.height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ bool success_import = false;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
+ } else {
+ success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
+ }
+ bool success_encode = false;
+ if (success_import) {
+ success_encode = WebPEncode(&config, &pic);
+ }
+ WebPPictureFree(&pic);
+
+ if (!success_encode) {
+ WebPMemoryWriterClear(&wrt);
+ ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
+ }
+
+ // copy from wrt
+ Vector<uint8_t> dst;
+ dst.resize(wrt.size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, wrt.mem, wrt.size);
+ WebPMemoryWriterClear(&wrt);
+ return dst;
+}
+
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
+ int size = p_buffer.size();
+ ERR_FAIL_COND_V(size <= 0, Ref<Image>());
+ const uint8_t *r = p_buffer.ptr();
+
+ // A WebP file uses a RIFF header, which starts with "RIFF____WEBP".
+ ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>());
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(r, size, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(r, size, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
+
+ Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
+ return img;
+}
+
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
+
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
+
+ p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
+
+ return OK;
+}
+} // namespace WebPCommon
diff --git a/modules/webp/webp_common.h b/modules/webp/webp_common.h
new file mode 100644
index 0000000000..11bef40256
--- /dev/null
+++ b/modules/webp/webp_common.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* webp_common.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WEBP_COMMON_H
+#define WEBP_COMMON_H
+
+#include "core/io/image.h"
+
+namespace WebPCommon {
+// Given an image, pack this data into a WebP file.
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality);
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image);
+// Given a WebP file, unpack it into an image.
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer);
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len);
+} //namespace WebPCommon
+
+#endif // WEBP_COMMON_H
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index 3996a002ed..df92097135 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -57,7 +57,7 @@
Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647).
If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted.
If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED].
- You can optionally specify a [code]channels_config[/code] array of [enum TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="remove_peer">
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index b4d97077e3..fed67397d1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -95,6 +95,13 @@
Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
+ <method name="set_default_extension" qualifiers="static">
+ <return type="void" />
+ <argument index="0" name="extension_class" type="StringName" />
+ <description>
+ Sets the [code]extension_class[/code] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
+ </description>
+ </method>
<method name="set_local_description">
<return type="int" enum="Error" />
<argument index="0" name="type" type="String" />
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
index e88acdc845..163d939ac1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -62,10 +62,5 @@
<description>
</description>
</method>
- <method name="make_default">
- <return type="void" />
- <description>
- </description>
- </method>
</methods>
</class>
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 283e421e11..09cd538b96 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -37,7 +37,11 @@
#include "webrtc_data_channel_extension.h"
#include "webrtc_peer_connection_extension.h"
-void register_webrtc_types() {
+void initialize_webrtc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#define SET_HINT(NAME, _VAL_, _MAX_) \
GLOBAL_DEF(NAME, _VAL_); \
ProjectSettings::get_singleton()->set_custom_property_info(NAME, PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"));
@@ -55,4 +59,8 @@ void register_webrtc_types() {
#undef SET_HINT
}
-void unregister_webrtc_types() {}
+void uninitialize_webrtc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h
index 2e4457beaf..17171d0e0b 100644
--- a/modules/webrtc/register_types.h
+++ b/modules/webrtc/register_types.h
@@ -31,7 +31,9 @@
#ifndef WEBRTC_REGISTER_TYPES_H
#define WEBRTC_REGISTER_TYPES_H
-void register_webrtc_types();
-void unregister_webrtc_types();
+#include "modules/register_module_types.h"
+
+void initialize_webrtc_module(ModuleInitializationLevel p_level);
+void uninitialize_webrtc_module(ModuleInitializationLevel p_level);
#endif // WEBRTC_REGISTER_TYPES_H
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index eac8f85a84..9d20ad3266 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -33,7 +33,7 @@
#include "core/io/packet_peer.h"
-#define WRTC_IN_BUF "network/limits/webrtc/max_channel_in_buffer_kb"
+#define WRTC_IN_BUF PNAME("network/limits/webrtc/max_channel_in_buffer_kb")
class WebRTCDataChannel : public PacketPeer {
GDCLASS(WebRTCDataChannel, PacketPeer);
@@ -81,4 +81,5 @@ public:
VARIANT_ENUM_CAST(WebRTCDataChannel::WriteMode);
VARIANT_ENUM_CAST(WebRTCDataChannel::ChannelState);
+
#endif // WEBRTC_DATA_CHANNEL_H
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index 0bc42b104c..e03b6b2473 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -140,41 +140,41 @@ void WebRTCMultiplayerPeer::poll() {
}
void WebRTCMultiplayerPeer::_find_next_peer() {
- Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(next_packet_peer);
+ HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(next_packet_peer);
if (E) {
- E = E->next();
+ ++E;
}
// After last.
while (E) {
- if (!E->get()->connected) {
- E = E->next();
+ if (!E->value->connected) {
+ ++E;
continue;
}
- for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
- next_packet_peer = E->key();
+ next_packet_peer = E->key;
return;
}
}
- E = E->next();
+ ++E;
}
- E = peer_map.front();
+ E = peer_map.begin();
// Before last
while (E) {
- if (!E->get()->connected) {
- E = E->next();
+ if (!E->value->connected) {
+ ++E;
continue;
}
- for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
- next_packet_peer = E->key();
+ next_packet_peer = E->key;
return;
}
}
- if (E->key() == (int)next_packet_peer) {
+ if (E->key == (int)next_packet_peer) {
break;
}
- E = E->next();
+ ++E;
}
// No packet found
next_packet_peer = 0;
@@ -197,14 +197,14 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
cfg["ordered"] = true;
switch (mode) {
- case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
+ case TRANSFER_MODE_UNRELIABLE_ORDERED:
cfg["maxPacketLifetime"] = 1;
break;
- case Multiplayer::TRANSFER_MODE_UNRELIABLE:
+ case TRANSFER_MODE_UNRELIABLE:
cfg["maxPacketLifetime"] = 1;
cfg["ordered"] = false;
break;
- case Multiplayer::TRANSFER_MODE_RELIABLE:
+ case TRANSFER_MODE_RELIABLE:
break;
default:
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode));
@@ -339,13 +339,13 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
int ch = get_transfer_channel();
if (ch == 0) {
switch (get_transfer_mode()) {
- case Multiplayer::TRANSFER_MODE_RELIABLE:
+ case TRANSFER_MODE_RELIABLE:
ch = CH_RELIABLE;
break;
- case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
+ case TRANSFER_MODE_UNRELIABLE_ORDERED:
ch = CH_ORDERED;
break;
- case Multiplayer::TRANSFER_MODE_UNRELIABLE:
+ case TRANSFER_MODE_UNRELIABLE:
ch = CH_UNRELIABLE;
break;
}
@@ -354,12 +354,12 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
}
if (target_peer > 0) {
- Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(target_peer);
+ HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(target_peer);
ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + ".");
- ERR_FAIL_COND_V_MSG(E->value()->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
- ERR_FAIL_COND_V(E->value()->channels[ch].is_null(), ERR_BUG);
- return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
+ ERR_FAIL_COND_V_MSG(E->value->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value->channels.size()));
+ ERR_FAIL_COND_V(E->value->channels[ch].is_null(), ERR_BUG);
+ return E->value->channels[ch]->put_packet(p_buffer, p_buffer_size);
} else {
int exclude = -target_peer;
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index 6675c67867..ea7c60036b 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_MULTIPLAYER_H
-#define WEBRTC_MULTIPLAYER_H
+#ifndef WEBRTC_MULTIPLAYER_PEER_H
+#define WEBRTC_MULTIPLAYER_PEER_H
-#include "core/multiplayer/multiplayer_peer.h"
+#include "scene/main/multiplayer_peer.h"
#include "webrtc_peer_connection.h"
class WebRTCMultiplayerPeer : public MultiplayerPeer {
@@ -69,7 +69,7 @@ private:
int next_packet_peer = 0;
bool server_compat = false;
- Map<int, Ref<ConnectedPeer>> peer_map;
+ HashMap<int, Ref<ConnectedPeer>> peer_map;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
@@ -106,4 +106,4 @@ public:
ConnectionStatus get_connection_status() const override;
};
-#endif // WEBRTC_MULTIPLAYER_H
+#endif // WEBRTC_MULTIPLAYER_PEER_H
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 7fdf26d3cd..75716017d7 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -32,13 +32,14 @@
#ifdef JAVASCRIPT_ENABLED
#include "webrtc_peer_connection_js.h"
-#else
-#include "webrtc_peer_connection_extension.h"
#endif
+#include "webrtc_peer_connection_extension.h"
+
StringName WebRTCPeerConnection::default_extension;
void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) {
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_extension, WebRTCPeerConnectionExtension::get_class_static()), vformat("Can't make %s the default WebRTC extension since it does not extend WebRTCPeerConnectionExtension.", p_extension));
default_extension = p_extension;
}
@@ -56,6 +57,8 @@ WebRTCPeerConnection *WebRTCPeerConnection::create() {
}
void WebRTCPeerConnection::_bind_methods() {
+ ClassDB::bind_static_method(get_class_static(), D_METHOD("set_default_extension", "extension_class"), &WebRTCPeerConnectionExtension::set_default_extension);
+
ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
index 8c324d0942..122ea3d00f 100644
--- a/modules/webrtc/webrtc_peer_connection.h
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -74,4 +74,5 @@ public:
};
VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+
#endif // WEBRTC_PEER_CONNECTION_H
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
index 3bc7de217e..85c04b3b19 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.cpp
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -31,8 +31,6 @@
#include "webrtc_peer_connection_extension.h"
void WebRTCPeerConnectionExtension::_bind_methods() {
- ClassDB::bind_method(D_METHOD("make_default"), &WebRTCPeerConnectionExtension::make_default);
-
GDVIRTUAL_BIND(_get_connection_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
@@ -44,11 +42,6 @@ void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_close);
}
-void WebRTCPeerConnectionExtension::make_default() {
- ERR_FAIL_COND_MSG(!_get_extension(), vformat("Can't make %s the default without extending it.", get_class()));
- WebRTCPeerConnection::set_default_extension(get_class());
-}
-
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const {
int state;
if (GDVIRTUAL_CALL(_get_connection_state, state)) {
diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h
index 82e32b5602..bde19c173b 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -44,8 +44,6 @@ protected:
static void _bind_methods();
public:
- void make_default();
-
virtual ConnectionState get_connection_state() const override;
virtual Error initialize(Dictionary p_config = Dictionary()) override;
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index 90e19fe4f1..ee3a302fa2 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -57,7 +57,7 @@ void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
- peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
+ peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannel>(memnew(WebRTCDataChannelJS(p_id))));
}
void WebRTCPeerConnectionJS::close() {
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index 8fa5ea7779..76b8c7fff8 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -52,6 +52,8 @@ extern int godot_js_rtc_pc_datachannel_create(int p_id, const char *p_label, con
}
class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
+ GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
+
private:
int _js_id;
ConnectionState _conn_state;
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index ca327a56fa..b71fd78124 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EMWSCLIENT_H
-#define EMWSCLIENT_H
+#ifndef EMWS_CLIENT_H
+#define EMWS_CLIENT_H
#ifdef JAVASCRIPT_ENABLED
@@ -68,4 +68,4 @@ public:
#endif // JAVASCRIPT_ENABLED
-#endif // EMWSCLIENT_H
+#endif // EMWS_CLIENT_H
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index 6bb4552c37..f52f615c35 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EMWSPEER_H
-#define EMWSPEER_H
+#ifndef EMWS_PEER_H
+#define EMWS_PEER_H
#ifdef JAVASCRIPT_ENABLED
@@ -90,4 +90,4 @@ public:
#endif // JAVASCRIPT_ENABLED
-#endif // LSWPEER_H
+#endif // EMWS_PEER_H
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index ae31d9dbb0..14a9449605 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef EMWSSERVER_H
-#define EMWSSERVER_H
+#ifndef EMWS_SERVER_H
+#define EMWS_SERVER_H
#ifdef JAVASCRIPT_ENABLED
@@ -61,4 +61,4 @@ public:
#endif
-#endif // LWSSERVER_H
+#endif // EMWS_SERVER_H
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 6d63938d4f..f562de111f 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -55,25 +55,33 @@ static void _editor_init_callback() {
}
#endif
-void register_websocket_types() {
+void initialize_websocket_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
#ifdef JAVASCRIPT_ENABLED
- EMWSPeer::make_default();
- EMWSClient::make_default();
- EMWSServer::make_default();
+ EMWSPeer::make_default();
+ EMWSClient::make_default();
+ EMWSServer::make_default();
#else
- WSLPeer::make_default();
- WSLClient::make_default();
- WSLServer::make_default();
+ WSLPeer::make_default();
+ WSLClient::make_default();
+ WSLServer::make_default();
#endif
- GDREGISTER_ABSTRACT_CLASS(WebSocketMultiplayerPeer);
- ClassDB::register_custom_instance_class<WebSocketServer>();
- ClassDB::register_custom_instance_class<WebSocketClient>();
- ClassDB::register_custom_instance_class<WebSocketPeer>();
+ GDREGISTER_ABSTRACT_CLASS(WebSocketMultiplayerPeer);
+ ClassDB::register_custom_instance_class<WebSocketServer>();
+ ClassDB::register_custom_instance_class<WebSocketClient>();
+ ClassDB::register_custom_instance_class<WebSocketPeer>();
+ }
#ifdef TOOLS_ENABLED
- EditorNode::add_init_callback(&_editor_init_callback);
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorNode::add_init_callback(&_editor_init_callback);
+ }
#endif
}
-void unregister_websocket_types() {}
+void uninitialize_websocket_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
index 4ab6c0cfd3..dab42d6ed9 100644
--- a/modules/websocket/register_types.h
+++ b/modules/websocket/register_types.h
@@ -31,7 +31,9 @@
#ifndef WEBSOCKET_REGISTER_TYPES_H
#define WEBSOCKET_REGISTER_TYPES_H
-void register_websocket_types();
-void unregister_websocket_types();
+#include "modules/register_module_types.h"
+
+void initialize_websocket_module(ModuleInitializationLevel p_level);
+void uninitialize_websocket_module(ModuleInitializationLevel p_level);
#endif // WEBSOCKET_REGISTER_TYPES_H
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 43d9d59f38..3259e78b3b 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -32,8 +32,8 @@
#define WEBSOCKET_MULTIPLAYER_PEER_H
#include "core/error/error_list.h"
-#include "core/multiplayer/multiplayer_peer.h"
#include "core/templates/list.h"
+#include "scene/main/multiplayer_peer.h"
#include "websocket_peer.h"
class WebSocketMultiplayerPeer : public MultiplayerPeer {
@@ -62,7 +62,7 @@ protected:
};
List<Packet> _incoming_packets;
- Map<int, Ref<WebSocketPeer>> _peer_map;
+ HashMap<int, Ref<WebSocketPeer>> _peer_map;
Packet _current_packet;
bool _is_multiplayer = false;
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 13fef2424f..22099f7258 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBSOCKETPEER_H
-#define WEBSOCKETPEER_H
+#ifndef WEBSOCKET_PEER_H
+#define WEBSOCKET_PEER_H
#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
@@ -66,4 +66,5 @@ public:
};
VARIANT_ENUM_CAST(WebSocketPeer::WriteMode);
-#endif // WEBSOCKETPEER_H
+
+#endif // WEBSOCKET_PEER_H
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 7bd80851f5..ac04c4e57e 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBSOCKET_H
-#define WEBSOCKET_H
+#ifndef WEBSOCKET_SERVER_H
+#define WEBSOCKET_SERVER_H
#include "core/crypto/crypto.h"
#include "core/object/ref_counted.h"
@@ -87,4 +87,4 @@ public:
~WebSocketServer();
};
-#endif // WEBSOCKET_H
+#endif // WEBSOCKET_SERVER_H
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index 58c329f043..478dbb9d47 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -91,6 +91,7 @@ void WSLClient::_do_handshake() {
data->id = 1;
_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
_peer->set_no_delay(true);
+ _status = CONNECTION_CONNECTED;
_on_connect(protocol);
break;
}
@@ -103,15 +104,16 @@ bool WSLClient::_verify_headers(String &r_protocol) {
String s = (char *)_resp_buf;
Vector<String> psa = s.split("\r\n");
int len = psa.size();
- ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
+ ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers. Got: " + itos(len) + ", expected >= 4.");
Vector<String> req = psa[0].split(" ", false);
- ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code.");
+ ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code. Got '" + psa[0] + "', expected 'HTTP/1.1 101'.");
// Wrong protocol
- ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1" || req[1] != "101", false, "Invalid protocol or status code.");
+ ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1", false, "Invalid protocol. Got: '" + req[0] + "', expected 'HTTP/1.1'.");
+ ERR_FAIL_COND_V_MSG(req[1] != "101", false, "Invalid status code. Got: '" + req[1] + "', expected '101'.");
- Map<String, String> headers;
+ HashMap<String, String> headers;
for (int i = 1; i < len; i++) {
Vector<String> header = psa[i].split(":", false, 1);
ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i] + ".");
@@ -137,9 +139,11 @@ bool WSLClient::_verify_headers(String &r_protocol) {
#undef WSL_CHECK
if (_protocols.size() == 0) {
// We didn't request a custom protocol
- ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false);
+ ERR_FAIL_COND_V_MSG(headers.has("sec-websocket-protocol"), false, "Received unrequested sub-protocol -> " + headers["sec-websocket-protocol"]);
} else {
- ERR_FAIL_COND_V(!headers.has("sec-websocket-protocol"), false);
+ // We requested at least one custom protocol but didn't receive one
+ ERR_FAIL_COND_V_MSG(!headers.has("sec-websocket-protocol"), false, "Requested sub-protocol(s) but received none.");
+ // Check received sub-protocol was one of those requested.
r_protocol = headers["sec-websocket-protocol"];
bool valid = false;
for (int i = 0; i < _protocols.size(); i++) {
@@ -150,6 +154,7 @@ bool WSLClient::_verify_headers(String &r_protocol) {
break;
}
if (!valid) {
+ ERR_FAIL_V_MSG(false, "Received unrequested sub-protocol -> " + r_protocol);
return false;
}
}
@@ -227,6 +232,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
request += "\r\n";
_request = request.utf8();
+ _status = CONNECTION_CONNECTING;
return OK;
}
@@ -333,21 +339,19 @@ Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const {
}
MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
+ // This is surprising, but keeps the current behaviour to allow clean close requests.
+ // TODO Refactor WebSocket and split Client/Server/Multiplayer like done in other peers.
if (_peer->is_connected_to_host()) {
return CONNECTION_CONNECTED;
}
-
- if (_tcp->get_status() == StreamPeerTCP::STATUS_CONNECTING || _resolver_id != IP::RESOLVER_INVALID_ID) {
- return CONNECTION_CONNECTING;
- }
-
- return CONNECTION_DISCONNECTED;
+ return _status;
}
void WSLClient::disconnect_from_host(int p_code, String p_reason) {
_peer->close(p_code, p_reason);
_connection = Ref<StreamPeer>(nullptr);
_tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+ _status = CONNECTION_DISCONNECTED;
_key = "";
_host = "";
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 22b3a4f373..58b867fbe4 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WSLCLIENT_H
-#define WSLCLIENT_H
+#ifndef WSL_CLIENT_H
+#define WSL_CLIENT_H
#ifndef JAVASCRIPT_ENABLED
@@ -52,6 +52,7 @@ private:
Ref<WSLPeer> _peer;
Ref<StreamPeerTCP> _tcp;
Ref<StreamPeer> _connection;
+ ConnectionStatus _status = CONNECTION_DISCONNECTED;
CharString _request;
int _requested = 0;
@@ -59,11 +60,9 @@ private:
uint8_t _resp_buf[WSL_MAX_HEADER_SIZE];
int _resp_pos = 0;
- String _response;
-
String _key;
String _host;
- uint16_t _port;
+ uint16_t _port = 0;
Array _ip_candidates;
Vector<String> _protocols;
bool _use_ssl = false;
@@ -89,4 +88,4 @@ public:
#endif // JAVASCRIPT_ENABLED
-#endif // WSLCLIENT_H
+#endif // WSL_CLIENT_H
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index abeecdd537..aabd3fd43e 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WSLPEER_H
-#define WSLPEER_H
+#ifndef WSL_PEER_H
+#define WSL_PEER_H
#ifndef JAVASCRIPT_ENABLED
@@ -112,4 +112,4 @@ public:
#endif // JAVASCRIPT_ENABLED
-#endif // LSWPEER_H
+#endif // WSL_PEER_H
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index b58b2e4724..517b9643f8 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -46,7 +46,7 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St
ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
r_resource_name = req[1];
- Map<String, String> headers;
+ HashMap<String, String> headers;
for (int i = 1; i < len; i++) {
Vector<String> header = psa[i].split(":", false, 1);
ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i]);
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
index a920e9c665..ec7567c732 100644
--- a/modules/websocket/wsl_server.h
+++ b/modules/websocket/wsl_server.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WSLSERVER_H
-#define WSLSERVER_H
+#ifndef WSL_SERVER_H
+#define WSL_SERVER_H
#ifndef JAVASCRIPT_ENABLED
@@ -95,4 +95,4 @@ public:
#endif // JAVASCRIPT_ENABLED
-#endif // WSLSERVER_H
+#endif // WSL_SERVER_H
diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h
index 34225df001..52104895d4 100644
--- a/modules/webxr/godot_webxr.h
+++ b/modules/webxr/godot_webxr.h
@@ -82,4 +82,4 @@ extern int *godot_webxr_get_bounds_geometry();
}
#endif
-#endif /* GODOT_WEBXR_H */
+#endif // GODOT_WEBXR_H
diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp
index a15dc93248..cd403a4996 100644
--- a/modules/webxr/register_types.cpp
+++ b/modules/webxr/register_types.cpp
@@ -37,7 +37,11 @@
Ref<WebXRInterfaceJS> webxr;
#endif
-void register_webxr_types() {
+void initialize_webxr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
GDREGISTER_ABSTRACT_CLASS(WebXRInterface);
#ifdef JAVASCRIPT_ENABLED
@@ -46,7 +50,11 @@ void register_webxr_types() {
#endif
}
-void unregister_webxr_types() {
+void uninitialize_webxr_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
#ifdef JAVASCRIPT_ENABLED
if (webxr.is_valid()) {
// uninitialise our interface if it is initialised
diff --git a/modules/webxr/register_types.h b/modules/webxr/register_types.h
index 5dda728099..2ea9bc1169 100644
--- a/modules/webxr/register_types.h
+++ b/modules/webxr/register_types.h
@@ -31,7 +31,9 @@
#ifndef WEBXR_REGISTER_TYPES_H
#define WEBXR_REGISTER_TYPES_H
-void register_webxr_types();
-void unregister_webxr_types();
+#include "modules/register_module_types.h"
+
+void initialize_webxr_module(ModuleInitializationLevel p_level);
+void uninitialize_webxr_module(ModuleInitializationLevel p_level);
#endif // WEBXR_REGISTER_TYPES_H
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index debfe8e950..07e6760555 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -291,15 +291,15 @@ void WebXRInterfaceJS::uninitialize() {
Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) {
Transform3D transform;
- transform.basis.elements[0].x = p_js_matrix[0];
- transform.basis.elements[1].x = p_js_matrix[1];
- transform.basis.elements[2].x = p_js_matrix[2];
- transform.basis.elements[0].y = p_js_matrix[4];
- transform.basis.elements[1].y = p_js_matrix[5];
- transform.basis.elements[2].y = p_js_matrix[6];
- transform.basis.elements[0].z = p_js_matrix[8];
- transform.basis.elements[1].z = p_js_matrix[9];
- transform.basis.elements[2].z = p_js_matrix[10];
+ transform.basis.rows[0].x = p_js_matrix[0];
+ transform.basis.rows[1].x = p_js_matrix[1];
+ transform.basis.rows[2].x = p_js_matrix[2];
+ transform.basis.rows[0].y = p_js_matrix[4];
+ transform.basis.rows[1].y = p_js_matrix[5];
+ transform.basis.rows[2].y = p_js_matrix[6];
+ transform.basis.rows[0].z = p_js_matrix[8];
+ transform.basis.rows[1].z = p_js_matrix[9];
+ transform.basis.rows[2].z = p_js_matrix[10];
transform.origin.x = p_js_matrix[12];
transform.origin.y = p_js_matrix[13];
transform.origin.z = p_js_matrix[14];
@@ -363,8 +363,8 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran
return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
};
-CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
- CameraMatrix eye;
+Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
+ Projection eye;
float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
if (!initialized || js_matrix == nullptr) {
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 31858194f6..f1ffedba46 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -87,7 +87,7 @@ public:
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index 139df9c735..e9985984a6 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -222,9 +222,16 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
return true;
}
-void register_xatlas_unwrap_types() {
+void initialize_xatlas_unwrap_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
array_mesh_lightmap_unwrap_callback = xatlas_mesh_lightmap_unwrap_callback;
}
-void unregister_xatlas_unwrap_types() {
+void uninitialize_xatlas_unwrap_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
}
diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h
index 63a985f8c5..f82b5912f4 100644
--- a/modules/xatlas_unwrap/register_types.h
+++ b/modules/xatlas_unwrap/register_types.h
@@ -31,7 +31,9 @@
#ifndef XATLAS_UNWRAP_REGISTER_TYPES_H
#define XATLAS_UNWRAP_REGISTER_TYPES_H
-void register_xatlas_unwrap_types();
-void unregister_xatlas_unwrap_types();
+#include "modules/register_module_types.h"
+
+void initialize_xatlas_unwrap_module(ModuleInitializationLevel p_level);
+void uninitialize_xatlas_unwrap_module(ModuleInitializationLevel p_level);
#endif // XATLAS_UNWRAP_REGISTER_TYPES_H