summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/astcenc/SCsub55
-rw-r--r--modules/astcenc/config.py6
-rw-r--r--modules/astcenc/image_compress_astcenc.cpp251
-rw-r--r--modules/astcenc/image_compress_astcenc.h39
-rw-r--r--modules/astcenc/register_types.cpp48
-rw-r--r--modules/astcenc/register_types.h (renamed from modules/visual_script/register_types.h)68
-rw-r--r--modules/basis_universal/SCsub4
-rw-r--r--modules/basis_universal/register_types.cpp61
-rw-r--r--modules/basis_universal/register_types.h58
-rw-r--r--modules/bmp/SCsub2
-rw-r--r--modules/bmp/image_loader_bmp.cpp84
-rw-r--r--modules/bmp/image_loader_bmp.h60
-rw-r--r--modules/bmp/register_types.cpp65
-rw-r--r--modules/bmp/register_types.h58
-rw-r--r--modules/camera/camera_macos.h58
-rw-r--r--modules/camera/camera_macos.mm62
-rw-r--r--modules/camera/camera_win.cpp58
-rw-r--r--modules/camera/camera_win.h58
-rw-r--r--modules/camera/register_types.cpp58
-rw-r--r--modules/camera/register_types.h58
-rw-r--r--modules/csg/SCsub5
-rw-r--r--modules/csg/csg.cpp60
-rw-r--r--modules/csg/csg.h59
-rw-r--r--modules/csg/csg_shape.cpp232
-rw-r--r--modules/csg/csg_shape.h66
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml19
-rw-r--r--modules/csg/editor/csg_gizmos.cpp78
-rw-r--r--modules/csg/editor/csg_gizmos.h58
-rw-r--r--modules/csg/register_types.cpp58
-rw-r--r--modules/csg/register_types.h58
-rw-r--r--modules/cvtt/config.py2
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp62
-rw-r--r--modules/cvtt/image_compress_cvtt.h58
-rw-r--r--modules/cvtt/register_types.cpp58
-rw-r--r--modules/cvtt/register_types.h58
-rw-r--r--modules/dds/register_types.cpp58
-rw-r--r--modules/dds/register_types.h58
-rw-r--r--modules/dds/texture_loader_dds.cpp58
-rw-r--r--modules/dds/texture_loader_dds.h58
-rw-r--r--modules/denoise/SCsub2
-rw-r--r--modules/denoise/config.py11
-rw-r--r--modules/denoise/denoise_wrapper.cpp58
-rw-r--r--modules/denoise/denoise_wrapper.h58
-rw-r--r--modules/denoise/lightmap_denoiser.cpp60
-rw-r--r--modules/denoise/lightmap_denoiser.h58
-rw-r--r--modules/denoise/register_types.cpp58
-rw-r--r--modules/denoise/register_types.h58
-rw-r--r--modules/enet/doc_classes/ENetConnection.xml60
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml46
-rw-r--r--modules/enet/doc_classes/ENetPacketPeer.xml66
-rw-r--r--modules/enet/enet_connection.cpp63
-rw-r--r--modules/enet/enet_connection.h63
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp519
-rw-r--r--modules/enet/enet_multiplayer_peer.h89
-rw-r--r--modules/enet/enet_packet_peer.cpp60
-rw-r--r--modules/enet/enet_packet_peer.h58
-rw-r--r--modules/enet/register_types.cpp58
-rw-r--r--modules/enet/register_types.h58
-rw-r--r--modules/etcpak/config.py2
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp80
-rw-r--r--modules/etcpak/image_compress_etcpak.h58
-rw-r--r--modules/etcpak/register_types.cpp58
-rw-r--r--modules/etcpak/register_types.h58
-rw-r--r--modules/freetype/SCsub37
-rw-r--r--modules/freetype/config.py8
-rw-r--r--modules/freetype/register_types.cpp58
-rw-r--r--modules/freetype/register_types.h58
-rw-r--r--modules/freetype/uwpdef.h58
-rw-r--r--modules/gdscript/SCsub2
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml350
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml8
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp373
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h67
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp66
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h58
-rw-r--r--modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd2
-rw-r--r--modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd2
-rw-r--r--modules/gdscript/gdscript.cpp793
-rw-r--r--modules/gdscript/gdscript.h108
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp2213
-rw-r--r--modules/gdscript/gdscript_analyzer.h102
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp476
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h72
-rw-r--r--modules/gdscript/gdscript_cache.cpp306
-rw-r--r--modules/gdscript/gdscript_cache.h86
-rw-r--r--modules/gdscript/gdscript_codegen.h62
-rw-r--r--modules/gdscript/gdscript_compiler.cpp662
-rw-r--r--modules/gdscript/gdscript_compiler.h76
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp93
-rw-r--r--modules/gdscript/gdscript_editor.cpp387
-rw-r--r--modules/gdscript/gdscript_function.cpp75
-rw-r--r--modules/gdscript/gdscript_function.h67
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp58
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h58
-rw-r--r--modules/gdscript/gdscript_parser.cpp345
-rw-r--r--modules/gdscript/gdscript_parser.h194
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp58
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h58
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp77
-rw-r--r--modules/gdscript/gdscript_tokenizer.h58
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp76
-rw-r--r--modules/gdscript/gdscript_utility_functions.h61
-rw-r--r--modules/gdscript/gdscript_vm.cpp527
-rw-r--r--modules/gdscript/gdscript_warning.cpp74
-rw-r--r--modules/gdscript/gdscript_warning.h60
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp119
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h58
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp64
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h58
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp71
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h58
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp96
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h59
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp107
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h58
-rw-r--r--modules/gdscript/language_server/godot_lsp.h62
-rw-r--r--modules/gdscript/register_types.cpp65
-rw-r--r--modules/gdscript/register_types.h58
-rw-r--r--modules/gdscript/tests/README.md2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp131
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h58
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h92
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_a.notest.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_b.notest.gd (renamed from modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_base.notest.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_c.notest.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_extend.notest.gd23
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd20
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out17
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out (renamed from modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd (renamed from modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd)2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd112
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out55
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd86
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out (renamed from modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd (renamed from modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd)3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_base.gd18
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_base.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lambda_typed.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lambda_typed.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd50
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd41
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd32
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/null_initializer.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd51
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd39
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_inline.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.gd17
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/match_multiple_variable_binds_in_branch.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd62
-rw-r--r--modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd48
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_name.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary.out4
-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/function_many_parameters.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_callable.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd76
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd40
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_assert.gd24
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_assert.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_dictionary.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/str_preserves_case.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd17
-rw-r--r--modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/vector_inf.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd11
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.out11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_on_void.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_on_void.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd51
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out14
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.gd20
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/gdscript.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.gd14
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd25
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out17
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.gd60
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/range_returns_ints.gd77
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/range_returns_ints.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd45
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.gd11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd9
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_assignment.out12
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out3
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp58
-rw-r--r--modules/gdscript/tests/test_gdscript.h58
-rw-r--r--modules/glslang/glslang_resource_limits.h67
-rw-r--r--modules/glslang/register_types.cpp62
-rw-r--r--modules/glslang/register_types.h58
-rw-r--r--modules/gltf/SCsub6
-rw-r--r--modules/gltf/config.py1
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml41
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml65
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml85
-rw-r--r--modules/gltf/doc_classes/GLTFLight.xml31
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml22
-rw-r--r--modules/gltf/doc_classes/GLTFSkeleton.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFSkin.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFSpecGloss.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml108
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml3
-rw-r--r--modules/gltf/doc_classes/GLTFTextureSampler.xml25
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp68
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.h60
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp109
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h64
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.cpp96
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.h71
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp75
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.h63
-rw-r--r--modules/gltf/extensions/SCsub9
-rw-r--r--modules/gltf/extensions/gltf_document_extension.cpp (renamed from modules/gltf/gltf_document_extension.cpp)161
-rw-r--r--modules/gltf/extensions/gltf_document_extension.h (renamed from modules/gltf/gltf_document_extension.h)86
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp (renamed from modules/gltf/gltf_document_extension_convert_importer_mesh.cpp)63
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h (renamed from modules/gltf/gltf_document_extension_convert_importer_mesh.h)62
-rw-r--r--modules/gltf/extensions/gltf_light.cpp179
-rw-r--r--modules/gltf/extensions/gltf_light.h70
-rw-r--r--modules/gltf/extensions/gltf_spec_gloss.cpp60
-rw-r--r--modules/gltf/extensions/gltf_spec_gloss.h66
-rw-r--r--modules/gltf/gltf_defines.h65
-rw-r--r--modules/gltf/gltf_document.cpp3130
-rw-r--r--modules/gltf/gltf_document.h393
-rw-r--r--modules/gltf/gltf_state.cpp172
-rw-r--r--modules/gltf/gltf_state.h162
-rw-r--r--modules/gltf/gltf_template_convert.h64
-rw-r--r--modules/gltf/register_types.cpp131
-rw-r--r--modules/gltf/register_types.h63
-rw-r--r--modules/gltf/structures/gltf_accessor.cpp58
-rw-r--r--modules/gltf/structures/gltf_accessor.h61
-rw-r--r--modules/gltf/structures/gltf_animation.cpp58
-rw-r--r--modules/gltf/structures/gltf_animation.h60
-rw-r--r--modules/gltf/structures/gltf_buffer_view.cpp60
-rw-r--r--modules/gltf/structures/gltf_buffer_view.h58
-rw-r--r--modules/gltf/structures/gltf_camera.cpp157
-rw-r--r--modules/gltf/structures/gltf_camera.h92
-rw-r--r--modules/gltf/structures/gltf_mesh.cpp62
-rw-r--r--modules/gltf/structures/gltf_mesh.h68
-rw-r--r--modules/gltf/structures/gltf_node.cpp68
-rw-r--r--modules/gltf/structures/gltf_node.h62
-rw-r--r--modules/gltf/structures/gltf_skeleton.cpp62
-rw-r--r--modules/gltf/structures/gltf_skeleton.h62
-rw-r--r--modules/gltf/structures/gltf_skin.cpp63
-rw-r--r--modules/gltf/structures/gltf_skin.h65
-rw-r--r--modules/gltf/structures/gltf_texture.cpp69
-rw-r--r--modules/gltf/structures/gltf_texture.h61
-rw-r--r--modules/gltf/structures/gltf_texture_sampler.cpp47
-rw-r--r--modules/gltf/structures/gltf_texture_sampler.h162
-rw-r--r--modules/gridmap/SCsub4
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml111
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp151
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h64
-rw-r--r--modules/gridmap/grid_map.cpp514
-rw-r--r--modules/gridmap/grid_map.h99
-rw-r--r--modules/gridmap/register_types.cpp58
-rw-r--r--modules/gridmap/register_types.h58
-rw-r--r--modules/hdr/SCsub2
-rw-r--r--modules/hdr/image_loader_hdr.cpp64
-rw-r--r--modules/hdr/image_loader_hdr.h60
-rw-r--r--modules/hdr/register_types.cpp65
-rw-r--r--modules/hdr/register_types.h58
-rw-r--r--modules/jpg/image_loader_jpegd.cpp100
-rw-r--r--modules/jpg/image_loader_jpegd.h60
-rw-r--r--modules/jpg/register_types.cpp65
-rw-r--r--modules/jpg/register_types.h58
-rw-r--r--modules/jsonrpc/jsonrpc.cpp58
-rw-r--r--modules/jsonrpc/jsonrpc.h58
-rw-r--r--modules/jsonrpc/register_types.cpp58
-rw-r--r--modules/jsonrpc/register_types.h58
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp132
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h62
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl2
-rw-r--r--modules/lightmapper_rd/register_types.cpp59
-rw-r--r--modules/lightmapper_rd/register_types.h58
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp58
-rw-r--r--modules/mbedtls/crypto_mbedtls.h64
-rw-r--r--modules/mbedtls/dtls_server_mbedtls.cpp58
-rw-r--r--modules/mbedtls/dtls_server_mbedtls.h60
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.cpp109
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.h62
-rw-r--r--modules/mbedtls/register_types.cpp62
-rw-r--r--modules/mbedtls/register_types.h58
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.cpp100
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h72
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.cpp58
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.h58
-rw-r--r--modules/mbedtls/tls_context_mbedtls.cpp (renamed from modules/mbedtls/ssl_context_mbedtls.cpp)90
-rw-r--r--modules/mbedtls/tls_context_mbedtls.h (renamed from modules/mbedtls/ssl_context_mbedtls.h)78
-rw-r--r--modules/meshoptimizer/register_types.cpp58
-rw-r--r--modules/meshoptimizer/register_types.h58
-rw-r--r--modules/minimp3/SCsub2
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp72
-rw-r--r--modules/minimp3/audio_stream_mp3.h70
-rw-r--r--modules/minimp3/register_types.cpp58
-rw-r--r--modules/minimp3/register_types.h58
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp58
-rw-r--r--modules/minimp3/resource_importer_mp3.h58
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml2
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp60
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h58
-rw-r--r--modules/mobile_vr/register_types.cpp60
-rw-r--r--modules/mobile_vr/register_types.h58
-rw-r--r--modules/mono/.editorconfig29
-rw-r--r--modules/mono/.gitignore3
-rw-r--r--modules/mono/Directory.Build.props5
-rw-r--r--modules/mono/Directory.Build.targets26
-rw-r--r--modules/mono/README.md55
-rw-r--r--modules/mono/SCsub37
-rw-r--r--modules/mono/SdkPackageVersions.props7
-rw-r--r--modules/mono/build_scripts/api_solution_build.py80
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py389
-rw-r--r--modules/mono/build_scripts/gen_cs_glue_version.py20
-rw-r--r--modules/mono/build_scripts/godot_net_sdk_build.py55
-rw-r--r--modules/mono/build_scripts/godot_tools_build.py38
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py55
-rw-r--r--modules/mono/build_scripts/mono_android_config.xml28
-rw-r--r--modules/mono/build_scripts/mono_configure.py570
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py112
-rw-r--r--modules/mono/build_scripts/solution_builder.py145
-rw-r--r--modules/mono/class_db_api_json.cpp61
-rw-r--r--modules/mono/class_db_api_json.h58
-rw-r--r--modules/mono/config.py58
-rw-r--r--modules/mono/csharp_script.cpp2301
-rw-r--r--modules/mono/csharp_script.h265
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml47
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props25
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs7
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs118
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs202
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs31
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs36
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs339
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/EventHandlerSuffixSuppressor.cs53
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs291
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj14
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs139
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs82
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs63
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs73
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs383
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs24
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs105
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs23
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs410
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs49
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs662
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs360
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs19
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs286
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs433
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets11
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs61
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs228
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs128
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs209
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs117
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs24
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs233
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs135
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs68
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs414
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs150
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs9
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs65
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs208
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs173
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs265
-rw-r--r--modules/mono/editor/bindings_generator.cpp2114
-rw-r--r--modules/mono/editor/bindings_generator.h271
-rw-r--r--modules/mono/editor/code_completion.cpp77
-rw-r--r--modules/mono/editor/code_completion.h58
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp394
-rw-r--r--modules/mono/editor/editor_internal_calls.h64
-rw-r--r--modules/mono/editor/godotsharp_export.cpp144
-rw-r--r--modules/mono/editor/godotsharp_export.h48
-rw-r--r--modules/mono/editor/hostfxr_resolver.cpp335
-rw-r--r--modules/mono/editor/hostfxr_resolver.h (renamed from modules/mono/utils/mono_reg_utils.h)87
-rw-r--r--modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs8
-rw-r--r--modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs12
-rw-r--r--modules/mono/editor/script_templates/Node/default.cs2
-rw-r--r--modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs16
-rw-r--r--modules/mono/editor/semver.cpp149
-rw-r--r--modules/mono/editor/semver.h106
-rw-r--r--modules/mono/glue/GodotSharp/.editorconfig8
-rw-r--r--modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml5
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs65
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs129
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs6
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj11
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs463
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj17
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/Main.cs286
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs64
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs198
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs647
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs23
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs11
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs6
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs711
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs252
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs87
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs89
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs25
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs1052
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs92
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs140
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs480
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs278
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs292
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs98
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs111
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs531
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs643
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs95
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs220
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs224
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs154
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs155
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs313
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs139
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs59
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs1097
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs96
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs598
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs514
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs103
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs34
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs625
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs414
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs146
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs164
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs142
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs110
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs1028
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs479
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs85
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs76
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs94
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs (renamed from modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs)9
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs73
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs801
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs120
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs262
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs397
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs933
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs232
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs224
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs236
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs178
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs908
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs640
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj81
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings4
-rw-r--r--modules/mono/glue/base_object_glue.cpp257
-rw-r--r--modules/mono/glue/callable_glue.cpp79
-rw-r--r--modules/mono/glue/collections_glue.cpp374
-rw-r--r--modules/mono/glue/gd_glue.cpp348
-rw-r--r--modules/mono/glue/glue_header.h91
-rw-r--r--modules/mono/glue/nodepath_glue.cpp102
-rw-r--r--modules/mono/glue/rid_glue.cpp64
-rw-r--r--modules/mono/glue/runtime_interop.cpp1503
-rw-r--r--modules/mono/glue/runtime_interop.h40
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp86
-rw-r--r--modules/mono/glue/string_glue.cpp85
-rw-r--r--modules/mono/glue/string_name_glue.cpp62
-rw-r--r--modules/mono/godotsharp_defs.h62
-rw-r--r--modules/mono/godotsharp_dirs.cpp266
-rw-r--r--modules/mono/godotsharp_dirs.h81
-rw-r--r--modules/mono/interop_types.h208
-rw-r--r--modules/mono/managed_callable.cpp123
-rw-r--r--modules/mono/managed_callable.h75
-rw-r--r--modules/mono/mono_gc_handle.cpp90
-rw-r--r--modules/mono/mono_gc_handle.h114
-rw-r--r--modules/mono/mono_gd/android_mono_config.h58
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp1510
-rw-r--r--modules/mono/mono_gd/gd_mono.h304
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp482
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h138
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp408
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h314
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp576
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h160
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp556
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h78
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp145
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp209
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h71
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp1824
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h605
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp296
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h97
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h320
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp202
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h80
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp677
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h205
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.cpp79
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h263
-rw-r--r--modules/mono/mono_gd/i_mono_class_member.h70
-rw-r--r--modules/mono/mono_gd/managed_type.cpp58
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp60
-rw-r--r--modules/mono/mono_gd/support/android_support.h58
-rw-r--r--modules/mono/mono_gd/support/ios_support.h58
-rw-r--r--modules/mono/mono_gd/support/ios_support.mm58
-rw-r--r--modules/mono/register_types.cpp58
-rw-r--r--modules/mono/register_types.h58
-rw-r--r--modules/mono/signal_awaiter_utils.cpp142
-rw-r--r--modules/mono/signal_awaiter_utils.h79
-rw-r--r--modules/mono/thirdparty/coreclr_delegates.h47
-rw-r--r--modules/mono/thirdparty/hostfxr.h323
-rw-r--r--modules/mono/utils/macos_utils.cpp58
-rw-r--r--modules/mono/utils/macos_utils.h62
-rw-r--r--modules/mono/utils/macros.h58
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp242
-rw-r--r--modules/mono/utils/path_utils.cpp91
-rw-r--r--modules/mono/utils/path_utils.h60
-rw-r--r--modules/mono/utils/string_utils.cpp87
-rw-r--r--modules/mono/utils/string_utils.h61
-rw-r--r--modules/msdfgen/SCsub2
-rw-r--r--modules/msdfgen/config.py3
-rw-r--r--modules/msdfgen/register_types.cpp58
-rw-r--r--modules/msdfgen/register_types.h58
-rw-r--r--modules/multiplayer/SCsub2
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml38
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml36
-rw-r--r--modules/multiplayer/doc_classes/SceneMultiplayer.xml64
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml32
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.cpp347
-rw-r--r--modules/multiplayer/editor/editor_network_profiler.h101
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.cpp175
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.h85
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp (renamed from modules/multiplayer/editor/replication_editor_plugin.cpp)253
-rw-r--r--modules/multiplayer/editor/replication_editor.h (renamed from modules/multiplayer/editor/replication_editor_plugin.h)120
-rw-r--r--modules/multiplayer/multiplayer_debugger.cpp333
-rw-r--r--modules/multiplayer/multiplayer_debugger.h134
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp124
-rw-r--r--modules/multiplayer/multiplayer_spawner.h71
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp118
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.h75
-rw-r--r--modules/multiplayer/register_types.cpp68
-rw-r--r--modules/multiplayer/register_types.h58
-rw-r--r--modules/multiplayer/scene_cache_interface.cpp72
-rw-r--r--modules/multiplayer/scene_cache_interface.h58
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp468
-rw-r--r--modules/multiplayer/scene_multiplayer.h141
-rw-r--r--modules/multiplayer/scene_replication_config.cpp74
-rw-r--r--modules/multiplayer/scene_replication_config.h58
-rw-r--r--modules/multiplayer/scene_replication_interface.cpp612
-rw-r--r--modules/multiplayer/scene_replication_interface.h140
-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.cpp144
-rw-r--r--modules/multiplayer/scene_rpc_interface.h63
-rw-r--r--modules/navigation/SCsub2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp62
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h60
-rw-r--r--modules/navigation/godot_navigation_server.cpp506
-rw-r--r--modules/navigation/godot_navigation_server.h135
-rw-r--r--modules/navigation/nav_base.h64
-rw-r--r--modules/navigation/nav_link.cpp60
-rw-r--r--modules/navigation/nav_link.h73
-rw-r--r--modules/navigation/nav_map.cpp353
-rw-r--r--modules/navigation/nav_map.h103
-rw-r--r--modules/navigation/nav_region.cpp66
-rw-r--r--modules/navigation/nav_region.h83
-rw-r--r--modules/navigation/nav_rid.h58
-rw-r--r--modules/navigation/nav_utils.h80
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp270
-rw-r--r--modules/navigation/navigation_mesh_generator.h66
-rw-r--r--modules/navigation/register_types.cpp58
-rw-r--r--modules/navigation/register_types.h58
-rw-r--r--modules/navigation/rvo_agent.cpp58
-rw-r--r--modules/navigation/rvo_agent.h58
-rw-r--r--modules/noise/config.py2
-rw-r--r--modules/noise/doc_classes/Noise.xml34
-rw-r--r--modules/noise/doc_classes/NoiseTexture2D.xml (renamed from modules/noise/doc_classes/NoiseTexture.xml)7
-rw-r--r--modules/noise/editor/noise_editor_plugin.cpp67
-rw-r--r--modules/noise/editor/noise_editor_plugin.h58
-rw-r--r--modules/noise/fastnoise_lite.cpp76
-rw-r--r--modules/noise/fastnoise_lite.h60
-rw-r--r--modules/noise/noise.cpp68
-rw-r--r--modules/noise/noise.h70
-rw-r--r--modules/noise/noise_texture_2d.cpp (renamed from modules/noise/noise_texture.cpp)228
-rw-r--r--modules/noise/noise_texture_2d.h (renamed from modules/noise/noise_texture.h)76
-rw-r--r--modules/noise/register_types.cpp63
-rw-r--r--modules/noise/register_types.h58
-rw-r--r--modules/noise/tests/test_fastnoise_lite.h637
-rw-r--r--modules/noise/tests/test_noise_texture_2d.h267
-rw-r--r--modules/ogg/doc_classes/OggPacketSequence.xml4
-rw-r--r--modules/ogg/ogg_packet_sequence.cpp76
-rw-r--r--modules/ogg/ogg_packet_sequence.h66
-rw-r--r--modules/ogg/register_types.cpp58
-rw-r--r--modules/ogg/register_types.h58
-rw-r--r--modules/openxr/SCsub75
-rw-r--r--modules/openxr/action_map/openxr_action.cpp72
-rw-r--r--modules/openxr/action_map/openxr_action.h58
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp159
-rw-r--r--modules/openxr/action_map/openxr_action_map.h62
-rw-r--r--modules/openxr/action_map/openxr_action_set.cpp68
-rw-r--r--modules/openxr/action_map/openxr_action_set.h58
-rw-r--r--modules/openxr/action_map/openxr_defs.cpp651
-rw-r--r--modules/openxr/action_map/openxr_defs.h124
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile.cpp84
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile.h61
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp397
-rw-r--r--modules/openxr/action_map/openxr_interaction_profile_meta_data.h118
-rw-r--r--modules/openxr/config.py7
-rw-r--r--modules/openxr/doc_classes/OpenXRAction.xml6
-rw-r--r--modules/openxr/doc_classes/OpenXRActionMap.xml16
-rw-r--r--modules/openxr/doc_classes/OpenXRActionSet.xml6
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml42
-rw-r--r--modules/openxr/doc_classes/OpenXRIPBinding.xml6
-rw-r--r--modules/openxr/doc_classes/OpenXRInteractionProfile.xml2
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml15
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp146
-rw-r--r--modules/openxr/editor/openxr_action_editor.h67
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp273
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h75
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.cpp191
-rw-r--r--modules/openxr/editor/openxr_action_set_editor.h69
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.cpp58
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h58
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.cpp148
-rw-r--r--modules/openxr/editor/openxr_interaction_profile_editor.h73
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.cpp64
-rw-r--r--modules/openxr/editor/openxr_select_action_dialog.h58
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp68
-rw-r--r--modules/openxr/editor/openxr_select_interaction_profile_dialog.h60
-rw-r--r--modules/openxr/extensions/openxr_android_extension.cpp118
-rw-r--r--modules/openxr/extensions/openxr_android_extension.h72
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp63
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_depth_extension.h54
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_provider.h66
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h143
-rw-r--r--modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp127
-rw-r--r--modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h72
-rw-r--r--modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp240
-rw-r--r--modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h247
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.cpp272
-rw-r--r--modules/openxr/extensions/openxr_hand_tracking_extension.h98
-rw-r--r--modules/openxr/extensions/openxr_htc_controller_extension.cpp129
-rw-r--r--modules/openxr/extensions/openxr_htc_controller_extension.h (renamed from modules/mono/glue/arguments_vector.h)110
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp208
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.h67
-rw-r--r--modules/openxr/extensions/openxr_huawei_controller_extension.cpp83
-rw-r--r--modules/openxr/extensions/openxr_huawei_controller_extension.h48
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.cpp416
-rw-r--r--modules/openxr/extensions/openxr_opengl_extension.h118
-rw-r--r--modules/openxr/extensions/openxr_palm_pose_extension.cpp58
-rw-r--r--modules/openxr/extensions/openxr_palm_pose_extension.h53
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.cpp98
-rw-r--r--modules/openxr/extensions/openxr_pico_controller_extension.h48
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.cpp248
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.h104
-rw-r--r--modules/openxr/extensions/openxr_wmr_controller_extension.cpp119
-rw-r--r--modules/openxr/extensions/openxr_wmr_controller_extension.h54
-rw-r--r--modules/openxr/openxr_api.cpp735
-rw-r--r--modules/openxr/openxr_api.h208
-rw-r--r--modules/openxr/openxr_interface.cpp250
-rw-r--r--modules/openxr/openxr_interface.h74
-rw-r--r--modules/openxr/openxr_util.cpp58
-rw-r--r--modules/openxr/openxr_util.h58
-rw-r--r--modules/openxr/register_types.cpp127
-rw-r--r--modules/openxr/register_types.h58
-rw-r--r--modules/openxr/scene/openxr_hand.cpp307
-rw-r--r--modules/openxr/scene/openxr_hand.h93
-rw-r--r--modules/openxr/util.h116
-rw-r--r--modules/raycast/SCsub23
-rw-r--r--modules/raycast/config.py21
-rw-r--r--modules/raycast/godot_update_embree.py24
-rw-r--r--modules/raycast/lightmap_raycaster_embree.cpp (renamed from modules/raycast/lightmap_raycaster.cpp)62
-rw-r--r--modules/raycast/lightmap_raycaster_embree.h (renamed from modules/raycast/lightmap_raycaster.h)65
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp58
-rw-r--r--modules/raycast/raycast_occlusion_cull.h58
-rw-r--r--modules/raycast/register_types.cpp62
-rw-r--r--modules/raycast/register_types.h63
-rw-r--r--modules/raycast/static_raycaster_embree.cpp (renamed from modules/raycast/static_raycaster.cpp)62
-rw-r--r--modules/raycast/static_raycaster_embree.h (renamed from modules/raycast/static_raycaster.h)65
-rw-r--r--modules/regex/SCsub2
-rw-r--r--modules/regex/doc_classes/RegEx.xml46
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml8
-rw-r--r--modules/regex/regex.cpp82
-rw-r--r--modules/regex/regex.h67
-rw-r--r--modules/regex/register_types.cpp58
-rw-r--r--modules/regex/register_types.h58
-rw-r--r--modules/regex/tests/test_regex.h60
-rw-r--r--modules/register_module_types.h68
-rw-r--r--modules/squish/image_decompress_squish.cpp60
-rw-r--r--modules/squish/image_decompress_squish.h58
-rw-r--r--modules/squish/register_types.cpp58
-rw-r--r--modules/squish/register_types.h58
-rw-r--r--modules/svg/SCsub43
-rw-r--r--modules/svg/image_loader_svg.cpp146
-rw-r--r--modules/svg/image_loader_svg.h72
-rw-r--r--modules/svg/register_types.cpp70
-rw-r--r--modules/svg/register_types.h58
-rw-r--r--modules/text_server_adv/SCsub69
-rw-r--r--modules/text_server_adv/config.py8
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct121
-rw-r--r--modules/text_server_adv/gdextension_build/text_server_adv.gdextension24
-rw-r--r--modules/text_server_adv/icu_data/icudata_stub.cpp58
-rw-r--r--modules/text_server_adv/register_types.cpp60
-rw-r--r--modules/text_server_adv/register_types.h58
-rw-r--r--modules/text_server_adv/script_iterator.cpp58
-rw-r--r--modules/text_server_adv/script_iterator.h58
-rw-r--r--modules/text_server_adv/text_server_adv.cpp1947
-rw-r--r--modules/text_server_adv/text_server_adv.h653
-rw-r--r--modules/text_server_adv/thorvg_bounds_iterator.cpp70
-rw-r--r--modules/text_server_adv/thorvg_bounds_iterator.h (renamed from modules/mono/mono_gd/managed_type.h)113
-rw-r--r--modules/text_server_adv/thorvg_svg_in_ot.cpp287
-rw-r--r--modules/text_server_adv/thorvg_svg_in_ot.h86
-rw-r--r--modules/text_server_fb/SCsub12
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct102
-rw-r--r--modules/text_server_fb/gdextension_build/text_server_fb.gdextension24
-rw-r--r--modules/text_server_fb/register_types.cpp60
-rw-r--r--modules/text_server_fb/register_types.h58
-rw-r--r--modules/text_server_fb/text_server_fb.cpp1443
-rw-r--r--modules/text_server_fb/text_server_fb.h638
-rw-r--r--modules/text_server_fb/thorvg_bounds_iterator.cpp70
-rw-r--r--modules/text_server_fb/thorvg_bounds_iterator.h58
-rw-r--r--modules/text_server_fb/thorvg_svg_in_ot.cpp287
-rw-r--r--modules/text_server_fb/thorvg_svg_in_ot.h86
-rw-r--r--modules/tga/SCsub2
-rw-r--r--modules/tga/image_loader_tga.cpp95
-rw-r--r--modules/tga/image_loader_tga.h62
-rw-r--r--modules/tga/register_types.cpp65
-rw-r--r--modules/tga/register_types.h58
-rw-r--r--modules/theora/SCsub7
-rw-r--r--modules/theora/config.py3
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml2
-rw-r--r--modules/theora/register_types.cpp58
-rw-r--r--modules/theora/register_types.h58
-rw-r--r--modules/theora/video_stream_theora.cpp147
-rw-r--r--modules/theora/video_stream_theora.h70
-rw-r--r--modules/tinyexr/config.py2
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp66
-rw-r--r--modules/tinyexr/image_loader_tinyexr.h60
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp58
-rw-r--r--modules/tinyexr/image_saver_tinyexr.h58
-rw-r--r--modules/tinyexr/register_types.cpp65
-rw-r--r--modules/tinyexr/register_types.h58
-rw-r--r--modules/upnp/doc_classes/UPNP.xml61
-rw-r--r--modules/upnp/doc_classes/UPNPDevice.xml18
-rw-r--r--modules/upnp/register_types.cpp58
-rw-r--r--modules/upnp/register_types.h58
-rw-r--r--modules/upnp/upnp.cpp60
-rw-r--r--modules/upnp/upnp.h58
-rw-r--r--modules/upnp/upnp_device.cpp58
-rw-r--r--modules/upnp/upnp_device.h58
-rw-r--r--modules/vhacd/register_types.cpp58
-rw-r--r--modules/vhacd/register_types.h58
-rw-r--r--modules/visual_script/SCsub11
-rw-r--r--modules/visual_script/config.py63
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml357
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml19
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml221
-rw-r--r--modules/visual_script/doc_classes/VisualScriptClassConstant.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComment.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComposeArray.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCondition.xml18
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstant.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstructor.xml35
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml166
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNodes.xml37
-rw-r--r--modules/visual_script/doc_classes/VisualScriptDeconstruct.xml16
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEmitSignal.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml16
-rw-r--r--modules/visual_script/doc_classes/VisualScriptExpression.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunction.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionCall.xml75
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionState.xml35
-rw-r--r--modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml16
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexGet.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexSet.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptInputAction.xml33
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIterator.xml18
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLists.xml77
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVar.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml25
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml49
-rw-r--r--modules/visual_script/doc_classes/VisualScriptNode.xml47
-rw-r--r--modules/visual_script/doc_classes/VisualScriptOperator.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPreload.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertyGet.xml48
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertySet.xml84
-rw-r--r--modules/visual_script/doc_classes/VisualScriptResourcePath.xml13
-rw-r--r--modules/visual_script/doc_classes/VisualScriptReturn.xml24
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneNode.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneTree.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelect.xml22
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelf.xml15
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSequence.xml22
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSubCall.xml11
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSwitch.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptTypeCast.xml19
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableGet.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableSet.xml21
-rw-r--r--modules/visual_script/doc_classes/VisualScriptWhile.xml17
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYield.xml30
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYieldSignal.xml36
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp4936
-rw-r--r--modules/visual_script/editor/visual_script_editor.h377
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp1276
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h229
-rw-r--r--modules/visual_script/icons/VisualScript.svg1
-rw-r--r--modules/visual_script/icons/VisualScriptInternal.svg1
-rw-r--r--modules/visual_script/register_types.cpp150
-rw-r--r--modules/visual_script/visual_script.cpp2502
-rw-r--r--modules/visual_script/visual_script.h627
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp1380
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h153
-rw-r--r--modules/visual_script/visual_script_expression.cpp1570
-rw-r--r--modules/visual_script/visual_script_expression.h284
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp880
-rw-r--r--modules/visual_script/visual_script_flow_control.h268
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp2444
-rw-r--r--modules/visual_script/visual_script_func_nodes.h363
-rw-r--r--modules/visual_script/visual_script_nodes.cpp4072
-rw-r--r--modules/visual_script/visual_script_nodes.h1092
-rw-r--r--modules/visual_script/visual_script_yield_nodes.cpp598
-rw-r--r--modules/visual_script/visual_script_yield_nodes.h147
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp103
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h70
-rw-r--r--modules/vorbis/config.py3
-rw-r--r--modules/vorbis/register_types.cpp58
-rw-r--r--modules/vorbis/register_types.h58
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp76
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h58
-rw-r--r--modules/webp/SCsub238
-rw-r--r--modules/webp/image_loader_webp.cpp60
-rw-r--r--modules/webp/image_loader_webp.h60
-rw-r--r--modules/webp/register_types.cpp69
-rw-r--r--modules/webp/register_types.h58
-rw-r--r--modules/webp/resource_saver_webp.cpp64
-rw-r--r--modules/webp/resource_saver_webp.h58
-rw-r--r--modules/webp/webp_common.cpp112
-rw-r--r--modules/webp/webp_common.h60
-rw-r--r--modules/webrtc/SCsub2
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannel.xml4
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml20
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml51
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml73
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml44
-rw-r--r--modules/webrtc/library_godot_webrtc.js254
-rw-r--r--modules/webrtc/register_types.cpp61
-rw-r--r--modules/webrtc/register_types.h58
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp58
-rw-r--r--modules/webrtc/webrtc_data_channel.h58
-rw-r--r--modules/webrtc/webrtc_data_channel_extension.cpp206
-rw-r--r--modules/webrtc/webrtc_data_channel_extension.h117
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp62
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h62
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp151
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h101
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp75
-rw-r--r--modules/webrtc/webrtc_peer_connection.h77
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp130
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h91
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp85
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h74
-rw-r--r--modules/websocket/SCsub4
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml94
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml70
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml155
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml127
-rw-r--r--modules/websocket/editor/editor_debugger_server_websocket.cpp120
-rw-r--r--modules/websocket/editor/editor_debugger_server_websocket.h67
-rw-r--r--modules/websocket/emws_client.cpp159
-rw-r--r--modules/websocket/emws_client.h71
-rw-r--r--modules/websocket/emws_peer.cpp240
-rw-r--r--modules/websocket/emws_peer.h110
-rw-r--r--modules/websocket/emws_server.cpp92
-rw-r--r--modules/websocket/emws_server.h64
-rw-r--r--modules/websocket/library_godot_websocket.js58
-rw-r--r--modules/websocket/packet_buffer.h116
-rw-r--r--modules/websocket/register_types.cpp96
-rw-r--r--modules/websocket/register_types.h58
-rw-r--r--modules/websocket/remote_debugger_peer_websocket.cpp104
-rw-r--r--modules/websocket/remote_debugger_peer_websocket.h66
-rw-r--r--modules/websocket/websocket_client.cpp141
-rw-r--r--modules/websocket/websocket_client.h75
-rw-r--r--modules/websocket/websocket_macros.h68
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp577
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h144
-rw-r--r--modules/websocket/websocket_peer.cpp165
-rw-r--r--modules/websocket/websocket_peer.h125
-rw-r--r--modules/websocket/websocket_server.cpp167
-rw-r--r--modules/websocket/websocket_server.h90
-rw-r--r--modules/websocket/wsl_client.cpp407
-rw-r--r--modules/websocket/wsl_client.h91
-rw-r--r--modules/websocket/wsl_peer.cpp913
-rw-r--r--modules/websocket/wsl_peer.h192
-rw-r--r--modules/websocket/wsl_server.cpp329
-rw-r--r--modules/websocket/wsl_server.h98
-rw-r--r--modules/webxr/SCsub2
-rw-r--r--modules/webxr/config.py2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml127
-rw-r--r--modules/webxr/godot_webxr.h100
-rw-r--r--modules/webxr/native/library_godot_webxr.js623
-rw-r--r--modules/webxr/native/webxr.externs.js682
-rw-r--r--modules/webxr/register_types.cpp66
-rw-r--r--modules/webxr/register_types.h58
-rw-r--r--modules/webxr/webxr_interface.cpp81
-rw-r--r--modules/webxr/webxr_interface.h72
-rw-r--r--modules/webxr/webxr_interface_js.cpp582
-rw-r--r--modules/webxr/webxr_interface_js.h219
-rw-r--r--modules/xatlas_unwrap/config.py2
-rw-r--r--modules/xatlas_unwrap/register_types.cpp58
-rw-r--r--modules/xatlas_unwrap/register_types.h58
-rw-r--r--modules/zip/SCsub9
-rw-r--r--modules/zip/config.py17
-rw-r--r--modules/zip/doc_classes/ZIPPacker.xml72
-rw-r--r--modules/zip/doc_classes/ZIPReader.xml52
-rw-r--r--modules/zip/register_types.cpp (renamed from modules/mono/mono_gd/gd_mono_internals.h)98
-rw-r--r--modules/zip/register_types.h (renamed from modules/mono/mono_gd/gd_mono_header.h)83
-rw-r--r--modules/zip/zip_packer.cpp107
-rw-r--r--modules/zip/zip_packer.h68
-rw-r--r--modules/zip/zip_reader.cpp123
-rw-r--r--modules/zip/zip_reader.h59
1223 files changed, 70612 insertions, 72819 deletions
diff --git a/modules/astcenc/SCsub b/modules/astcenc/SCsub
new file mode 100644
index 0000000000..0f04f2bc28
--- /dev/null
+++ b/modules/astcenc/SCsub
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_astcenc = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/astcenc/"
+thirdparty_sources = [
+ "astcenc_averages_and_directions.cpp",
+ "astcenc_block_sizes.cpp",
+ "astcenc_color_quantize.cpp",
+ "astcenc_color_unquantize.cpp",
+ "astcenc_compress_symbolic.cpp",
+ "astcenc_compute_variance.cpp",
+ "astcenc_decompress_symbolic.cpp",
+ "astcenc_diagnostic_trace.cpp",
+ "astcenc_entry.cpp",
+ "astcenc_find_best_partitioning.cpp",
+ "astcenc_ideal_endpoints_and_weights.cpp",
+ "astcenc_image.cpp",
+ "astcenc_integer_sequence.cpp",
+ "astcenc_mathlib.cpp",
+ "astcenc_mathlib_softfloat.cpp",
+ "astcenc_partition_tables.cpp",
+ "astcenc_percentile_tables.cpp",
+ "astcenc_pick_best_endpoint_format.cpp",
+ "astcenc_platform_isa_detection.cpp",
+ "astcenc_quantization.cpp",
+ "astcenc_symbolic_physical.cpp",
+ "astcenc_weight_align.cpp",
+ "astcenc_weight_quant_xfer_tables.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_astcenc.Prepend(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_astcenc.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_astcenc.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/astcenc/config.py b/modules/astcenc/config.py
new file mode 100644
index 0000000000..eb565b85b9
--- /dev/null
+++ b/modules/astcenc/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return env.editor_build
+
+
+def configure(env):
+ pass
diff --git a/modules/astcenc/image_compress_astcenc.cpp b/modules/astcenc/image_compress_astcenc.cpp
new file mode 100644
index 0000000000..ce10201343
--- /dev/null
+++ b/modules/astcenc/image_compress_astcenc.cpp
@@ -0,0 +1,251 @@
+/**************************************************************************/
+/* image_compress_astcenc.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_compress_astcenc.h"
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#include <astcenc.h>
+
+void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format) {
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+
+ // TODO: See how to handle lossy quality.
+
+ Image::Format img_format = r_img->get_format();
+ if (img_format >= Image::FORMAT_DXT1) {
+ return; // Do not compress, already compressed.
+ }
+
+ bool is_hdr = false;
+ if ((img_format >= Image::FORMAT_RH) && (img_format <= Image::FORMAT_RGBE9995)) {
+ is_hdr = true;
+ r_img->convert(Image::FORMAT_RGBAF);
+ } else {
+ r_img->convert(Image::FORMAT_RGBA8);
+ }
+
+ // Determine encoder output format from our enum.
+
+ Image::Format target_format = Image::FORMAT_RGBA8;
+ astcenc_profile profile = ASTCENC_PRF_LDR;
+ unsigned int block_x = 4;
+ unsigned int block_y = 4;
+
+ if (p_format == Image::ASTCFormat::ASTC_FORMAT_4x4) {
+ if (is_hdr) {
+ target_format = Image::FORMAT_ASTC_4x4_HDR;
+ profile = ASTCENC_PRF_HDR;
+ } else {
+ target_format = Image::FORMAT_ASTC_4x4;
+ }
+ } else if (p_format == Image::ASTCFormat::ASTC_FORMAT_8x8) {
+ if (is_hdr) {
+ target_format = Image::FORMAT_ASTC_8x8_HDR;
+ profile = ASTCENC_PRF_HDR;
+ } else {
+ target_format = Image::FORMAT_ASTC_8x8;
+ }
+ block_x = 8;
+ block_y = 8;
+ }
+
+ // Compress image data and (if required) mipmaps.
+
+ const bool mipmaps = r_img->has_mipmaps();
+ int width = r_img->get_width();
+ int height = r_img->get_height();
+
+ print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
+
+ // Initialize astcenc.
+
+ astcenc_config config;
+ config.block_x = block_x;
+ config.block_y = block_y;
+ config.profile = profile;
+ const float quality = ASTCENC_PRE_MEDIUM;
+
+ astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
+
+ // Context allocation.
+
+ astcenc_context *context;
+ const unsigned int thread_count = OS::get_singleton()->get_processor_count();
+
+ status = astcenc_context_alloc(&config, thread_count, &context);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
+
+ // Compress image.
+
+ Vector<uint8_t> image_data = r_img->get_data();
+ uint8_t *slices = image_data.ptrw();
+
+ astcenc_image image;
+ image.dim_x = width;
+ image.dim_y = height;
+ image.dim_z = 1;
+ image.data_type = ASTCENC_TYPE_U8;
+ if (is_hdr) {
+ image.data_type = ASTCENC_TYPE_F32;
+ }
+ image.data = reinterpret_cast<void **>(&slices);
+
+ // Compute the number of ASTC blocks in each dimension.
+ unsigned int block_count_x = (width + block_x - 1) / block_x;
+ unsigned int block_count_y = (height + block_y - 1) / block_y;
+ size_t comp_len = block_count_x * block_count_y * 16;
+
+ Vector<uint8_t> compressed_data;
+ compressed_data.resize(comp_len);
+ compressed_data.fill(0);
+
+ const astcenc_swizzle swizzle = {
+ ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
+ };
+
+ status = astcenc_compress_image(context, &image, &swizzle, compressed_data.ptrw(), comp_len, 0);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status)));
+
+ // Replace original image with compressed one.
+
+ r_img->set_data(width, height, mipmaps, target_format, compressed_data);
+
+ print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
+}
+
+void _decompress_astc(Image *r_img) {
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+
+ // Determine decompression parameters from image format.
+
+ Image::Format img_format = r_img->get_format();
+ bool is_hdr = false;
+ unsigned int block_x = 0;
+ unsigned int block_y = 0;
+ if (img_format == Image::FORMAT_ASTC_4x4) {
+ block_x = 4;
+ block_y = 4;
+ is_hdr = false;
+ } else if (img_format == Image::FORMAT_ASTC_4x4_HDR) {
+ block_x = 4;
+ block_y = 4;
+ is_hdr = true;
+ } else if (img_format == Image::FORMAT_ASTC_8x8) {
+ block_x = 8;
+ block_y = 8;
+ is_hdr = false;
+ } else if (img_format == Image::FORMAT_ASTC_8x8_HDR) {
+ block_x = 8;
+ block_y = 8;
+ is_hdr = true;
+ } else {
+ ERR_FAIL_MSG("astcenc: Cannot decompress Image with a non-ASTC format.");
+ }
+
+ // Initialize astcenc.
+
+ astcenc_profile profile = ASTCENC_PRF_LDR;
+ if (is_hdr) {
+ profile = ASTCENC_PRF_HDR;
+ }
+ astcenc_config config;
+ const float quality = ASTCENC_PRE_MEDIUM;
+
+ astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
+
+ // Context allocation.
+
+ astcenc_context *context = nullptr;
+ const unsigned int thread_count = OS::get_singleton()->get_processor_count();
+
+ status = astcenc_context_alloc(&config, thread_count, &context);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
+
+ // Decompress image.
+
+ const bool mipmaps = r_img->has_mipmaps();
+ int width = r_img->get_width();
+ int height = r_img->get_height();
+
+ astcenc_image image;
+ image.dim_x = width;
+ image.dim_y = height;
+ image.dim_z = 1;
+ image.data_type = ASTCENC_TYPE_U8;
+ Image::Format target_format = Image::FORMAT_RGBA8;
+ if (is_hdr) {
+ target_format = Image::FORMAT_RGBAF;
+ image.data_type = ASTCENC_TYPE_F32;
+ }
+
+ Vector<uint8_t> image_data = r_img->get_data();
+
+ Vector<uint8_t> new_image_data;
+ new_image_data.resize(Image::get_image_data_size(width, height, target_format, false));
+ new_image_data.fill(0);
+ uint8_t *slices = new_image_data.ptrw();
+ image.data = reinterpret_cast<void **>(&slices);
+
+ const astcenc_swizzle swizzle = {
+ ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
+ };
+
+ status = astcenc_decompress_image(context, image_data.ptr(), image_data.size(), &image, &swizzle, 0);
+ ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
+ vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status)));
+ ERR_FAIL_COND_MSG(image.dim_z > 1,
+ "astcenc: ASTC decompression failed because this is a 3D texture, which is not supported.");
+
+ // Replace original image with compressed one.
+
+ Image::Format image_format = Image::FORMAT_RGBA8;
+ if (image.data_type == ASTCENC_TYPE_F32) {
+ image_format = Image::FORMAT_RGBAF;
+ } else if (image.data_type == ASTCENC_TYPE_U8) {
+ image_format = Image::FORMAT_RGBA8;
+ } else if (image.data_type == ASTCENC_TYPE_F16) {
+ image_format = Image::FORMAT_RGBAH;
+ } else {
+ ERR_FAIL_MSG("astcenc: ASTC decompression failed with an unknown format.");
+ }
+
+ r_img->set_data(image.dim_x, image.dim_y, mipmaps, image_format, new_image_data);
+
+ print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
+}
diff --git a/modules/astcenc/image_compress_astcenc.h b/modules/astcenc/image_compress_astcenc.h
new file mode 100644
index 0000000000..a197a91e0d
--- /dev/null
+++ b/modules/astcenc/image_compress_astcenc.h
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* image_compress_astcenc.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_COMPRESS_ASTCENC_H
+#define IMAGE_COMPRESS_ASTCENC_H
+
+#include "core/io/image.h"
+
+void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format);
+void _decompress_astc(Image *r_img);
+
+#endif // IMAGE_COMPRESS_ASTCENC_H
diff --git a/modules/astcenc/register_types.cpp b/modules/astcenc/register_types.cpp
new file mode 100644
index 0000000000..0bb1c3432f
--- /dev/null
+++ b/modules/astcenc/register_types.cpp
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_compress_astcenc.h"
+
+void initialize_astcenc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
+ Image::_image_compress_astc_func = _compress_astc;
+ Image::_image_decompress_astc = _decompress_astc;
+}
+
+void uninitialize_astcenc_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/visual_script/register_types.h b/modules/astcenc/register_types.h
index 90f84de11c..636da9ff8b 100644
--- a/modules/visual_script/register_types.h
+++ b/modules/astcenc/register_types.h
@@ -1,39 +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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 VISUAL_SCRIPT_REGISTER_TYPES_H
-#define VISUAL_SCRIPT_REGISTER_TYPES_H
+#ifndef ASTCENC_REGISTER_TYPES_H
+#define ASTCENC_REGISTER_TYPES_H
#include "modules/register_module_types.h"
-void initialize_visual_script_module(ModuleInitializationLevel p_level);
-void uninitialize_visual_script_module(ModuleInitializationLevel p_level);
+void initialize_astcenc_module(ModuleInitializationLevel p_level);
+void uninitialize_astcenc_module(ModuleInitializationLevel p_level);
-#endif // VISUAL_SCRIPT_REGISTER_TYPES_H
+#endif // ASTCENC_REGISTER_TYPES_H
diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub
index a44a7f0db3..161b0f3814 100644
--- a/modules/basis_universal/SCsub
+++ b/modules/basis_universal/SCsub
@@ -40,12 +40,12 @@ if not env.msvc:
else:
env_basisu.Prepend(CPPPATH=[thirdparty_dir])
-if env["target"] == "debug":
+if env.dev_build:
env_basisu.Append(CPPDEFINES=[("BASISU_DEVEL_MESSAGES", 1), ("BASISD_ENABLE_DEBUG_FLAGS", 1)])
env_thirdparty = env_basisu.Clone()
env_thirdparty.disable_warnings()
-if env["tools"]:
+if env.editor_build:
env_thirdparty.Append(CPPDEFINES=["BASISU_NO_IMG_LOADERS"])
env_thirdparty.add_source_files(thirdparty_obj, encoder_sources)
env_thirdparty.add_source_files(thirdparty_obj, transcoder_sources)
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index e80d453df7..7251511460 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -253,7 +253,7 @@ static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size
};
image.instantiate();
- image->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata);
+ image->set_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata);
return image;
}
@@ -277,6 +277,7 @@ void initialize_basis_universal_module(ModuleInitializationLevel p_level) {
basisu_encoder_init();
Image::basis_universal_packer = basis_universal_packer;
#endif
+ basist::basisu_transcoder_init();
Image::basis_universal_unpacker = basis_universal_unpacker;
Image::basis_universal_unpacker_ptr = basis_universal_unpacker_ptr;
}
diff --git a/modules/basis_universal/register_types.h b/modules/basis_universal/register_types.h
index 68d5dd64f3..84607f1564 100644
--- a/modules/basis_universal/register_types.h
+++ b/modules/basis_universal/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 BASIS_UNIVERSAL_REGISTER_TYPES_H
#define BASIS_UNIVERSAL_REGISTER_TYPES_H
diff --git a/modules/bmp/SCsub b/modules/bmp/SCsub
index 4f3405ff28..9d317887c3 100644
--- a/modules/bmp/SCsub
+++ b/modules/bmp/SCsub
@@ -5,5 +5,5 @@ Import("env_modules")
env_bmp = env_modules.Clone()
-# Godot's own source files
+# Godot source files
env_bmp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index 8813c3827a..5b451fbf6b 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_bmp.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_bmp.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_bmp.h"
@@ -58,6 +58,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
ERR_FAIL_COND_V_MSG(height % 8 != 0, ERR_UNAVAILABLE,
vformat("1-bpp BMP images must have a height that is a multiple of 8, but the imported BMP is %d pixels tall.", int(height)));
+ } else if (bits_per_pixel == 2) {
+ // Requires bit unpacking...
+ ERR_FAIL_COND_V_MSG(width % 4 != 0, ERR_UNAVAILABLE,
+ vformat("2-bpp BMP images must have a width that is a multiple of 4, but the imported BMP is %d pixels wide.", int(width)));
+ ERR_FAIL_COND_V_MSG(height % 4 != 0, ERR_UNAVAILABLE,
+ vformat("2-bpp BMP images must have a height that is a multiple of 4, but the imported BMP is %d pixels tall.", int(height)));
+
} else if (bits_per_pixel == 4) {
// Requires bit unpacking...
ERR_FAIL_COND_V_MSG(width % 2 != 0, ERR_UNAVAILABLE,
@@ -88,7 +95,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
const uint32_t line_width = (width_bytes + 3) & ~3;
// The actual data traversal is determined by
- // the data width in case of 8/4/1 bit images
+ // the data width in case of 8/4/2/1 bit images
const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes;
const uint8_t *line = p_buffer + (line_width * (height - 1));
const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset;
@@ -114,6 +121,17 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
index += 8;
line_ptr += 1;
} break;
+ case 2: {
+ uint8_t color_index = *line_ptr;
+
+ write_buffer[index + 0] = (color_index >> 6) & 3;
+ write_buffer[index + 1] = (color_index >> 4) & 3;
+ write_buffer[index + 2] = (color_index >> 2) & 3;
+ write_buffer[index + 3] = color_index & 3;
+
+ index += 4;
+ line_ptr += 1;
+ } break;
case 4: {
uint8_t color_index = *line_ptr;
@@ -156,7 +174,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
if (p_color_buffer == nullptr || color_table_size == 0) { // regular pixels
- p_image->create(width, height, false, Image::FORMAT_RGBA8, data);
+ p_image->set_data(width, height, false, Image::FORMAT_RGBA8, data);
} else { // data is in indexed format, extend it
@@ -194,13 +212,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
dest += 4;
}
- p_image->create(width, height, false, Image::FORMAT_RGBA8, extended_data);
+ p_image->set_data(width, height, false, Image::FORMAT_RGBA8, extended_data);
}
}
return err;
}
-Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderBMP::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
bmp_header_s bmp_header;
Error err = ERR_INVALID_DATA;
diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h
index 63dee0a969..08b9115c87 100644
--- a/modules/bmp/image_loader_bmp.h
+++ b/modules/bmp/image_loader_bmp.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_bmp.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_bmp.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_BMP_H
#define IMAGE_LOADER_BMP_H
@@ -83,7 +83,7 @@ protected:
const bmp_header_s &p_header);
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderBMP();
};
diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp
index 7c4a2085b2..8293a9a11a 100644
--- a/modules/bmp/register_types.cpp
+++ b/modules/bmp/register_types.cpp
@@ -1,45 +1,45 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_bmp.h"
-static ImageLoaderBMP *image_loader_bmp = nullptr;
+static Ref<ImageLoaderBMP> image_loader_bmp;
void initialize_bmp_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_bmp = memnew(ImageLoaderBMP);
+ image_loader_bmp.instantiate();
ImageLoader::add_image_format_loader(image_loader_bmp);
}
@@ -48,5 +48,6 @@ void uninitialize_bmp_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_bmp);
+ ImageLoader::remove_image_format_loader(image_loader_bmp);
+ image_loader_bmp.unref();
}
diff --git a/modules/bmp/register_types.h b/modules/bmp/register_types.h
index 45c8499c58..38258bf7d9 100644
--- a/modules/bmp/register_types.h
+++ b/modules/bmp/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 BMP_REGISTER_TYPES_H
#define BMP_REGISTER_TYPES_H
diff --git a/modules/camera/camera_macos.h b/modules/camera/camera_macos.h
index 903eda51bf..9d09d4bfc3 100644
--- a/modules/camera/camera_macos.h
+++ b/modules/camera/camera_macos.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* camera_macos.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* camera_macos.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CAMERA_MACOS_H
#define CAMERA_MACOS_H
diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_macos.mm
index 0b9696a3e9..eca8adbfcf 100644
--- a/modules/camera/camera_macos.mm
+++ b/modules/camera/camera_macos.mm
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* camera_macos.mm */
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* camera_macos.mm */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
///@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!
@@ -158,7 +158,7 @@
memcpy(w, dataY, new_width * new_height);
img[0].instantiate();
- img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
+ img[0]->set_data(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
}
{
@@ -177,7 +177,7 @@
///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion
img[1].instantiate();
- img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
+ img[1]->set_data(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
}
// set our texture...
diff --git a/modules/camera/camera_win.cpp b/modules/camera/camera_win.cpp
index ab7cfec01a..755642270e 100644
--- a/modules/camera/camera_win.cpp
+++ b/modules/camera/camera_win.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* camera_win.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* camera_win.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "camera_win.h"
diff --git a/modules/camera/camera_win.h b/modules/camera/camera_win.h
index ebfc117190..8817e2decd 100644
--- a/modules/camera/camera_win.h
+++ b/modules/camera/camera_win.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* camera_win.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* camera_win.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CAMERA_WIN_H
#define CAMERA_WIN_H
diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp
index 40e2224d6b..feee6769f8 100644
--- a/modules/camera/register_types.cpp
+++ b/modules/camera/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h
index 4ac4426588..c068f83fac 100644
--- a/modules/camera/register_types.h
+++ b/modules/camera/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CAMERA_REGISTER_TYPES_H
#define CAMERA_REGISTER_TYPES_H
diff --git a/modules/csg/SCsub b/modules/csg/SCsub
index c7307ddefd..1cf9974fc1 100644
--- a/modules/csg/SCsub
+++ b/modules/csg/SCsub
@@ -3,10 +3,9 @@
Import("env")
Import("env_modules")
-# Godot's own source files
env_csg = env_modules.Clone()
-# Godot's own source files
+# Godot source files
env_csg.add_source_files(env.modules_sources, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_csg.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 93533e1690..dee95d1ac5 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csg.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "csg.h"
@@ -729,7 +729,7 @@ void CSGBrushOperation::MeshMerge::mark_inside_faces() {
}
}
-void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[], const Vector2 p_uvs[], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
+void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[3], const Vector2 p_uvs[3], bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
int indices[3];
for (int i = 0; i < 3; i++) {
VertexKey vk;
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 738e3d68ea..1513a01f9e 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csg.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CSG_H
#define CSG_H
@@ -39,7 +39,6 @@
#include "core/object/ref_counted.h"
#include "core/templates/list.h"
#include "core/templates/oa_hash_map.h"
-#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "scene/resources/material.h"
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 56be4e65f0..9cc3d0413d 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csg_shape.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg_shape.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "csg_shape.h"
@@ -53,6 +53,7 @@ void CSGShape3D::set_use_collision(bool p_enable) {
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
set_collision_layer(collision_layer);
set_collision_mask(collision_mask);
+ set_collision_priority(collision_priority);
_make_dirty(); //force update
} else {
PhysicsServer3D::get_singleton()->free(root_collision_instance);
@@ -91,13 +92,13 @@ uint32_t CSGShape3D::get_collision_mask() const {
void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
- uint32_t collision_layer = get_collision_layer();
+ uint32_t layer = get_collision_layer();
if (p_value) {
- collision_layer |= 1 << (p_layer_number - 1);
+ layer |= 1 << (p_layer_number - 1);
} else {
- collision_layer &= ~(1 << (p_layer_number - 1));
+ layer &= ~(1 << (p_layer_number - 1));
}
- set_collision_layer(collision_layer);
+ set_collision_layer(layer);
}
bool CSGShape3D::get_collision_layer_value(int p_layer_number) const {
@@ -124,6 +125,17 @@ bool CSGShape3D::get_collision_mask_value(int p_layer_number) const {
return get_collision_mask() & (1 << (p_layer_number - 1));
}
+void CSGShape3D::set_collision_priority(real_t p_priority) {
+ collision_priority = p_priority;
+ if (root_collision_instance.is_valid()) {
+ PhysicsServer3D::get_singleton()->body_set_collision_priority(root_collision_instance, p_priority);
+ }
+}
+
+real_t CSGShape3D::get_collision_priority() const {
+ return collision_priority;
+}
+
bool CSGShape3D::is_root_shape() const {
return !parent_shape;
}
@@ -545,6 +557,7 @@ void CSGShape3D::_notification(int p_what) {
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(root_collision_instance, get_instance_id());
set_collision_layer(collision_layer);
set_collision_mask(collision_mask);
+ set_collision_priority(collision_priority);
_update_collision_faces();
}
} break;
@@ -584,15 +597,14 @@ bool CSGShape3D::is_calculating_tangents() const {
return calculate_tangents;
}
-void CSGShape3D::_validate_property(PropertyInfo &property) const {
- bool is_collision_prefixed = property.name.begins_with("collision_");
- if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
+void CSGShape3D::_validate_property(PropertyInfo &p_property) const {
+ bool is_collision_prefixed = p_property.name.begins_with("collision_");
+ if ((is_collision_prefixed || p_property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
//hide collision if not root
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
} else if (is_collision_prefixed && !bool(get("use_collision"))) {
- property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
- GeometryInstance3D::_validate_property(property);
}
Array CSGShape3D::get_meshes() const {
@@ -632,6 +644,9 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value);
ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value);
+ ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &CSGShape3D::set_collision_priority);
+ ClassDB::bind_method(D_METHOD("get_collision_priority"), &CSGShape3D::get_collision_priority);
+
ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents);
ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents);
@@ -645,6 +660,7 @@ void CSGShape3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority");
BIND_ENUM_CONSTANT(OPERATION_UNION);
BIND_ENUM_CONSTANT(OPERATION_INTERSECTION);
@@ -674,7 +690,7 @@ CSGCombiner3D::CSGCombiner3D() {
/////////////////////
CSGBrush *CSGPrimitive3D::_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) {
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
Vector<bool> invert;
invert.resize(p_vertices.size() / 3);
@@ -685,9 +701,9 @@ CSGBrush *CSGPrimitive3D::_create_brush_from_arrays(const Vector<Vector3> &p_ver
w[i] = flip_faces;
}
}
- brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
+ new_brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
- return brush;
+ return new_brush;
}
void CSGPrimitive3D::_bind_methods() {
@@ -726,7 +742,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
Vector<bool> smooth;
Vector<Ref<Material>> materials;
Vector<Vector2> uvs;
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
@@ -760,8 +776,8 @@ CSGBrush *CSGMesh3D::_build_brush() {
}
Ref<Material> mat;
- if (material.is_valid()) {
- mat = material;
+ if (base_material.is_valid()) {
+ mat = base_material;
} else {
mat = mesh->surface_get_material(i);
}
@@ -917,12 +933,12 @@ Ref<Mesh> CSGMesh3D::get_mesh() {
CSGBrush *CSGSphere3D::_build_brush() {
// set our bounding box
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
int face_count = rings * radial_segments * 2 - radial_segments * 2;
bool invert_val = get_flip_faces();
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
Vector<Vector3> faces;
Vector<Vector2> uvs;
@@ -1003,7 +1019,7 @@ CSGBrush *CSGSphere3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
@@ -1020,7 +1036,7 @@ CSGBrush *CSGSphere3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
@@ -1032,9 +1048,9 @@ CSGBrush *CSGSphere3D::_build_brush() {
}
}
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
+ new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
- return brush;
+ return new_brush;
}
void CSGSphere3D::_bind_methods() {
@@ -1121,12 +1137,12 @@ CSGSphere3D::CSGSphere3D() {
CSGBrush *CSGBox3D::_build_brush() {
// set our bounding box
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
int face_count = 12; //it's a cube..
bool invert_val = get_flip_faces();
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
Vector<Vector3> faces;
Vector<Vector2> uvs;
@@ -1188,7 +1204,7 @@ CSGBrush *CSGBox3D::_build_brush() {
smoothw[face] = false;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
//face 2
@@ -1202,7 +1218,7 @@ CSGBrush *CSGBox3D::_build_brush() {
smoothw[face] = false;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
@@ -1213,9 +1229,9 @@ CSGBrush *CSGBox3D::_build_brush() {
}
}
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
+ new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
- return brush;
+ return new_brush;
}
void CSGBox3D::_bind_methods() {
@@ -1254,12 +1270,12 @@ Ref<Material> CSGBox3D::get_material() const {
CSGBrush *CSGCylinder3D::_build_brush() {
// set our bounding box
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
bool invert_val = get_flip_faces();
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
Vector<Vector3> faces;
Vector<Vector2> uvs;
@@ -1296,14 +1312,14 @@ CSGBrush *CSGCylinder3D::_build_brush() {
float ang = inc * Math_TAU;
float ang_n = inc_n * Math_TAU;
- Vector3 base(Math::cos(ang), 0, Math::sin(ang));
- Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
+ Vector3 face_base(Math::cos(ang), 0, Math::sin(ang));
+ Vector3 face_base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
Vector3 face_points[4] = {
- base + Vector3(0, -1, 0),
- base_n + Vector3(0, -1, 0),
- base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
- base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ face_base + Vector3(0, -1, 0),
+ face_base_n + Vector3(0, -1, 0),
+ face_base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ face_base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
};
Vector2 u[4] = {
@@ -1324,7 +1340,7 @@ CSGBrush *CSGCylinder3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
@@ -1340,7 +1356,7 @@ CSGBrush *CSGCylinder3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
@@ -1355,7 +1371,7 @@ CSGBrush *CSGCylinder3D::_build_brush() {
smoothw[face] = false;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
if (!cone) {
@@ -1370,7 +1386,7 @@ CSGBrush *CSGCylinder3D::_build_brush() {
smoothw[face] = false;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
}
@@ -1381,9 +1397,9 @@ CSGBrush *CSGCylinder3D::_build_brush() {
}
}
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
+ new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
- return brush;
+ return new_brush;
}
void CSGCylinder3D::_bind_methods() {
@@ -1499,12 +1515,12 @@ CSGBrush *CSGTorus3D::_build_brush() {
float radius = (max_radius - min_radius) * 0.5;
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
int face_count = ring_sides * sides * 2;
bool invert_val = get_flip_faces();
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
Vector<Vector3> faces;
Vector<Vector2> uvs;
@@ -1580,7 +1596,7 @@ CSGBrush *CSGTorus3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
@@ -1595,7 +1611,7 @@ CSGBrush *CSGTorus3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = invert_val;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
}
@@ -1606,9 +1622,9 @@ CSGBrush *CSGTorus3D::_build_brush() {
}
}
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
+ new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
- return brush;
+ return new_brush;
}
void CSGTorus3D::_bind_methods() {
@@ -1710,10 +1726,10 @@ CSGTorus3D::CSGTorus3D() {
///////////////
CSGBrush *CSGPolygon3D::_build_brush() {
- CSGBrush *brush = memnew(CSGBrush);
+ CSGBrush *new_brush = memnew(CSGBrush);
if (polygon.size() < 3) {
- return brush;
+ return new_brush;
}
// Triangulate polygon shape.
@@ -1723,7 +1739,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
int shape_sides = shape_polygon.size();
Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
- ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
+ ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, new_brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
// Get polygon enclosing Rect2.
Rect2 shape_rect(shape_polygon[0], Vector2());
@@ -1748,12 +1764,12 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
if (!path) {
- return brush;
+ return new_brush;
}
curve = path->get_curve();
if (curve.is_null() || curve->get_point_count() < 2) {
- return brush;
+ return new_brush;
}
}
@@ -1790,7 +1806,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
int face_count = extrusions * extrusion_face_count + end_count * shape_face_count;
// Initialize variables used to create the mesh.
- Ref<Material> material = get_material();
+ Ref<Material> base_material = get_material();
Vector<Vector3> faces;
Vector<Vector2> uvs;
@@ -1822,7 +1838,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
u_step *= curve_length / path_u_distance;
}
double v_step = 1.0 / shape_sides;
- double spin_step = Math::deg2rad(spin_degrees / spin_sides);
+ double spin_step = Math::deg_to_rad(spin_degrees / spin_sides);
double extrusion_step = 1.0 / extrusions;
if (mode == MODE_PATH) {
if (path_joined) {
@@ -1836,13 +1852,13 @@ CSGBrush *CSGPolygon3D::_build_brush() {
base_xform = path->get_global_transform();
}
- Vector3 current_point = curve->interpolate_baked(0);
- Vector3 next_point = curve->interpolate_baked(extrusion_step);
+ Vector3 current_point = curve->sample_baked(0);
+ Vector3 next_point = curve->sample_baked(extrusion_step);
Vector3 current_up = Vector3(0, 1, 0);
Vector3 direction = next_point - current_point;
if (path_joined) {
- Vector3 last_point = curve->interpolate_baked(curve->get_baked_length());
+ Vector3 last_point = curve->sample_baked(curve->get_baked_length());
direction = next_point - last_point;
}
@@ -1853,7 +1869,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
case PATH_ROTATION_PATH:
break;
case PATH_ROTATION_PATH_FOLLOW:
- current_up = curve->interpolate_baked_up_vector(0);
+ current_up = curve->sample_baked_up_vector(0);
break;
}
@@ -1880,13 +1896,13 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
smoothw[face] = false;
- materialsw[face] = material;
+ materialsw[face] = base_material;
invertw[face] = flip_faces;
face++;
}
}
- real_t angle_simplify_dot = Math::cos(Math::deg2rad(path_simplify_angle));
+ real_t angle_simplify_dot = Math::cos(Math::deg_to_rad(path_simplify_angle));
Vector3 previous_simplify_dir = Vector3(0, 0, 0);
int faces_combined = 0;
@@ -1915,9 +1931,9 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
}
- Vector3 previous_point = curve->interpolate_baked(previous_offset);
- Vector3 current_point = curve->interpolate_baked(current_offset);
- Vector3 next_point = curve->interpolate_baked(next_offset);
+ Vector3 previous_point = curve->sample_baked(previous_offset);
+ Vector3 current_point = curve->sample_baked(current_offset);
+ Vector3 next_point = curve->sample_baked(next_offset);
Vector3 current_up = Vector3(0, 1, 0);
Vector3 direction = next_point - previous_point;
Vector3 current_dir = (current_point - previous_point).normalized();
@@ -1940,7 +1956,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
case PATH_ROTATION_PATH:
break;
case PATH_ROTATION_PATH_FOLLOW:
- current_up = curve->interpolate_baked_up_vector(current_offset);
+ current_up = curve->sample_baked_up_vector(current_offset);
break;
}
@@ -1987,7 +2003,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = flip_faces;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
@@ -2002,7 +2018,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
smoothw[face] = smooth_faces;
invertw[face] = flip_faces;
- materialsw[face] = material;
+ materialsw[face] = base_material;
face++;
}
@@ -2025,14 +2041,14 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
smoothw[face] = false;
- materialsw[face] = material;
+ materialsw[face] = base_material;
invertw[face] = flip_faces;
face++;
}
}
face_count -= faces_removed;
- ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
+ ERR_FAIL_COND_V_MSG(face != face_count, new_brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
}
if (faces_removed > 0) {
@@ -2043,9 +2059,9 @@ CSGBrush *CSGPolygon3D::_build_brush() {
invert.resize(face_count);
}
- brush->build_from_faces(faces, uvs, smooth, materials, invert);
+ new_brush->build_from_faces(faces, uvs, smooth, materials, invert);
- return brush;
+ return new_brush;
}
void CSGPolygon3D::_notification(int p_what) {
@@ -2058,18 +2074,16 @@ void CSGPolygon3D::_notification(int p_what) {
}
}
-void CSGPolygon3D::_validate_property(PropertyInfo &property) const {
- if (property.name.begins_with("spin") && mode != MODE_SPIN) {
- property.usage = PROPERTY_USAGE_NONE;
+void CSGPolygon3D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name.begins_with("spin") && mode != MODE_SPIN) {
+ p_property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name.begins_with("path") && mode != MODE_PATH) {
- property.usage = PROPERTY_USAGE_NONE;
+ if (p_property.name.begins_with("path") && mode != MODE_PATH) {
+ p_property.usage = PROPERTY_USAGE_NONE;
}
- if (property.name == "depth" && mode != MODE_DEPTH) {
- property.usage = PROPERTY_USAGE_NONE;
+ if (p_property.name == "depth" && mode != MODE_DEPTH) {
+ p_property.usage = PROPERTY_USAGE_NONE;
}
-
- CSGShape3D::_validate_property(property);
}
void CSGPolygon3D::_path_changed() {
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 0b49dc4609..9012c37679 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csg_shape.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CSG_SHAPE_H
#define CSG_SHAPE_H
@@ -65,6 +65,7 @@ private:
bool use_collision = false;
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
+ real_t collision_priority = 1.0;
Ref<ConcavePolygonShape3D> root_collision_shape;
RID root_collision_instance;
@@ -117,7 +118,7 @@ protected:
friend class CSGCombiner3D;
CSGBrush *_get_brush();
- virtual void _validate_property(PropertyInfo &property) const override;
+ void _validate_property(PropertyInfo &p_property) const;
public:
Array get_meshes() const;
@@ -144,6 +145,9 @@ public:
void set_collision_mask_value(int p_layer_number, bool p_value);
bool get_collision_mask_value(int p_layer_number) const;
+ void set_collision_priority(real_t p_priority);
+ real_t get_collision_priority() const;
+
void set_snap(float p_snap);
float get_snap() const;
@@ -383,7 +387,7 @@ private:
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
+ void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
public:
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index f1cd28e00f..06f8f5a403 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -13,14 +13,14 @@
<methods>
<method name="get_collision_layer_value" qualifiers="const">
<return type="bool" />
- <argument index="0" name="layer_number" type="int" />
+ <param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
- <argument index="0" name="layer_number" type="int" />
+ <param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
@@ -39,16 +39,16 @@
</method>
<method name="set_collision_layer_value">
<return type="void" />
- <argument index="0" name="layer_number" type="int" />
- <argument index="1" name="value" type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
<description>
Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
<return type="void" />
- <argument index="0" name="layer_number" type="int" />
- <argument index="1" name="value" type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
<description>
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>
@@ -64,7 +64,10 @@
A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this CSG shape scans for collisions. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
+ The physics layers this CSG shape scans for collisions. Only effective if [member use_collision] is [code]true[/code]. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
+ </member>
+ <member name="collision_priority" type="float" setter="set_collision_priority" getter="get_collision_priority" default="1.0">
+ The priority used to solve colliding when occurring penetration. Only effective if [member use_collision] is [code]true[/code]. The higher the priority is, the lower the penetration into the object will be. This can for example be used to prevent the player from breaking through the boundaries of a level.
</member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
@@ -73,7 +76,7 @@
Snap makes the mesh snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust.
</member>
<member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false">
- Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden.
+ Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. See also [member collision_mask] and [member collision_priority].
</member>
</members>
<constants>
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 6442ff71fc..2c533cb36d 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -1,38 +1,40 @@
-/*************************************************************************/
-/* csg_gizmos.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg_gizmos.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "csg_gizmos.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
@@ -55,7 +57,7 @@ CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() {
}
String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
return "Radius";
@@ -77,7 +79,7 @@ String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo,
}
Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
@@ -103,7 +105,7 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo
}
void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
Transform3D gt = cs->get_global_transform();
//gt.orthonormalize();
@@ -206,7 +208,7 @@ void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_i
}
void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
if (Object::cast_to<CSGSphere3D>(cs)) {
CSGSphere3D *s = Object::cast_to<CSGSphere3D>(cs);
@@ -215,7 +217,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Sphere Shape Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
@@ -229,7 +231,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Change Box Shape Size"));
ur->add_do_method(s, "set_size", s->get_size());
ur->add_undo_method(s, "set_size", p_restore);
@@ -247,7 +249,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Cylinder Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
@@ -272,7 +274,7 @@ void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int
return;
}
- UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
if (p_id == 0) {
ur->create_action(TTR("Change Torus Inner Radius"));
ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
@@ -306,7 +308,7 @@ bool CSGShape3DGizmoPlugin::is_selectable_when_hidden() const {
void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_node_3d());
Vector<Vector3> faces = cs->get_brush_faces();
diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h
index fa51010f98..89a4305683 100644
--- a/modules/csg/editor/csg_gizmos.h
+++ b/modules/csg/editor/csg_gizmos.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csg_gizmos.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csg_gizmos.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CSG_GIZMOS_H
#define CSG_GIZMOS_H
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
index 9b5888dafe..de949e30d8 100644
--- a/modules/csg/register_types.cpp
+++ b/modules/csg/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h
index ec65adde9c..9a21660a48 100644
--- a/modules/csg/register_types.h
+++ b/modules/csg/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CSG_REGISTER_TYPES_H
#define CSG_REGISTER_TYPES_H
diff --git a/modules/cvtt/config.py b/modules/cvtt/config.py
index 53b8f2f2e3..eb565b85b9 100644
--- a/modules/cvtt/config.py
+++ b/modules/cvtt/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env["tools"]
+ return env.editor_build
def configure(env):
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 3322ff0a1b..4982b6b995 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_compress_cvtt.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_compress_cvtt.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_compress_cvtt.h"
@@ -230,7 +230,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
h = MAX(h / 2, 1);
}
- p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+ p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
void image_decompress_cvtt(Image *p_image) {
@@ -339,5 +339,5 @@ void image_decompress_cvtt(Image *p_image) {
w >>= 1;
h >>= 1;
}
- p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+ p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h
index a8dbe1d929..5dc8b6f52c 100644
--- a/modules/cvtt/image_compress_cvtt.h
+++ b/modules/cvtt/image_compress_cvtt.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_compress_cvtt.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_compress_cvtt.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_COMPRESS_CVTT_H
#define IMAGE_COMPRESS_CVTT_H
diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp
index ff22c0f53e..c22aedb0fc 100644
--- a/modules/cvtt/register_types.cpp
+++ b/modules/cvtt/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
index 38a375eb44..f1fb620d7e 100644
--- a/modules/cvtt/register_types.h
+++ b/modules/cvtt/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CVTT_REGISTER_TYPES_H
#define CVTT_REGISTER_TYPES_H
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
index e819c92dd3..d336269eb3 100644
--- a/modules/dds/register_types.cpp
+++ b/modules/dds/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h
index 3cd154d576..51b2f66706 100644
--- a/modules/dds/register_types.h
+++ b/modules/dds/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 DDS_REGISTER_TYPES_H
#define DDS_REGISTER_TYPES_H
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index eb5614338b..0bde6f62ed 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* texture_loader_dds.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* texture_loader_dds.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "texture_loader_dds.h"
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
index 701f8f4a13..dc3df1fcee 100644
--- a/modules/dds/texture_loader_dds.h
+++ b/modules/dds/texture_loader_dds.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* texture_loader_dds.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* texture_loader_dds.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TEXTURE_LOADER_DDS_H
#define TEXTURE_LOADER_DDS_H
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index 97feea2b44..779ce165d2 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -103,9 +103,9 @@ env_oidn.Append(
"__STDC_LIMIT_MACROS",
"DISABLE_VERBOSE",
"MKLDNN_ENABLE_CONCURRENT_EXEC",
- "NDEBUG",
]
)
+env_oidn.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds.
env_thirdparty = env_oidn.Clone()
env_thirdparty.disable_warnings()
diff --git a/modules/denoise/config.py b/modules/denoise/config.py
index 521115dae5..27d2ffbf86 100644
--- a/modules/denoise/config.py
+++ b/modules/denoise/config.py
@@ -2,17 +2,10 @@ def can_build(env, platform):
# Thirdparty dependency OpenImage Denoise includes oneDNN library
# and the version we use only supports x86_64.
# It's also only relevant for tools build and desktop platforms,
- # as doing lightmap generation and denoising on Android or HTML5
+ # as doing lightmap generation and denoising on Android or Web
# would be a bit far-fetched.
desktop_platforms = ["linuxbsd", "macos", "windows"]
- supported_arch = env["bits"] == "64"
- if env["arch"] == "arm64":
- supported_arch = False
- if env["arch"].startswith("ppc"):
- supported_arch = False
- if env["arch"].startswith("rv"):
- supported_arch = False
- return env["tools"] and platform in desktop_platforms and supported_arch
+ return env.editor_build and platform in desktop_platforms and env["arch"] == "x86_64"
def configure(env):
diff --git a/modules/denoise/denoise_wrapper.cpp b/modules/denoise/denoise_wrapper.cpp
index ef97c1a184..9effb60202 100644
--- a/modules/denoise/denoise_wrapper.cpp
+++ b/modules/denoise/denoise_wrapper.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* denoise_wrapper.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* denoise_wrapper.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "denoise_wrapper.h"
#include "thirdparty/oidn/include/OpenImageDenoise/oidn.h"
diff --git a/modules/denoise/denoise_wrapper.h b/modules/denoise/denoise_wrapper.h
index 44e61ce31d..d4bf154a5d 100644
--- a/modules/denoise/denoise_wrapper.h
+++ b/modules/denoise/denoise_wrapper.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* denoise_wrapper.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* denoise_wrapper.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 DENOISE_WRAPPER_H
#define DENOISE_WRAPPER_H
diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp
index a0dbd07b10..8419b7e52a 100644
--- a/modules/denoise/lightmap_denoiser.cpp
+++ b/modules/denoise/lightmap_denoiser.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* lightmap_denoiser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmap_denoiser.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "lightmap_denoiser.h"
#include "denoise_wrapper.h"
@@ -51,7 +51,7 @@ Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) {
return p_image;
}
- img->create(img->get_width(), img->get_height(), false, img->get_format(), data);
+ img->set_data(img->get_width(), img->get_height(), false, img->get_format(), data);
return img;
}
diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h
index 452a7cae85..8f658ab096 100644
--- a/modules/denoise/lightmap_denoiser.h
+++ b/modules/denoise/lightmap_denoiser.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* lightmap_denoiser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmap_denoiser.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 LIGHTMAP_DENOISER_H
#define LIGHTMAP_DENOISER_H
diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp
index 891a03c657..9448776529 100644
--- a/modules/denoise/register_types.cpp
+++ b/modules/denoise/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/config/engine.h"
diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h
index 13eba88d17..239877a5c7 100644
--- a/modules/denoise/register_types.h
+++ b/modules/denoise/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 DENOISE_REGISTER_TYPES_H
#define DENOISE_REGISTER_TYPES_H
diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml
index 14aad0cb39..8c84fe87d7 100644
--- a/modules/enet/doc_classes/ENetConnection.xml
+++ b/modules/enet/doc_classes/ENetConnection.xml
@@ -12,31 +12,31 @@
<methods>
<method name="bandwidth_limit">
<return type="void" />
- <argument index="0" name="in_bandwidth" type="int" default="0" />
- <argument index="1" name="out_bandwidth" type="int" default="0" />
+ <param index="0" name="in_bandwidth" type="int" default="0" />
+ <param index="1" name="out_bandwidth" type="int" default="0" />
<description>
Adjusts the bandwidth limits of a host.
</description>
</method>
<method name="broadcast">
<return type="void" />
- <argument index="0" name="channel" type="int" />
- <argument index="1" name="packet" type="PackedByteArray" />
- <argument index="2" name="flags" type="int" />
+ <param index="0" name="channel" type="int" />
+ <param index="1" name="packet" type="PackedByteArray" />
+ <param index="2" name="flags" type="int" />
<description>
Queues a [code]packet[/code] to be sent to all peers associated with the host over the specified [code]channel[/code]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags.
</description>
</method>
<method name="channel_limit">
<return type="void" />
- <argument index="0" name="limit" type="int" />
+ <param index="0" name="limit" type="int" />
<description>
Limits the maximum allowed channels of future incoming connections.
</description>
</method>
<method name="compress">
<return type="void" />
- <argument index="0" name="mode" type="int" enum="ENetConnection.CompressionMode" />
+ <param index="0" name="mode" type="int" enum="ENetConnection.CompressionMode" />
<description>
Sets the compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
[b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets.
@@ -45,10 +45,10 @@
</method>
<method name="connect_to_host">
<return type="ENetPacketPeer" />
- <argument index="0" name="address" type="String" />
- <argument index="1" name="port" type="int" />
- <argument index="2" name="channels" type="int" default="0" />
- <argument index="3" name="data" type="int" default="0" />
+ <param index="0" name="address" type="String" />
+ <param index="1" name="port" type="int" />
+ <param index="2" name="channels" type="int" default="0" />
+ <param index="3" name="data" type="int" default="0" />
<description>
Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocating the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer.
[b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method.
@@ -56,22 +56,22 @@
</method>
<method name="create_host">
<return type="int" enum="Error" />
- <argument index="0" name="max_peers" type="int" default="32" />
- <argument index="1" name="max_channels" type="int" default="0" />
- <argument index="2" name="in_bandwidth" type="int" default="0" />
- <argument index="3" name="out_bandwidth" type="int" default="0" />
+ <param index="0" name="max_peers" type="int" default="32" />
+ <param index="1" name="max_channels" type="int" default="0" />
+ <param index="2" name="in_bandwidth" type="int" default="0" />
+ <param index="3" name="out_bandwidth" type="int" default="0" />
<description>
Create an ENetHost that will allow up to [code]max_peers[/code] connected peers, each allocating up to [code]max_channels[/code] channels, optionally limiting bandwidth to [code]in_bandwidth[/code] and [code]out_bandwidth[/code].
</description>
</method>
<method name="create_host_bound">
<return type="int" enum="Error" />
- <argument index="0" name="bind_address" type="String" />
- <argument index="1" name="bind_port" type="int" />
- <argument index="2" name="max_peers" type="int" default="32" />
- <argument index="3" name="max_channels" type="int" default="0" />
- <argument index="4" name="in_bandwidth" type="int" default="0" />
- <argument index="5" name="out_bandwidth" type="int" default="0" />
+ <param index="0" name="bind_address" type="String" />
+ <param index="1" name="bind_port" type="int" />
+ <param index="2" name="max_peers" type="int" default="32" />
+ <param index="3" name="max_channels" type="int" default="0" />
+ <param index="4" name="in_bandwidth" type="int" default="0" />
+ <param index="5" name="out_bandwidth" type="int" default="0" />
<description>
Create an ENetHost like [method create_host] which is also bound to the given [code]bind_address[/code] and [code]bind_port[/code].
</description>
@@ -84,17 +84,17 @@
</method>
<method name="dtls_client_setup">
<return type="int" enum="Error" />
- <argument index="0" name="certificate" type="X509Certificate" />
- <argument index="1" name="hostname" type="String" />
- <argument index="2" name="verify" type="bool" default="true" />
+ <param index="0" name="certificate" type="X509Certificate" />
+ <param index="1" name="hostname" type="String" />
+ <param index="2" name="verify" type="bool" default="true" />
<description>
Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS with [code]certificate[/code] and [code]hostname[/code] verification. Verification can be optionally turned off via the [code]verify[/code] parameter.
</description>
</method>
<method name="dtls_server_setup">
<return type="int" enum="Error" />
- <argument index="0" name="key" type="CryptoKey" />
- <argument index="1" name="certificate" type="X509Certificate" />
+ <param index="0" name="key" type="CryptoKey" />
+ <param index="1" name="certificate" type="X509Certificate" />
<description>
Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet servers. Call this right after [method create_host_bound] to have ENet expect peers to connect using DTLS.
</description>
@@ -118,7 +118,7 @@
</description>
</method>
<method name="get_peers">
- <return type="Array" />
+ <return type="ENetPacketPeer[]" />
<description>
Returns the list of peers associated with this host.
[b]Note:[/b] This list might include some peers that are not fully connected or are still being disconnected.
@@ -126,14 +126,14 @@
</method>
<method name="pop_statistic">
<return type="float" />
- <argument index="0" name="statistic" type="int" enum="ENetConnection.HostStatistic" />
+ <param index="0" name="statistic" type="int" enum="ENetConnection.HostStatistic" />
<description>
Returns and resets host statistics. See [enum HostStatistic] for more info.
</description>
</method>
<method name="refuse_new_connections">
<return type="void" />
- <argument index="0" name="refuse" type="bool" />
+ <param index="0" name="refuse" type="bool" />
<description>
Configures the DTLS server to automatically drop new connections.
[b]Note:[/b] This method is only relevant after calling [method dtls_server_setup].
@@ -141,7 +141,7 @@
</method>
<method name="service">
<return type="Array" />
- <argument index="0" name="timeout" type="int" default="0" />
+ <param index="0" name="timeout" type="int" default="0" />
<description>
Waits for events on the host specified and shuttles packets between the host and its peers. The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer].
Call this function regularly to handle connections, disconnections, and to receive new packets.
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
index 2ecf6b4122..2e0d1f5079 100644
--- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -14,60 +14,53 @@
<methods>
<method name="add_mesh_peer">
<return type="int" enum="Error" />
- <argument index="0" name="peer_id" type="int" />
- <argument index="1" name="host" type="ENetConnection" />
+ <param index="0" name="peer_id" type="int" />
+ <param index="1" name="host" type="ENetConnection" />
<description>
Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code].
[b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
</description>
</method>
- <method name="close_connection">
- <return type="void" />
- <argument index="0" name="wait_usec" type="int" default="100" />
- <description>
- Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server.
- </description>
- </method>
<method name="create_client">
<return type="int" enum="Error" />
- <argument index="0" name="address" type="String" />
- <argument index="1" name="port" type="int" />
- <argument index="2" name="channel_count" type="int" default="0" />
- <argument index="3" name="in_bandwidth" type="int" default="0" />
- <argument index="4" name="out_bandwidth" type="int" default="0" />
- <argument index="5" name="local_port" type="int" default="0" />
+ <param index="0" name="address" type="String" />
+ <param index="1" name="port" type="int" />
+ <param index="2" name="channel_count" type="int" default="0" />
+ <param index="3" name="in_bandwidth" type="int" default="0" />
+ <param index="4" name="out_bandwidth" type="int" default="0" />
+ <param index="5" name="local_port" type="int" default="0" />
<description>
- Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
+ Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
</description>
</method>
<method name="create_mesh">
<return type="int" enum="Error" />
- <argument index="0" name="unique_id" type="int" />
+ <param index="0" name="unique_id" type="int" />
<description>
Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server).
</description>
</method>
<method name="create_server">
<return type="int" enum="Error" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="max_clients" type="int" default="32" />
- <argument index="2" name="max_channels" type="int" default="0" />
- <argument index="3" name="in_bandwidth" type="int" default="0" />
- <argument index="4" name="out_bandwidth" type="int" default="0" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="max_clients" type="int" default="32" />
+ <param index="2" name="max_channels" type="int" default="0" />
+ <param index="3" name="in_bandwidth" type="int" default="0" />
+ <param index="4" name="out_bandwidth" type="int" default="0" />
<description>
- Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created.
+ Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the server could not be created.
</description>
</method>
<method name="get_peer" qualifiers="const">
<return type="ENetPacketPeer" />
- <argument index="0" name="id" type="int" />
+ <param index="0" name="id" type="int" />
<description>
Returns the [ENetPacketPeer] associated to the given [code]id[/code].
</description>
</method>
<method name="set_bind_ip">
<return type="void" />
- <argument index="0" name="ip" type="String" />
+ <param index="0" name="ip" type="String" />
<description>
The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code].
</description>
@@ -77,8 +70,5 @@
<member name="host" type="ENetConnection" setter="" getter="get_host">
The underlying [ENetConnection] created after [method create_client] and [method create_server].
</member>
- <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
- Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
- </member>
</members>
</class>
diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml
index 5de5a60853..52f45b2338 100644
--- a/modules/enet/doc_classes/ENetPacketPeer.xml
+++ b/modules/enet/doc_classes/ENetPacketPeer.xml
@@ -18,6 +18,18 @@
Returns the number of channels allocated for communication with peer.
</description>
</method>
+ <method name="get_remote_address" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the IP address of this peer.
+ </description>
+ </method>
+ <method name="get_remote_port" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the remote port of this peer.
+ </description>
+ </method>
<method name="get_state" qualifiers="const">
<return type="int" enum="ENetPacketPeer.PeerState" />
<description>
@@ -26,7 +38,7 @@
</method>
<method name="get_statistic">
<return type="float" />
- <argument index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" />
+ <param index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" />
<description>
Returns the requested [code]statistic[/code] for this peer. See [enum PeerStatistic].
</description>
@@ -39,21 +51,21 @@
</method>
<method name="peer_disconnect">
<return type="void" />
- <argument index="0" name="data" type="int" default="0" />
+ <param index="0" name="data" type="int" default="0" />
<description>
Request a disconnection from a peer. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete.
</description>
</method>
<method name="peer_disconnect_later">
<return type="void" />
- <argument index="0" name="data" type="int" default="0" />
+ <param index="0" name="data" type="int" default="0" />
<description>
Request a disconnection from a peer, but only after all queued outgoing packets are sent. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete.
</description>
</method>
<method name="peer_disconnect_now">
<return type="void" />
- <argument index="0" name="data" type="int" default="0" />
+ <param index="0" name="data" type="int" default="0" />
<description>
Force an immediate disconnection from a peer. No [constant ENetConnection.EVENT_DISCONNECT] will be generated. The foreign peer is not guaranteed to receive the disconnect notification, and is reset immediately upon return from this function.
</description>
@@ -66,9 +78,9 @@
</method>
<method name="ping_interval">
<return type="void" />
- <argument index="0" name="ping_interval" type="int" />
+ <param index="0" name="ping_interval" type="int" />
<description>
- Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes.
+ Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds.
</description>
</method>
<method name="reset">
@@ -79,18 +91,18 @@
</method>
<method name="send">
<return type="int" enum="Error" />
- <argument index="0" name="channel" type="int" />
- <argument index="1" name="packet" type="PackedByteArray" />
- <argument index="2" name="flags" type="int" />
+ <param index="0" name="channel" type="int" />
+ <param index="1" name="packet" type="PackedByteArray" />
+ <param index="2" name="flags" type="int" />
<description>
Queues a [code]packet[/code] to be sent over the specified [code]channel[/code]. See [code]FLAG_*[/code] constants for available packet flags.
</description>
</method>
<method name="set_timeout">
<return type="void" />
- <argument index="0" name="timeout" type="int" />
- <argument index="1" name="timeout_min" type="int" />
- <argument index="2" name="timeout_max" type="int" />
+ <param index="0" name="timeout" type="int" />
+ <param index="1" name="timeout_min" type="int" />
+ <param index="2" name="timeout_max" type="int" />
<description>
Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds.
The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped.
@@ -98,38 +110,48 @@
</method>
<method name="throttle_configure">
<return type="void" />
- <argument index="0" name="interval" type="int" />
- <argument index="1" name="acceleration" type="int" />
- <argument index="2" name="deceleration" type="int" />
+ <param index="0" name="interval" type="int" />
+ <param index="1" name="acceleration" type="int" />
+ <param index="2" name="deceleration" type="int" />
<description>
Configures throttle parameter for a peer.
- Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
+ Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probability by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent.
- When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
+ When the throttle has a value of [code]0[/code], all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions.
</description>
</method>
</methods>
<constants>
<constant name="STATE_DISCONNECTED" value="0" enum="PeerState">
+ The peer is disconnected.
</constant>
<constant name="STATE_CONNECTING" value="1" enum="PeerState">
+ The peer is currently attempting to connect.
</constant>
<constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState">
+ The peer has acknowledged the connection request.
</constant>
<constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState">
+ The peer is currently connecting.
</constant>
<constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState">
+ The peer has successfully connected, but is not ready to communicate with yet ([constant STATE_CONNECTED]).
</constant>
<constant name="STATE_CONNECTED" value="5" enum="PeerState">
+ The peer is currently connected and ready to communicate with.
</constant>
<constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState">
+ The peer is slated to disconnect after it has no more outgoing packets to send.
</constant>
<constant name="STATE_DISCONNECTING" value="7" enum="PeerState">
+ The peer is currently disconnecting.
</constant>
<constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState">
+ The peer has acknowledged the disconnection request.
</constant>
<constant name="STATE_ZOMBIE" value="9" enum="PeerState">
+ The peer has lost connection, but is not considered truly disconnected (as the peer didn't acknowledge the disconnection request).
</constant>
<constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic">
Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE].
@@ -138,6 +160,7 @@
Packet loss variance.
</constant>
<constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic">
+ The time at which packet loss statistics were last updated (in milliseconds since the connection started). The interval for packet loss statistics updates is 10 seconds, and at least one packet must have been sent since the last statistics update.
</constant>
<constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic">
Mean packet round trip time for reliable packets.
@@ -152,24 +175,31 @@
Variance of the last trip time recorded.
</constant>
<constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic">
+ The peer's current throttle status.
</constant>
<constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic">
+ The maximum number of unreliable packets that should not be dropped. This value is always greater than or equal to [code]1[/code]. The initial value is equal to [constant PACKET_THROTTLE_SCALE].
</constant>
<constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic">
+ Internal value used to increment the packet throttle counter. The value is hardcoded to [code]7[/code] and cannot be changed. You probably want to look at [constant PEER_PACKET_THROTTLE_ACCELERATION] instead.
</constant>
<constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic">
+ The time at which throttle statistics were last updated (in milliseconds since the connection started). The interval for throttle statistics updates is [constant PEER_PACKET_THROTTLE_INTERVAL].
</constant>
<constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic">
+ The throttle's acceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]more[/i] often. The default value is [code]2[/code].
</constant>
<constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic">
+ The throttle's deceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]less[/i] often. The default value is [code]2[/code].
</constant>
<constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic">
+ The interval over which the lowest mean round trip time should be measured for use by the throttle mechanism (in milliseconds). The default value is [code]5000[/code].
</constant>
<constant name="PACKET_LOSS_SCALE" value="65536">
The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS].
</constant>
<constant name="PACKET_THROTTLE_SCALE" value="32">
- The reference value for throttle configuration. See [method throttle_configure].
+ The reference value for throttle configuration. The default value is [code]32[/code]. See [method throttle_configure].
</constant>
<constant name="FLAG_RELIABLE" value="1">
Mark the packet to be sent as reliable.
diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp
index 629974d7c7..d16e7d7c4a 100644
--- a/modules/enet/enet_connection.cpp
+++ b/modules/enet/enet_connection.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_connection.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_connection.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "enet_connection.h"
@@ -34,6 +34,7 @@
#include "core/io/compression.h"
#include "core/io/ip.h"
+#include "core/variant/typed_array.h"
void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) {
ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
@@ -263,9 +264,9 @@ void ENetConnection::get_peers(List<Ref<ENetPacketPeer>> &r_peers) {
}
}
-Array ENetConnection::_get_peers() {
+TypedArray<ENetPacketPeer> ENetConnection::_get_peers() {
ERR_FAIL_COND_V_MSG(!host, Array(), "The ENetConnection instance isn't currently active.");
- Array out;
+ TypedArray<ENetPacketPeer> out;
for (const Ref<ENetPacketPeer> &I : peers) {
out.push_back(I);
}
diff --git a/modules/enet/enet_connection.h b/modules/enet/enet_connection.h
index 0c873b6c55..9e444911cc 100644
--- a/modules/enet/enet_connection.h
+++ b/modules/enet/enet_connection.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_connection.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_connection.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ENET_CONNECTION_H
#define ENET_CONNECTION_H
@@ -38,6 +38,9 @@
#include <enet/enet.h>
+template <typename T>
+class TypedArray;
+
class ENetConnection : public RefCounted {
GDCLASS(ENetConnection, RefCounted);
@@ -83,7 +86,7 @@ private:
Error _create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth);
Array _service(int p_timeout = 0);
void _broadcast(int p_channel, PackedByteArray p_packet, int p_flags);
- Array _get_peers();
+ TypedArray<ENetPacketPeer> _get_peers();
class Compressor {
private:
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index dfdd08c9f4..50ea0dd37a 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_multiplayer_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "enet_multiplayer_peer.h"
#include "core/io/ip.h"
@@ -44,6 +44,22 @@ int ENetMultiplayerPeer::get_packet_peer() const {
return incoming_packets.front()->get().from;
}
+MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, TRANSFER_MODE_RELIABLE);
+ return incoming_packets.front()->get().transfer_mode;
+}
+
+int ENetMultiplayerPeer::get_packet_channel() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
+ int ch = incoming_packets.front()->get().channel;
+ if (ch >= SYSCH_MAX) { // First 2 channels are reserved.
+ return ch - SYSCH_MAX + 1;
+ }
+ return 0;
+}
+
Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
set_refuse_new_connections(false);
@@ -114,182 +130,37 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
return OK;
}
-bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
- switch (p_type) {
- case ENetConnection::EVENT_CONNECT: {
- if (is_refusing_new_connections()) {
- p_event.peer->reset();
- return false;
- }
- // Client joined with invalid ID, probably trying to exploit us.
- if (p_event.data < 2 || peers.has((int)p_event.data)) {
- p_event.peer->reset();
- return false;
- }
- int id = p_event.data;
- p_event.peer->set_meta(SNAME("_net_id"), id);
- peers[id] = p_event.peer;
-
- emit_signal(SNAME("peer_connected"), id);
- if (server_relay) {
- _notify_peers(id, true);
- }
- return false;
- }
- case ENetConnection::EVENT_DISCONNECT: {
- int id = p_event.peer->get_meta(SNAME("_net_id"));
- if (!peers.has(id)) {
- // Never fully connected.
- return false;
- }
-
- emit_signal(SNAME("peer_disconnected"), id);
- peers.erase(id);
- if (server_relay) {
- _notify_peers(id, false);
- }
- return false;
- }
- case ENetConnection::EVENT_RECEIVE: {
- if (p_event.channel_id == SYSCH_CONFIG) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Only server can send config messages");
- } else {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- uint32_t source = decode_uint32(&p_event.packet->data[0]);
- int target = decode_uint32(&p_event.packet->data[4]);
-
- uint32_t id = p_event.peer->get_meta(SNAME("_net_id"));
- // Someone is cheating and trying to fake the source!
- if (source != id) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!");
- }
-
- Packet packet;
- packet.packet = p_event.packet;
- packet.channel = p_event.channel_id;
- packet.from = id;
-
- // Even if relaying is disabled, these targets are valid as incoming packets.
- if (target == 1 || target == 0 || target < -1) {
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- }
-
- if (server_relay && target != 1) {
- packet.packet->referenceCount++;
- _relay(source, target, p_event.channel_id, p_event.packet);
- packet.packet->referenceCount--;
- _destroy_unused(p_event.packet);
- }
- // Destroy packet later
- }
- return false;
- }
- default:
- return true;
+void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) {
+ Packet packet;
+ packet.packet = p_event.packet;
+ packet.channel = p_event.channel_id;
+ packet.from = p_source;
+ if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) {
+ packet.transfer_mode = TRANSFER_MODE_RELIABLE;
+ } else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) {
+ packet.transfer_mode = TRANSFER_MODE_UNRELIABLE;
+ } else {
+ packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED;
}
+ packet.packet->referenceCount++;
+ incoming_packets.push_back(packet);
}
-bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
- switch (p_type) {
- case ENetConnection::EVENT_CONNECT: {
- connection_status = CONNECTION_CONNECTED;
- emit_signal(SNAME("peer_connected"), 1);
- emit_signal(SNAME("connection_succeeded"));
- return false;
- }
- case ENetConnection::EVENT_DISCONNECT: {
- if (connection_status == CONNECTION_CONNECTED) {
- // Client just disconnected from server.
- emit_signal(SNAME("server_disconnected"));
- } else {
- emit_signal(SNAME("connection_failed"));
- }
- close_connection();
- return true;
- }
- case ENetConnection::EVENT_RECEIVE: {
- if (p_event.channel_id == SYSCH_CONFIG) {
- // Config message
- if (p_event.packet->dataLength != 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V(false);
- }
-
- int msg = decode_uint32(&p_event.packet->data[0]);
- int id = decode_uint32(&p_event.packet->data[4]);
-
- switch (msg) {
- case SYSMSG_ADD_PEER: {
- peers[id] = Ref<ENetPacketPeer>();
- emit_signal(SNAME("peer_connected"), id);
-
- } break;
- case SYSMSG_REMOVE_PEER: {
- peers.erase(id);
- emit_signal(SNAME("peer_disconnected"), id);
- } break;
- }
- _destroy_unused(p_event.packet);
- } else {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- uint32_t source = decode_uint32(&p_event.packet->data[0]);
- Packet packet;
- packet.packet = p_event.packet;
- packet.from = source;
- packet.channel = p_event.channel_id;
-
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- // Destroy packet later
- }
- return false;
+void ENetMultiplayerPeer::_disconnect_inactive_peers() {
+ HashSet<int> to_drop;
+ for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.value->is_active()) {
+ continue;
}
- default:
- return true;
+ to_drop.insert(E.key);
}
-}
-
-bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event, int p_peer_id) {
- switch (p_type) {
- case ENetConnection::EVENT_CONNECT:
- p_event.peer->reset();
- return false;
- case ENetConnection::EVENT_DISCONNECT:
- if (peers.has(p_peer_id)) {
- emit_signal(SNAME("peer_disconnected"), p_peer_id);
- peers.erase(p_peer_id);
- }
- hosts.erase(p_peer_id);
- return true;
- case ENetConnection::EVENT_RECEIVE: {
- if (p_event.packet->dataLength < 8) {
- _destroy_unused(p_event.packet);
- ERR_FAIL_V_MSG(false, "Invalid packet size");
- }
-
- Packet packet;
- packet.packet = p_event.packet;
- packet.from = p_peer_id;
- packet.channel = p_event.channel_id;
-
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- return false;
- } break;
- default:
- // Nothing to do
- return true;
+ for (const int &P : to_drop) {
+ peers.erase(P);
+ if (hosts.has(P)) {
+ hosts.erase(P);
+ }
+ ERR_CONTINUE(active_mode == MODE_CLIENT && P != TARGET_PEER_SERVER);
+ emit_signal(SNAME("peer_disconnected"), P);
}
}
@@ -298,74 +169,92 @@ void ENetMultiplayerPeer::poll() {
_pop_current_packet();
+ _disconnect_inactive_peers();
+
switch (active_mode) {
case MODE_CLIENT: {
- if (peers.has(1) && !peers[1]->is_active()) {
- if (connection_status == CONNECTION_CONNECTED) {
- // Client just disconnected from server.
- emit_signal(SNAME("server_disconnected"));
- } else {
- emit_signal(SNAME("connection_failed"));
- }
- close_connection();
+ if (!peers.has(1)) {
+ close();
return;
}
ENetConnection::Event event;
ENetConnection::EventType ret = hosts[0]->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- return;
- }
do {
- if (_parse_client_event(ret, event)) {
- return;
+ if (ret == ENetConnection::EVENT_CONNECT) {
+ connection_status = CONNECTION_CONNECTED;
+ emit_signal(SNAME("peer_connected"), 1);
+ } else if (ret == ENetConnection::EVENT_DISCONNECT) {
+ if (connection_status == CONNECTION_CONNECTED) {
+ // Client just disconnected from server.
+ emit_signal(SNAME("peer_disconnected"), 1);
+ }
+ close();
+ } else if (ret == ENetConnection::EVENT_RECEIVE) {
+ _store_packet(1, event);
+ } else if (ret != ENetConnection::EVENT_NONE) {
+ close(); // Error.
}
- } while (hosts[0]->check_events(ret, event) > 0);
+ } while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0);
} break;
case MODE_SERVER: {
- for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (!(E.value->is_active())) {
- emit_signal(SNAME("peer_disconnected"), E.value->get_meta(SNAME("_net_id")));
- peers.erase(E.key);
- }
- }
ENetConnection::Event event;
ENetConnection::EventType ret = hosts[0]->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- return;
- }
do {
- if (_parse_server_event(ret, event)) {
- return;
+ if (ret == ENetConnection::EVENT_CONNECT) {
+ if (is_refusing_new_connections()) {
+ event.peer->reset();
+ continue;
+ }
+ // Client joined with invalid ID, probably trying to exploit us.
+ if (event.data < 2 || peers.has((int)event.data)) {
+ event.peer->reset();
+ continue;
+ }
+ int id = event.data;
+ event.peer->set_meta(SNAME("_net_id"), id);
+ peers[id] = event.peer;
+ emit_signal(SNAME("peer_connected"), id);
+ } else if (ret == ENetConnection::EVENT_DISCONNECT) {
+ int id = event.peer->get_meta(SNAME("_net_id"));
+ if (!peers.has(id)) {
+ // Never fully connected.
+ continue;
+ }
+ emit_signal(SNAME("peer_disconnected"), id);
+ peers.erase(id);
+ } else if (ret == ENetConnection::EVENT_RECEIVE) {
+ int32_t source = event.peer->get_meta(SNAME("_net_id"));
+ _store_packet(source, event);
+ } else if (ret != ENetConnection::EVENT_NONE) {
+ close(); // Error
}
- } while (hosts[0]->check_events(ret, event) > 0);
+ } while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0);
} break;
case MODE_MESH: {
- for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (!(E.value->is_active())) {
- emit_signal(SNAME("peer_disconnected"), E.key);
- peers.erase(E.key);
- if (hosts.has(E.key)) {
- hosts.erase(E.key);
- }
- }
- }
+ HashSet<int> to_drop;
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
ENetConnection::Event event;
ENetConnection::EventType ret = E.value->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- if (peers.has(E.key)) {
- emit_signal(SNAME("peer_disconnected"), E.key);
- peers.erase(E.key);
- }
- hosts.erase(E.key);
- continue;
- }
do {
- if (_parse_mesh_event(ret, event, E.key)) {
+ if (ret == ENetConnection::EVENT_CONNECT) {
+ event.peer->reset();
+ } else if (ret == ENetConnection::EVENT_RECEIVE) {
+ _store_packet(E.key, event);
+ } else if (ret == ENetConnection::EVENT_NONE) {
+ break; // Keep polling the others.
+ } else {
+ to_drop.insert(E.key); // Error or disconnect.
break; // Keep polling the others.
}
} while (E.value->check_events(ret, event) > 0);
}
+ for (const int &P : to_drop) {
+ if (peers.has(P)) {
+ emit_signal(SNAME("peer_disconnected"), P);
+ peers.erase(P);
+ }
+ hosts.erase(P);
+ }
} break;
default:
return;
@@ -376,29 +265,45 @@ bool ENetMultiplayerPeer::is_server() const {
return active_mode == MODE_SERVER;
}
-void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
+bool ENetMultiplayerPeer::is_server_relay_supported() const {
+ return active_mode == MODE_SERVER || active_mode == MODE_CLIENT;
+}
+
+void ENetMultiplayerPeer::disconnect_peer(int p_peer, bool p_force) {
+ ERR_FAIL_COND(!_is_active() || !peers.has(p_peer));
+ peers[p_peer]->peer_disconnect(0); // Will be removed during next poll.
+ if (active_mode == MODE_CLIENT || active_mode == MODE_SERVER) {
+ hosts[0]->flush();
+ } else {
+ ERR_FAIL_COND(!hosts.has(p_peer));
+ hosts[p_peer]->flush();
+ }
+ if (p_force) {
+ peers.erase(p_peer);
+ if (hosts.has(p_peer)) {
+ hosts.erase(p_peer);
+ }
+ if (active_mode == MODE_CLIENT) {
+ hosts.clear(); // Avoid flushing again.
+ close();
+ }
+ }
+}
+
+void ENetMultiplayerPeer::close() {
if (!_is_active()) {
return;
}
_pop_current_packet();
- bool peers_disconnected = false;
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
if (E.value.is_valid() && E.value->get_state() == ENetPacketPeer::STATE_CONNECTED) {
- E.value->peer_disconnect_now(unique_id);
- peers_disconnected = true;
+ E.value->peer_disconnect_now(0);
}
}
-
- if (peers_disconnected) {
- for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
- E.value->flush();
- }
-
- if (wait_usec > 0) {
- OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send
- }
+ for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
+ E.value->flush();
}
active_mode = MODE_NONE;
@@ -422,8 +327,8 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si
current_packet = incoming_packets.front()->get();
incoming_packets.pop_front();
- *r_buffer = (const uint8_t *)(&current_packet.packet->data[8]);
- r_buffer_size = current_packet.packet->dataLength - 8;
+ *r_buffer = (const uint8_t *)(current_packet.packet->data);
+ r_buffer_size = current_packet.packet->dataLength;
return OK;
}
@@ -436,9 +341,9 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
int packet_flags = 0;
int channel = SYSCH_RELIABLE;
- int transfer_channel = get_transfer_channel();
- if (transfer_channel > 0) {
- channel = SYSCH_MAX + transfer_channel - 1;
+ int tr_channel = get_transfer_channel();
+ if (tr_channel > 0) {
+ channel = SYSCH_MAX + tr_channel - 1;
} else {
switch (get_transfer_mode()) {
case TRANSFER_MODE_UNRELIABLE: {
@@ -457,15 +362,13 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
}
#ifdef DEBUG_ENABLED
- if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) {
- WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU));
+ if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) {
+ WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU));
}
#endif
- ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); // Source ID
- encode_uint32(target_peer, &packet->data[4]); // Dest ID
- memcpy(&packet->data[8], p_buffer, p_buffer_size);
+ ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags);
+ memcpy(&packet->data[0], p_buffer, p_buffer_size);
if (is_server()) {
if (target_peer == 0) {
@@ -548,16 +451,6 @@ void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) {
MultiplayerPeer::set_refuse_new_connections(p_enabled);
}
-void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) {
- ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active.");
-
- server_relay = p_enabled;
-}
-
-bool ENetMultiplayerPeer::is_server_relay_enabled() const {
- return server_relay;
-}
-
Ref<ENetConnection> ENetMultiplayerPeer::get_host() const {
ERR_FAIL_COND_V(!_is_active(), nullptr);
ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr);
@@ -577,84 +470,16 @@ void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) {
}
}
-void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) {
- if (p_to == 0) {
- // Re-send to everyone but sender :|
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_from) {
- continue;
- }
-
- E.value->send(p_channel, p_packet);
- }
- } else if (p_to < 0) {
- // Re-send to everyone but excluded and sender.
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded
- continue;
- }
-
- E.value->send(p_channel, p_packet);
- }
- } else {
- // To someone else, specifically
- ERR_FAIL_COND(!peers.has(p_to));
- ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags);
- peers[p_to]->send(p_channel, packet);
- }
-}
-
-void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) {
- if (p_connected) {
- ERR_FAIL_COND(!peers.has(p_id));
- // Someone connected, notify all the peers available.
- Ref<ENetPacketPeer> peer = peers[p_id];
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
- encode_uint32(p_id, &packet->data[4]);
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_id) {
- continue;
- }
- // Send new peer to existing peer.
- E.value->send(SYSCH_CONFIG, packet);
- // Send existing peer to new peer.
- // This packet will be automatically destroyed by ENet after send.
- ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]);
- encode_uint32(E.key, &packet2->data[4]);
- peer->send(SYSCH_CONFIG, packet2);
- }
- _destroy_unused(packet);
- } else {
- // Server just received a client disconnect and is in relay mode, notify everyone else.
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
- encode_uint32(p_id, &packet->data[4]);
- for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (E.key == p_id) {
- continue;
- }
- E.value->send(SYSCH_CONFIG, packet);
- }
- _destroy_unused(packet);
- }
-}
-
void ENetMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
ClassDB::bind_method(D_METHOD("create_mesh", "unique_id"), &ENetMultiplayerPeer::create_mesh);
ClassDB::bind_method(D_METHOD("add_mesh_peer", "peer_id", "host"), &ENetMultiplayerPeer::add_mesh_peer);
- ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100));
ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip);
- ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled);
- ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled);
ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host);
ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
}
@@ -664,7 +489,7 @@ ENetMultiplayerPeer::ENetMultiplayerPeer() {
ENetMultiplayerPeer::~ENetMultiplayerPeer() {
if (_is_active()) {
- close_connection();
+ close();
}
}
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index 3152068d46..f0d157cbf8 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_multiplayer_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ENET_MULTIPLAYER_PEER_H
#define ENET_MULTIPLAYER_PEER_H
@@ -47,10 +47,9 @@ private:
};
enum {
- SYSCH_CONFIG = 0,
- SYSCH_RELIABLE = 1,
- SYSCH_UNRELIABLE = 2,
- SYSCH_MAX = 3
+ SYSCH_RELIABLE = 0,
+ SYSCH_UNRELIABLE = 1,
+ SYSCH_MAX = 2
};
enum Mode {
@@ -66,8 +65,6 @@ private:
int target_peer = 0;
- bool server_relay = true;
-
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
HashMap<int, Ref<ENetConnection>> hosts;
@@ -77,18 +74,16 @@ private:
ENetPacket *packet = nullptr;
int from = 0;
int channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
};
List<Packet> incoming_packets;
Packet current_packet;
+ void _store_packet(int32_t p_source, ENetConnection::Event &p_event);
void _pop_current_packet();
- bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
- bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
- bool _parse_mesh_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event, int p_peer_id);
- void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet);
- void _notify_peers(int p_id, bool p_connected);
+ void _disconnect_inactive_peers();
void _destroy_unused(ENetPacket *p_packet);
_FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
@@ -99,10 +94,18 @@ protected:
public:
virtual void set_target_peer(int p_peer) override;
+
virtual int get_packet_peer() const override;
+ virtual TransferMode get_packet_mode() const override;
+ virtual int get_packet_channel() const override;
virtual void poll() override;
+ virtual void close() override;
+ virtual void disconnect_peer(int p_peer, bool p_force = false) override;
+
virtual bool is_server() const override;
+ virtual bool is_server_relay_supported() const override;
+
// Overridden so we can instrument the DTLSServer when needed.
virtual void set_refuse_new_connections(bool p_enabled) override;
@@ -120,13 +123,7 @@ public:
Error create_mesh(int p_id);
Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host);
- void close_connection(uint32_t wait_usec = 100);
-
- void disconnect_peer(int p_peer, bool now = false);
-
void set_bind_ip(const IPAddress &p_ip);
- void set_server_relay_enabled(bool p_enabled);
- bool is_server_relay_enabled() const;
Ref<ENetConnection> get_host() const;
Ref<ENetPacketPeer> get_peer(int p_id) const;
diff --git a/modules/enet/enet_packet_peer.cpp b/modules/enet/enet_packet_peer.cpp
index 62c0550edb..f64704c67d 100644
--- a/modules/enet/enet_packet_peer.cpp
+++ b/modules/enet/enet_packet_peer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_packet_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_packet_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "enet_packet_peer.h"
@@ -206,6 +206,8 @@ void ENetPacketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("send", "channel", "packet", "flags"), &ENetPacketPeer::_send);
ClassDB::bind_method(D_METHOD("throttle_configure", "interval", "acceleration", "deceleration"), &ENetPacketPeer::throttle_configure);
ClassDB::bind_method(D_METHOD("set_timeout", "timeout", "timeout_min", "timeout_max"), &ENetPacketPeer::set_timeout);
+ ClassDB::bind_method(D_METHOD("get_remote_address"), &ENetPacketPeer::get_remote_address);
+ ClassDB::bind_method(D_METHOD("get_remote_port"), &ENetPacketPeer::get_remote_port);
ClassDB::bind_method(D_METHOD("get_statistic", "statistic"), &ENetPacketPeer::get_statistic);
ClassDB::bind_method(D_METHOD("get_state"), &ENetPacketPeer::get_state);
ClassDB::bind_method(D_METHOD("get_channels"), &ENetPacketPeer::get_channels);
diff --git a/modules/enet/enet_packet_peer.h b/modules/enet/enet_packet_peer.h
index eaf70a276a..fe40d06188 100644
--- a/modules/enet/enet_packet_peer.h
+++ b/modules/enet/enet_packet_peer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* enet_packet_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* enet_packet_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ENET_PACKET_PEER_H
#define ENET_PACKET_PEER_H
diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp
index 14f3374e24..9c287a4f40 100644
--- a/modules/enet/register_types.cpp
+++ b/modules/enet/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/error/error_macros.h"
diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h
index b4f491287d..ae883fee12 100644
--- a/modules/enet/register_types.h
+++ b/modules/enet/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ENET_REGISTER_TYPES_H
#define ENET_REGISTER_TYPES_H
diff --git a/modules/etcpak/config.py b/modules/etcpak/config.py
index 53b8f2f2e3..eb565b85b9 100644
--- a/modules/etcpak/config.py
+++ b/modules/etcpak/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env["tools"]
+ return env.editor_build
def configure(env):
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 7d5557d197..a6aeec54cc 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -1,40 +1,40 @@
-/*************************************************************************/
-/* image_compress_etcpak.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_compress_etcpak.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_compress_etcpak.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "thirdparty/etcpak/ProcessDxtc.hpp"
-#include "thirdparty/etcpak/ProcessRGB.hpp"
+#include <ProcessDxtc.hpp>
+#include <ProcessRGB.hpp>
EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
switch (p_channels) {
@@ -111,13 +111,17 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
Image::Format target_format = Image::FORMAT_RGBA8;
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
target_format = Image::FORMAT_ETC;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
target_format = Image::FORMAT_ETC2_RGB8;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
target_format = Image::FORMAT_ETC2_RA_AS_RG;
r_img->convert_rg_to_ra_rgba8();
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
target_format = Image::FORMAT_ETC2_RGBA8;
+ r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
target_format = Image::FORMAT_DXT1;
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
@@ -126,7 +130,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
target_format = Image::FORMAT_DXT5;
} else {
- ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
+ ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
}
// Compress image data and (if required) mipmaps.
@@ -167,7 +171,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
const uint8_t *src_read = r_img->get_data().ptr();
- print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format)));
+ print_verbose(vformat("etcpak: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
Vector<uint8_t> dest_data;
@@ -219,21 +223,21 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
}
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
} else {
- ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
+ ERR_FAIL_MSG("etcpak: Invalid or unsupported compression format.");
}
}
// Replace original image with compressed one.
- r_img->create(width, height, mipmaps, target_format, dest_data);
+ r_img->set_data(width, height, mipmaps, target_format, dest_data);
- print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
+ print_verbose(vformat("etcpak: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
}
diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h
index 691528c235..8cb17b1c8a 100644
--- a/modules/etcpak/image_compress_etcpak.h
+++ b/modules/etcpak/image_compress_etcpak.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_compress_etcpak.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_compress_etcpak.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_COMPRESS_ETCPAK_H
#define IMAGE_COMPRESS_ETCPAK_H
diff --git a/modules/etcpak/register_types.cpp b/modules/etcpak/register_types.cpp
index eaad1e7b01..3133babdbb 100644
--- a/modules/etcpak/register_types.cpp
+++ b/modules/etcpak/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/etcpak/register_types.h b/modules/etcpak/register_types.h
index 2048a62737..2580ae2f97 100644
--- a/modules/etcpak/register_types.h
+++ b/modules/etcpak/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ETCPAK_REGISTER_TYPES_H
#define ETCPAK_REGISTER_TYPES_H
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index 4b2ea6faa5..0b86bc569f 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -58,22 +58,23 @@ if env["builtin_freetype"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- thirdparty_brotli_dir = "#thirdparty/brotli/"
- thirdparty_brotli_sources = [
- "common/constants.c",
- "common/context.c",
- "common/dictionary.c",
- "common/platform.c",
- "common/shared_dictionary.c",
- "common/transform.c",
- "dec/bit_reader.c",
- "dec/decode.c",
- "dec/huffman.c",
- "dec/state.c",
- ]
- thirdparty_sources += [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources]
- env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
- env_freetype.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"])
+ if env["brotli"]:
+ thirdparty_brotli_dir = "#thirdparty/brotli/"
+ thirdparty_brotli_sources = [
+ "common/constants.c",
+ "common/context.c",
+ "common/dictionary.c",
+ "common/platform.c",
+ "common/shared_dictionary.c",
+ "common/transform.c",
+ "dec/bit_reader.c",
+ "dec/decode.c",
+ "dec/huffman.c",
+ "dec/state.c",
+ ]
+ thirdparty_sources += [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources]
+ env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+ env_freetype.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"])
if env.get("use_ubsan") or env.get("use_asan") or env.get("use_tsan") or env.get("use_lsan") or env.get("use_msan"):
env_freetype.Append(CPPDEFINES=["BROTLI_BUILD_PORTABLE"])
@@ -89,7 +90,7 @@ if env["builtin_freetype"]:
env.Prepend(CPPPATH=[thirdparty_dir + "/include"])
env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", "FT_CONFIG_OPTION_SYSTEM_ZLIB"])
- if env["target"] == "debug":
+ if env.dev_build:
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
# Also requires libpng headers
@@ -98,7 +99,7 @@ if env["builtin_freetype"]:
sfnt = thirdparty_dir + "src/sfnt/sfnt.c"
# Must be done after all CPPDEFINES are being set so we can copy them.
- if env["platform"] == "javascript":
+ if env["platform"] == "web":
# Forcibly undefine this macro so SIMD is not used in this file,
# since currently unsupported in WASM
tmp_env = env_freetype.Clone()
diff --git a/modules/freetype/config.py b/modules/freetype/config.py
index d22f9454ed..c0586d5536 100644
--- a/modules/freetype/config.py
+++ b/modules/freetype/config.py
@@ -2,5 +2,13 @@ def can_build(env, platform):
return True
+def get_opts(platform):
+ from SCons.Variables import BoolVariable
+
+ return [
+ BoolVariable("brotli", "Enable Brotli decompressor for WOFF2 fonts support", True),
+ ]
+
+
def configure(env):
pass
diff --git a/modules/freetype/register_types.cpp b/modules/freetype/register_types.cpp
index a5a60c0368..70f277c3a4 100644
--- a/modules/freetype/register_types.cpp
+++ b/modules/freetype/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h
index 3399c0b3bc..8ed3995d88 100644
--- a/modules/freetype/register_types.h
+++ b/modules/freetype/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 FREETYPE_REGISTER_TYPES_H
#define FREETYPE_REGISTER_TYPES_H
diff --git a/modules/freetype/uwpdef.h b/modules/freetype/uwpdef.h
index f85f293171..52b839c9b4 100644
--- a/modules/freetype/uwpdef.h
+++ b/modules/freetype/uwpdef.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* uwpdef.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* uwpdef.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 UWPDEF_H
#define UWPDEF_H
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 2f507db548..1dc4768186 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -7,7 +7,7 @@ env_gdscript = env_modules.Clone()
env_gdscript.add_source_files(env.modules_sources, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp")
SConscript("editor/script_templates/SCsub")
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 61ce6185a5..a7d5c406e9 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ env.module_add_dependencies("gdscript", ["jsonrpc", "websocket"], True)
return True
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 10cf783e73..3fe741a582 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -8,48 +8,47 @@
For the list of the global functions and constants see [@GlobalScope].
</description>
<tutorials>
+ <link title="GDScript exports">$DOCS_URL/tutorials/scripting/gdscript/gdscript_exports.html</link>
</tutorials>
<methods>
<method name="Color8">
<return type="Color" />
- <argument index="0" name="r8" type="int" />
- <argument index="1" name="g8" type="int" />
- <argument index="2" name="b8" type="int" />
- <argument index="3" name="a8" type="int" default="255" />
+ <param index="0" name="r8" type="int" />
+ <param index="1" name="g8" type="int" />
+ <param index="2" name="b8" type="int" />
+ <param index="3" name="a8" type="int" default="255" />
<description>
- Returns a color constructed from integer red, green, blue, and alpha channels. Each channel should have 8 bits of information ranging from 0 to 255.
- [code]r8[/code] red channel
- [code]g8[/code] green channel
- [code]b8[/code] blue channel
- [code]a8[/code] alpha channel
+ Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value.
[codeblock]
- red = Color8(255, 0, 0)
+ var red = Color8(255, 0, 0) # Same as Color(1, 0, 0)
+ var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2).
+ var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4).
[/codeblock]
</description>
</method>
<method name="assert">
<return type="void" />
- <argument index="0" name="condition" type="bool" />
- <argument index="1" name="message" type="String" default="&quot;&quot;" />
+ <param index="0" name="condition" type="bool" />
+ <param index="1" name="message" type="String" default="&quot;&quot;" />
<description>
- Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
- [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
- The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
+ Asserts that the [param condition] is [code]true[/code]. If the [param condition] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
+ An optional [param message] can be shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
+ [b]Warning:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
[codeblock]
# Imagine we always want speed to be between 0 and 20.
var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
- assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
+ assert(speed &lt; 20, "the speed limit is 20") # Show a message
[/codeblock]
</description>
</method>
<method name="char">
<return type="String" />
- <argument index="0" name="char" type="int" />
+ <param index="0" name="char" type="int" />
<description>
- Returns a character as a String of the given Unicode code point (which is compatible with ASCII code).
+ Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code).
[codeblock]
a = char(65) # a is "A"
a = char(65 + 32) # a is "a"
@@ -59,31 +58,31 @@
</method>
<method name="convert">
<return type="Variant" />
- <argument index="0" name="what" type="Variant" />
- <argument index="1" name="type" type="int" />
+ <param index="0" name="what" type="Variant" />
+ <param index="1" name="type" type="int" />
<description>
- Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the [enum Variant.Type] values.
+ Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values.
[codeblock]
- a = Vector2(1, 0)
- # Prints 1
- print(a.length())
- a = convert(a, TYPE_STRING)
- # Prints 6 as "(1, 0)" is 6 characters
- print(a.length())
+ var a = [4, 2.5, 1.2]
+ print(a is Array) # Prints true
+
+ var b = convert(a, TYPE_PACKED_BYTE_ARRAY)
+ print(b) # Prints [4, 2, 1]
+ print(b is Array) # Prints false
[/codeblock]
</description>
</method>
- <method name="dict2inst">
+ <method name="dict_to_inst">
<return type="Object" />
- <argument index="0" name="dictionary" type="Dictionary" />
+ <param index="0" name="dictionary" type="Dictionary" />
<description>
- Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing.
+ Converts a [param dictionary] (created with [method inst_to_dict]) back to an Object instance. Can be useful for deserializing.
</description>
</method>
<method name="get_stack">
<return type="Array" />
<description>
- Returns an array of dictionaries representing the current call stack.
+ Returns an array of dictionaries representing the current call stack. See also [method print_stack].
[codeblock]
func _ready():
foo()
@@ -94,22 +93,24 @@
func bar():
print(get_stack())
[/codeblock]
- would print
+ Starting from [code]_ready()[/code], [code]bar()[/code] would print:
[codeblock]
[{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}]
[/codeblock]
- [b]Note:[/b] Not supported for calling from threads. Instead, this will return an empty array.
+ [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method get_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server.
+ [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will return an empty array.
</description>
</method>
- <method name="inst2dict">
+ <method name="inst_to_dict">
<return type="Dictionary" />
- <argument index="0" name="instance" type="Object" />
+ <param index="0" name="instance" type="Object" />
<description>
- Returns the passed instance converted to a dictionary (useful for serializing).
+ Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing.
+ [b]Note:[/b] Cannot be used to serialize objects with built-in scripts attached or objects allocated within built-in scripts.
[codeblock]
var foo = "bar"
func _ready():
- var d = inst2dict(self)
+ var d = inst_to_dict(self)
print(d.keys())
print(d.values())
[/codeblock]
@@ -122,39 +123,41 @@
</method>
<method name="len">
<return type="int" />
- <argument index="0" name="var" type="Variant" />
+ <param index="0" name="var" type="Variant" />
<description>
- Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc.
- [b]Note:[/b] Generates a fatal error if Variant can not provide a length.
+ Returns the length of the given Variant [param var]. The length can be the character count of a [String], the element count of any array type or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped.
[codeblock]
a = [1, 2, 3, 4]
len(a) # Returns 4
+
+ b = "Hello!"
+ len(b) # Returns 6
[/codeblock]
</description>
</method>
<method name="load">
<return type="Resource" />
- <argument index="0" name="path" type="String" />
+ <param index="0" name="path" type="String" />
<description>
- Loads a resource from the filesystem located at [code]path[/code]. The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), which might cause slight delay, especially when loading scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
+ Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload].
[b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
- # Load a scene called main located in the root of the project directory and cache it in a variable.
+ # Load a scene called "main" located in the root of the project directory and cache it in a variable.
var main = load("res://main.tscn") # main will contain a PackedScene resource.
[/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].
+ [b]Important:[/b] The path must be absolute. A relative path will always return [code]null[/code].
+ This function is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios.
+ [b]Note:[/b] Files have to be imported into the engine first to load them using this function. 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">
<return type="Resource" />
- <argument index="0" name="path" type="String" />
+ <param index="0" name="path" type="String" />
<description>
- Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load].
+ Returns a [Resource] from the filesystem located at [param path]. During run-time, the resource is loaded when the script is being parsed. This function effectively acts as a reference to that resource. Note that this function requires [param path] to be a constant [String]. If you want to load a resource from a dynamic/variable path, use [method load].
[b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
- # Instance a scene.
+ # Create instance of a scene.
var diamond = preload("res://diamond.tscn").instantiate()
[/codeblock]
</description>
@@ -163,23 +166,24 @@
<return type="void" />
<description>
Like [method @GlobalScope.print], but includes the current stack frame when running with the debugger turned on.
- Output in the console would look something like this:
+ The output in the console may look like the following:
[codeblock]
Test print
- At: res://test.gd:15:_process()
+ At: res://test.gd:15:_process()
[/codeblock]
- [b]Note:[/b] Not supported for calling from threads. Instead of the stack frame, this will print the thread ID.
+ [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID.
</description>
</method>
<method name="print_stack">
<return type="void" />
<description>
- Prints a stack trace at the current code location. Only works when running with debugger turned on.
- Output in the console would look something like this:
+ Prints a stack trace at the current code location. See also [method get_stack].
+ The output in the console may look like the following:
[codeblock]
Frame 0 - res://test.gd:16 in function '_process'
[/codeblock]
- [b]Note:[/b] Not supported for calling from threads. Instead of the stack trace, this will print the thread ID.
+ [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method print_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server.
+ [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID.
</description>
</method>
<method name="range" qualifiers="vararg">
@@ -226,7 +230,7 @@
<method name="str" qualifiers="vararg">
<return type="String" />
<description>
- Converts one or more arguments to string in the best way possible.
+ Converts one or more arguments to a [String] in the best way possible.
[codeblock]
var a = [10, 20, 30]
var b = str(a);
@@ -237,8 +241,13 @@
</method>
<method name="type_exists">
<return type="bool" />
- <argument index="0" name="type" type="StringName" />
+ <param index="0" name="type" type="StringName" />
<description>
+ Returns [code]true[/code] if the given [Object]-derived class exists in [ClassDB]. Note that [Variant] data types are not registered in [ClassDB].
+ [codeblock]
+ type_exists("Sprite2D") # Returns true
+ type_exists("NonExistentClass") # Returns false
+ [/codeblock]
</description>
</method>
</methods>
@@ -251,168 +260,347 @@
</constant>
<constant name="INF" value="inf">
Positive floating-point infinity. This is the result of floating-point division when the divisor is [code]0.0[/code]. For negative infinity, use [code]-INF[/code]. Dividing by [code]-0.0[/code] will result in negative infinity if the numerator is positive, so dividing by [code]0.0[/code] is not the same as dividing by [code]-0.0[/code] (despite [code]0.0 == -0.0[/code] returning [code]true[/code]).
- [b]Note:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead.
+ [b]Warning:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead.
</constant>
<constant name="NAN" value="nan">
"Not a Number", an invalid floating-point value. [constant NAN] has special properties, including that it is not equal to itself ([code]NAN == NAN[/code] returns [code]false[/code]). It is output by some invalid operations, such as dividing floating-point [code]0.0[/code] by [code]0.0[/code].
- [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.
+ [b]Warning:[/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>
+ Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property use the type hint notation.
+ [codeblock]
+ @export var int_number = 5
+ @export var float_number: float = 5
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_category">
<return type="void" />
- <argument index="0" name="name" type="String" />
+ <param index="0" name="name" type="String" />
<description>
+ Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
+ See also [constant PROPERTY_USAGE_CATEGORY].
+ [codeblock]
+ @export_category("My Properties")
+ @export var number = 3
+ @export var string = ""
+ [/codeblock]
+ [b]Note:[/b] Categories in the property list are supposed to indicate different base types, so the use of this annotation is not encouraged. See [annotation @export_group] and [annotation @export_subgroup] instead.
</description>
</annotation>
<annotation name="@export_color_no_alpha">
<return type="void" />
<description>
+ Export a [Color] property without transparency (its alpha fixed as [code]1.0[/code]).
+ See also [constant PROPERTY_HINT_COLOR_NO_ALPHA].
+ [codeblock]
+ @export_color_no_alpha var modulate_color: Color
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_dir">
<return type="void" />
<description>
+ Export a [String] property as a path to a directory. The path will be limited to the project folder and its subfolders. See [annotation @export_global_dir] to allow picking from the entire filesystem.
+ See also [constant PROPERTY_HINT_DIR].
+ [codeblock]
+ @export_dir var sprite_folder: String
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_enum" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="names" type="String" />
+ <param index="0" name="names" type="String" />
<description>
+ Export a [String] or integer property as an enumerated list of options. If the property is an integer field, then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon.
+ See also [constant PROPERTY_HINT_ENUM].
+ [codeblock]
+ @export_enum("Rebecca", "Mary", "Leah") var character_name: String
+ @export_enum("Warrior", "Magician", "Thief") var character_class: int
+ @export_enum("Slow:30", "Average:60", "Very Fast:200") var character_speed: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_exp_easing" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="hints" type="String" default="&quot;&quot;" />
+ <param index="0" name="hints" type="String" default="&quot;&quot;" />
<description>
+ Export a floating-point property with an easing editor widget. Additional hints can be provided to adjust the behavior of the widget. [code]"attenuation"[/code] flips the curve, which makes it more intuitive for editing attenuation properties. [code]"positive_only"[/code] limits values to only be greater than or equal to zero.
+ See also [constant PROPERTY_HINT_EXP_EASING].
+ [codeblock]
+ @export_exp_easing var transition_speed
+ @export_exp_easing("attenuation") var fading_attenuation
+ @export_exp_easing("positive_only") var effect_power
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_file" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <param index="0" name="filter" type="String" default="&quot;&quot;" />
<description>
+ Export a [String] property as a path to a file. The path will be limited to the project folder and its subfolders. See [annotation @export_global_file] to allow picking from the entire filesystem.
+ If [param filter] is provided, only matching files will be available for picking.
+ See also [constant PROPERTY_HINT_FILE].
+ [codeblock]
+ @export_file var sound_effect_file: String
+ @export_file("*.txt") var notes_file: String
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="names" type="String" />
+ <param index="0" name="names" type="String" />
<description>
+ Export an integer property as a bit flag field. This allows to store several "checked" or [code]true[/code] values with one property, and comfortably select them from the Inspector dock.
+ See also [constant PROPERTY_HINT_FLAGS].
+ [codeblock]
+ @export_flags("Fire", "Water", "Earth", "Wind") var spell_elements = 0
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_2d_navigation">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 2D navigation layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_navigation/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_2D_NAVIGATION].
+ [codeblock]
+ @export_flags_2d_navigation var navigation_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_2d_physics">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 2D physics layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_physics/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_2D_PHYSICS].
+ [codeblock]
+ @export_flags_2d_physics var physics_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_2d_render">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 2D render layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/2d_render/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_2D_RENDER].
+ [codeblock]
+ @export_flags_2d_render var render_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_3d_navigation">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 3D navigation layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_navigation/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_3D_NAVIGATION].
+ [codeblock]
+ @export_flags_3d_navigation var navigation_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_3d_physics">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 3D physics layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_physics/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_3D_PHYSICS].
+ [codeblock]
+ @export_flags_3d_physics var physics_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_3d_render">
<return type="void" />
<description>
+ Export an integer property as a bit flag field for 3D render layers. The widget in the Inspector dock will use the layer names defined in [member ProjectSettings.layer_names/3d_render/layer_1].
+ See also [constant PROPERTY_HINT_LAYERS_3D_RENDER].
+ [codeblock]
+ @export_flags_3d_render var render_layers: int
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_global_dir">
<return type="void" />
<description>
+ Export a [String] property as a path to a directory. The path can be picked from the entire filesystem. See [annotation @export_dir] to limit it to the project folder and its subfolders.
+ See also [constant PROPERTY_HINT_GLOBAL_DIR].
+ [codeblock]
+ @export_global_dir var sprite_folder: String
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_global_file" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <param index="0" name="filter" type="String" default="&quot;&quot;" />
<description>
+ Export a [String] property as a path to a file. The path can be picked from the entire filesystem. See [annotation @export_file] to limit it to the project folder and its subfolders.
+ If [param filter] is provided, only matching files will be available for picking.
+ See also [constant PROPERTY_HINT_GLOBAL_FILE].
+ [codeblock]
+ @export_global_file var sound_effect_file: String
+ @export_global_file("*.txt") var notes_file: String
+ [/codeblock]
</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;" />
+ <param index="0" name="name" type="String" />
+ <param index="1" name="prefix" type="String" default="&quot;&quot;" />
<description>
+ Define a new group for the following exported properties. This helps to organize properties in the Inspector dock. Groups can be added with an optional [param prefix], which would make group to only consider properties that have this prefix. The grouping will break on the first property that doesn't have a prefix. The prefix is also removed from the property's name in the Inspector dock.
+ If no [param prefix] is provided, the every following property is added to the group. The group ends when then next group or category is defined. You can also force end a group by using this annotation with empty strings for parameters, [code]@export_group("", "")[/code].
+ Groups cannot be nested, use [annotation @export_subgroup] to add subgroups within groups.
+ See also [constant PROPERTY_USAGE_GROUP].
+ [codeblock]
+ @export_group("My Properties")
+ @export var number = 3
+ @export var string = ""
+
+ @export_group("Prefixed Properties", "prefix_")
+ @export var prefix_number = 3
+ @export var prefix_string = ""
+
+ @export_group("", "")
+ @export var ungrouped_number = 3
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_multiline">
<return type="void" />
<description>
+ Export a [String] property with a large [TextEdit] widget instead of a [LineEdit]. This adds support for multiline content and makes it easier to edit large amount of text stored in the property.
+ See also [constant PROPERTY_HINT_MULTILINE_TEXT].
+ [codeblock]
+ @export_multiline var character_biography
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_node_path" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="type" type="String" default="&quot;&quot;" />
+ <param index="0" name="type" type="String" default="&quot;&quot;" />
<description>
+ Export a [NodePath] property with a filter for allowed node types.
+ See also [constant PROPERTY_HINT_NODE_PATH_VALID_TYPES].
+ [codeblock]
+ @export_node_path(Button, TouchScreenButton) var some_button
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_placeholder">
<return type="void" />
+ <param index="0" name="placeholder" type="String" />
<description>
+ Export a [String] property with a placeholder text displayed in the editor widget when no value is present.
+ See also [constant PROPERTY_HINT_PLACEHOLDER_TEXT].
+ [codeblock]
+ @export_placeholder("Name in lowercase") var character_id: String
+ [/codeblock]
</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>
+ <param index="0" name="min" type="float" />
+ <param index="1" name="max" type="float" />
+ <param index="2" name="step" type="float" default="1.0" />
+ <param index="3" name="extra_hints" type="String" default="&quot;&quot;" />
+ <description>
+ Export a numeric property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting.
+ If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget.
+ Hints also allow to indicate the units for the edited value. Using [code]"radians"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock. [code]"degrees"[/code] allows to add a degree sign as a unit suffix. Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string.
+ See also [constant PROPERTY_HINT_RANGE].
+ [codeblock]
+ @export_range(0, 20) var number
+ @export_range(-10, 20) var number
+ @export_range(-10, 20, 0.2) var number: float
+
+ @export_range(0, 100, 1, "or_greater") var power_percent
+ @export_range(0, 100, 1, "or_greater", "or_less") var health_delta
+
+ @export_range(-3.14, 3.14, 0.001, "radians") var angle_radians
+ @export_range(0, 360, 1, "degrees") var angle_degrees
+ @export_range(-8, 8, 2, "suffix:px") var target_offset
+ [/codeblock]
</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;" />
+ <param index="0" name="name" type="String" />
+ <param index="1" name="prefix" type="String" default="&quot;&quot;" />
<description>
+ Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock. Subgroups work exactly like groups, except they need a parent group to exist. See [annotation @export_group].
+ See also [constant PROPERTY_USAGE_SUBGROUP].
+ [codeblock]
+ @export_group("My Properties")
+ @export var number = 3
+ @export var string = ""
+
+ @export_subgroup("My Prefixed Properties", "prefix_")
+ @export var prefix_number = 3
+ @export var prefix_string = ""
+ [/codeblock]
+ [b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
</description>
</annotation>
<annotation name="@icon">
<return type="void" />
- <argument index="0" name="icon_path" type="String" />
+ <param index="0" name="icon_path" type="String" />
<description>
+ Add a custom icon to the current script. The script must be registered as a global class using the [code]class_name[/code] keyword for this to have a visible effect. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs.
+ [codeblock]
+ @icon("res://path/to/class/icon.svg")
+ [/codeblock]
+ [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported.
+ [b]Note:[/b] As annotations describe their subject, the [code]@icon[/code] annotation must be placed before the class definition and inheritance.
</description>
</annotation>
<annotation name="@onready">
<return type="void" />
<description>
+ Mark the following property as assigned on [Node]'s ready state change. Values for these properties are not assigned immediately upon the node's creation, and instead are computed and stored right before [method Node._ready].
+ [codeblock]
+ @onready var character_name: Label = $Label
+ [/codeblock]
</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" />
+ <param index="0" name="mode" type="String" default="&quot;&quot;" />
+ <param index="1" name="sync" type="String" default="&quot;&quot;" />
+ <param index="2" name="transfer_mode" type="String" default="&quot;&quot;" />
+ <param index="3" name="transfer_channel" type="int" default="0" />
<description>
+ Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url].
+ [codeblock]
+ @rpc()
+ [/codeblock]
</description>
</annotation>
<annotation name="@tool">
<return type="void" />
<description>
+ Mark the current script as a tool script, allowing it to be loaded and executed by the editor. See [url=$DOCS_URL/tutorials/plugins/running_code_in_the_editor.html]Running code in the editor[/url].
+ [codeblock]
+ @tool
+ extends Node
+ [/codeblock]
+ [b]Note:[/b] As annotations describe their subject, the [code]@tool[/code] annotation must be placed before the class definition and inheritance.
</description>
</annotation>
<annotation name="@warning_ignore" qualifiers="vararg">
<return type="void" />
- <argument index="0" name="warning" type="String" />
+ <param index="0" name="warning" type="String" />
<description>
+ Mark the following statement to ignore the specified [param warning]. See [url=$DOCS_URL/tutorials/scripting/gdscript/warning_system.html]GDScript warning system[/url].
+ [codeblock]
+ func test():
+ print("hello")
+ return
+ @warning_ignore("unreachable_code")
+ print("unreachable")
+ [/codeblock]
</description>
</annotation>
</annotations>
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 578e7a34f3..1a102bd16f 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -4,19 +4,13 @@
A script implemented in the GDScript programming language.
</brief_description>
<description>
- A script implemented in the GDScript programming language. The script extends the functionality of all objects that instance it.
+ A script implemented in the GDScript programming language. The script extends the functionality of all objects that instantiate it.
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
<link title="GDScript documentation index">$DOCS_URL/tutorials/scripting/gdscript/index.html</link>
</tutorials>
<methods>
- <method name="get_as_byte_code" qualifiers="const">
- <return type="PackedByteArray" />
- <description>
- Returns byte code for the script source code.
- </description>
- </method>
<method name="new" qualifiers="vararg">
<return type="Variant" />
<description>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 4372bb33ba..23be913a24 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_highlighter.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_highlighter.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_highlighter.h"
#include "../gdscript.h"
@@ -39,28 +39,32 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
Type next_type = NONE;
Type current_type = NONE;
- Type previous_type = NONE;
-
- String previous_text = "";
- int previous_column = 0;
+ Type prev_type = NONE;
+ String prev_text = "";
+ int prev_column = 0;
bool prev_is_char = false;
- bool prev_is_number = false;
+ bool prev_is_digit = false;
+ bool prev_is_binary_op = false;
+
bool in_keyword = false;
bool in_word = false;
- bool in_function_name = false;
- bool in_lambda = false;
- bool in_variable_declaration = false;
- bool in_signal_declaration = false;
- bool in_function_args = false;
- bool in_member_variable = false;
+ bool in_number = 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 in_member_variable = false;
+ bool in_lambda = false;
+
+ bool in_function_name = false;
+ bool in_function_args = false;
+ bool in_variable_declaration = false;
+ bool in_signal_declaration = false;
bool expect_type = false;
+
Color keyword_color;
Color color;
@@ -84,16 +88,17 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
const int line_length = str.length();
Color prev_color;
- if (in_region != -1 && str.length() == 0) {
+ if (in_region != -1 && line_length == 0) {
color_region_cache[p_line] = in_region;
}
- for (int j = 0; j < str.length(); j++) {
+ for (int j = 0; j < line_length; j++) {
Dictionary highlighter_info;
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = is_digit(str[j]);
+ bool is_a_digit = is_digit(str[j]);
+ bool is_binary_op = false;
/* color regions */
if (is_a_symbol || in_region != -1) {
@@ -110,10 +115,10 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (from != line_length) {
- /* check if we are in entering a region */
+ // Check if we are in entering a region.
if (in_region == -1) {
for (int c = 0; c < color_regions.size(); c++) {
- /* check there is enough room */
+ // Check there is enough room.
int chars_left = line_length - from;
int start_key_length = color_regions[c].start_key.length();
int end_key_length = color_regions[c].end_key.length();
@@ -121,7 +126,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
continue;
}
- /* search the line */
+ // Search the line.
bool match = true;
const char32_t *start_key = color_regions[c].start_key.get_data();
for (int k = 0; k < start_key_length; k++) {
@@ -136,7 +141,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_region = c;
from += start_key_length;
- /* check if it's the whole line */
+ // Check if it's the whole line.
if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
if (from + end_key_length > line_length) {
// If it's key length and there is a '\', dont skip to highlight esc chars.
@@ -161,7 +166,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- /* if we are in one find the end key */
+ // If we are in one, find the end key.
if (in_region != -1) {
Color region_color = color_regions[in_region].color;
if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) {
@@ -178,7 +183,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
highlighter_info["color"] = region_color;
color_map[j] = highlighter_info;
- /* search the line */
+ // Search the line.
int region_end_index = -1;
int end_key_length = color_regions[in_region].end_key.length();
const char32_t *end_key = color_regions[in_region].end_key.get_data();
@@ -221,9 +226,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- previous_type = REGION;
- previous_text = "";
- previous_column = j;
+ prev_type = REGION;
+ prev_text = "";
+ prev_column = j;
j = from + (end_key_length - 1);
if (region_end_index == -1) {
color_region_cache[p_line] = in_region;
@@ -231,68 +236,112 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_region = -1;
prev_is_char = false;
- prev_is_number = false;
+ prev_is_digit = false;
+ prev_is_binary_op = false;
continue;
}
}
}
- // allow ABCDEF in hex notation
- if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
- is_number = true;
+ // VERY hacky... but couldn't come up with anything better.
+ if (j > 0 && (str[j] == '&' || str[j] == '^' || str[j] == '%' || str[j] == '+' || str[j] == '-' || str[j] == '~' || str[j] == '.')) {
+ int to = j - 1;
+ // Find what the last text was (prev_text won't work if there's no whitespace, so we need to do it manually).
+ while (to > 0 && is_whitespace(str[to])) {
+ to--;
+ }
+ int from = to;
+ while (from > 0 && !is_symbol(str[from])) {
+ from--;
+ }
+ String word = str.substr(from + 1, to - from);
+ // Keywords need to be exceptions, except for keywords that represent a value.
+ if (word == "true" || word == "false" || word == "null" || word == "PI" || word == "TAU" || word == "INF" || word == "NAN" || word == "self" || word == "super" || !reserved_keywords.has(word)) {
+ if (!is_symbol(str[to]) || str[to] == '"' || str[to] == '\'' || str[to] == ')' || str[to] == ']' || str[to] == '}') {
+ is_binary_op = true;
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ // Allow ABCDEF in hex notation.
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_a_digit)) {
+ is_a_digit = true;
} else {
is_hex_notation = false;
}
- // disallow anything not a 0 or 1
- if (is_bin_notation && (is_binary_digit(str[j]))) {
- is_number = true;
- } else if (is_bin_notation) {
- is_bin_notation = false;
- is_number = false;
- } else {
+ // Disallow anything not a 0 or 1 in binary notation.
+ if (is_bin_notation && !is_binary_digit(str[j])) {
+ is_a_digit = false;
is_bin_notation = false;
}
- // check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
- if ((str[j] == '.' || str[j] == 'x' || str[j] == 'b' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
- is_number = true;
- is_a_symbol = false;
- is_char = false;
+ if (!in_number && !in_word && is_a_digit) {
+ in_number = true;
+ }
- if (str[j] == 'x' && str[j - 1] == '0') {
- is_hex_notation = true;
- } else if (str[j] == 'b' && str[j - 1] == '0') {
+ // Special cases for numbers.
+ if (in_number && !is_a_digit) {
+ if (str[j] == 'b' && str[j - 1] == '0') {
is_bin_notation = true;
+ } else if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ } else if (!((str[j] == '-' || str[j] == '+') && str[j - 1] == 'e' && !prev_is_digit) &&
+ !(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) &&
+ !(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) &&
+ !(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) &&
+ !((str[j] == '-' || str[j] == '+' || str[j] == '~') && !prev_is_binary_op && str[j - 1] != 'e')) {
+ /* This condition continues Number highlighting in special cases.
+ 1st row: '+' or '-' after scientific notation;
+ 2nd row: '_' as a numeric separator;
+ 3rd row: Scientific notation 'e' and floating points;
+ 4th row: Floating points inside the number, or leading if after a unary mathematical operator;
+ 5th row: Multiple unary mathematical operators */
+ in_number = false;
}
+ } else if (!is_binary_op && (str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.'))))) {
+ in_number = true;
}
- if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !in_number) {
in_word = true;
}
- if ((in_keyword || in_word) && !is_hex_notation) {
- is_number = false;
- }
-
if (is_a_symbol && str[j] != '.' && in_word) {
in_word = false;
}
- if (!is_char) {
- in_keyword = false;
- }
-
if (!in_keyword && is_char && !prev_is_char) {
int to = j;
- while (to < str.length() && !is_symbol(str[to])) {
+ while (to < line_length && !is_symbol(str[to])) {
to++;
}
String word = str.substr(j, to - j);
- Color col = Color();
- if (keywords.has(word)) {
- col = keywords[word];
+ Color col;
+ if (global_functions.has(word)) {
+ // "assert" and "preload" are reserved, so highlight even if not followed by a bracket.
+ if (word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::ASSERT) || word == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::PRELOAD)) {
+ col = global_function_color;
+ } else {
+ // For other global functions, check if followed by bracket.
+ int k = to;
+ while (k < line_length && is_whitespace(str[k])) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ col = global_function_color;
+ }
+ }
+ } else if (class_names.has(word)) {
+ col = class_names[word];
+ } else if (reserved_keywords.has(word)) {
+ col = reserved_keywords[word];
} else if (member_keywords.has(word)) {
col = member_keywords[word];
}
@@ -300,12 +349,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
if (col != Color()) {
for (int k = j - 1; k >= 0; k--) {
if (str[k] == '.') {
- col = Color(); // keyword & member indexing not allowed
+ col = Color(); // Keyword, member & global func indexing not allowed.
break;
} else if (str[k] > 32) {
break;
}
}
+
if (col != Color()) {
in_keyword = true;
keyword_color = col;
@@ -314,29 +364,29 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
if (!in_function_name && in_word && !in_keyword) {
- if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::SIGNAL)) {
+ if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::SIGNAL)) {
in_signal_declaration = true;
} else {
int k = j;
- while (k < str.length() && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ while (k < line_length && !is_symbol(str[k]) && !is_whitespace(str[k])) {
k++;
}
- // check for space between name and bracket
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ // Check for space between name and bracket.
+ while (k < line_length && is_whitespace(str[k])) {
k++;
}
if (str[k] == '(') {
in_function_name = true;
- } else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) {
+ } else if (prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::VAR)) {
in_variable_declaration = true;
}
// Check for lambda.
- if (in_function_name && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ if (in_function_name && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
k = j - 1;
- while (k > 0 && (str[k] == '\t' || str[k] == ' ')) {
+ while (k > 0 && is_whitespace(str[k])) {
k--;
}
@@ -347,9 +397,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ if (!in_function_name && !in_member_variable && !in_keyword && !in_number && in_word) {
int k = j;
- while (k > 0 && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ while (k > 0 && !is_symbol(str[k]) && !is_whitespace(str[k])) {
k--;
}
@@ -367,18 +417,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_function_args = false;
}
- if (expect_type && (prev_is_char || str[j] == '=')) {
+ if (expect_type && (prev_is_char || str[j] == '=') && str[j] != '[') {
expect_type = false;
}
- if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
+ if (j > 0 && str[j - 1] == '-' && str[j] == '>') {
expect_type = true;
}
if (in_variable_declaration || in_function_args) {
int k = j;
// Skip space
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ while (k < line_length && is_whitespace(str[k])) {
k++;
}
@@ -395,15 +445,28 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_member_variable = false;
}
- if (!in_node_path && in_region == -1 && (str[j] == '^')) {
+ // Keep symbol color for binary '&&'. In the case of '&&&' use StringName color for the last ampersand.
+ if (!in_string_name && in_region == -1 && str[j] == '&' && !is_binary_op) {
+ if (j >= 2 && str[j - 1] == '&' && str[j - 2] != '&' && prev_is_binary_op) {
+ is_binary_op = true;
+ } else if (j == 0 || (j > 0 && str[j - 1] != '&') || prev_is_binary_op) {
+ in_string_name = true;
+ }
+ } else if (in_region != -1 || is_a_symbol) {
+ in_string_name = false;
+ }
+
+ // '^^' has no special meaning, so unlike StringName, when binary, use NodePath color for the last caret.
+ if (!in_node_path && in_region == -1 && str[j] == '^' && !is_binary_op && (j == 0 || (j > 0 && str[j - 1] != '^') || prev_is_binary_op)) {
in_node_path = true;
- } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) {
+ } else if (in_region != -1 || is_a_symbol) {
in_node_path = false;
}
- if (!in_node_ref && in_region == -1 && (str[j] == '$' || str[j] == '%')) {
+ if (!in_node_ref && in_region == -1 && (str[j] == '$' || (str[j] == '%' && !is_binary_op))) {
in_node_ref = true;
- } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) {
+ } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%') || (is_a_digit && j > 0 && (str[j - 1] == '$' || str[j - 1] == '/' || str[j - 1] == '%'))) {
+ // NodeRefs can't start with digits, so point out wrong syntax immediately.
in_node_ref = false;
}
@@ -413,16 +476,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
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) {
+ if (in_node_ref) {
next_type = NODE_REF;
color = node_ref_color;
} else if (in_annotation) {
@@ -431,6 +485,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
} else if (in_string_name) {
next_type = STRING_NAME;
color = string_name_color;
+ } else if (in_node_path) {
+ next_type = NODE_PATH;
+ color = node_path_color;
} else if (in_keyword) {
next_type = KEYWORD;
color = keyword_color;
@@ -444,17 +501,17 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
} else if (in_function_name) {
next_type = FUNCTION;
- if (!in_lambda && previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
+ if (!in_lambda && prev_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::Token::FUNC)) {
color = function_definition_color;
} else {
color = function_color;
}
+ } else if (in_number) {
+ next_type = NUMBER;
+ color = number_color;
} else if (is_a_symbol) {
next_type = SYMBOL;
color = symbol_color;
- } else if (is_number) {
- next_type = NUMBER;
- color = number_color;
} else if (expect_type) {
next_type = TYPE;
color = type_color;
@@ -466,27 +523,28 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
if (current_type == NONE) {
current_type = next_type;
} else {
- previous_type = current_type;
+ prev_type = current_type;
current_type = next_type;
- // no need to store regions...
- if (previous_type == REGION) {
- previous_text = "";
- previous_column = j;
+ // No need to store regions...
+ if (prev_type == REGION) {
+ prev_text = "";
+ prev_column = j;
} else {
- String text = str.substr(previous_column, j - previous_column).strip_edges();
- previous_column = j;
+ String text = str.substr(prev_column, j - prev_column).strip_edges();
+ prev_column = j;
- // ignore if just whitespace
+ // Ignore if just whitespace.
if (!text.is_empty()) {
- previous_text = text;
+ prev_text = text;
}
}
}
}
prev_is_char = is_char;
- prev_is_number = is_number;
+ prev_is_digit = is_a_digit;
+ prev_is_binary_op = is_binary_op;
if (color != prev_color) {
prev_color = color;
@@ -501,15 +559,17 @@ String GDScriptSyntaxHighlighter::_get_name() const {
return "GDScript";
}
-Array GDScriptSyntaxHighlighter::_get_supported_languages() const {
- Array languages;
+PackedStringArray GDScriptSyntaxHighlighter::_get_supported_languages() const {
+ PackedStringArray languages;
languages.push_back("GDScript");
return languages;
}
void GDScriptSyntaxHighlighter::_update_cache() {
- keywords.clear();
+ class_names.clear();
+ reserved_keywords.clear();
member_keywords.clear();
+ global_functions.clear();
color_regions.clear();
color_region_cache.clear();
@@ -524,7 +584,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<StringName> types;
ClassDB::get_class_list(&types);
for (const StringName &E : types) {
- keywords[E] = types_color;
+ class_names[E] = types_color;
}
/* User types. */
@@ -532,14 +592,14 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
- keywords[E] = usertype_color;
+ class_names[E] = usertype_color;
}
/* Autoloads. */
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;
+ class_names[info.name] = usertype_color;
}
}
@@ -550,7 +610,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> core_types;
gdscript->get_core_type_words(&core_types);
for (const String &E : core_types) {
- keywords[StringName(E)] = basetype_color;
+ class_names[StringName(E)] = basetype_color;
}
/* Reserved words. */
@@ -560,12 +620,23 @@ void GDScriptSyntaxHighlighter::_update_cache() {
gdscript->get_reserved_words(&keyword_list);
for (const String &E : keyword_list) {
if (gdscript->is_control_flow_keyword(E)) {
- keywords[StringName(E)] = control_flow_keyword_color;
+ reserved_keywords[StringName(E)] = control_flow_keyword_color;
} else {
- keywords[StringName(E)] = keyword_color;
+ reserved_keywords[StringName(E)] = keyword_color;
}
}
+ /* Global functions. */
+ List<StringName> global_function_list;
+ GDScriptUtilityFunctions::get_function_list(&global_function_list);
+ Variant::get_utility_function_list(&global_function_list);
+ // "assert" and "preload" are not utility functions, but are global nonetheless, so insert them.
+ global_functions.insert(SNAME("assert"));
+ global_functions.insert(SNAME("preload"));
+ for (const StringName &E : global_function_list) {
+ global_functions.insert(E);
+ }
+
/* Comments */
const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
List<String> comments;
@@ -586,23 +657,23 @@ void GDScriptSyntaxHighlighter::_update_cache() {
add_color_region(beg, end, string_color, end.is_empty());
}
- const Ref<Script> script = _get_edited_resource();
- if (script.is_valid()) {
+ const Ref<Script> scr = _get_edited_resource();
+ if (scr.is_valid()) {
/* Member types. */
const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
- StringName instance_base = script->get_instance_base_type();
+ StringName instance_base = scr->get_instance_base_type();
if (instance_base != StringName()) {
List<PropertyInfo> plist;
ClassDB::get_property_list(instance_base, &plist);
for (const PropertyInfo &E : plist) {
- String name = E.name;
+ String prop_name = E.name;
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
- if (name.contains("/")) {
+ if (prop_name.contains("/")) {
continue;
}
- member_keywords[name] = member_variable_color;
+ member_keywords[prop_name] = member_variable_color;
}
List<String> clist;
@@ -613,24 +684,27 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
}
- const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
+ const String text_edit_color_theme = EDITOR_GET("text_editor/theme/color_theme");
const bool godot_2_theme = text_edit_color_theme == "Godot 2";
if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
+ global_function_color = Color(0.64, 0.64, 0.96);
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);
+ string_name_color = Color(1.0, 0.76, 0.65);
} else {
- function_definition_color = Color(0.0, 0.65, 0.73);
- 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);
+ function_definition_color = Color(0, 0.6, 0.6);
+ global_function_color = Color(0.36, 0.18, 0.72);
+ node_path_color = Color(0.18, 0.55, 0);
+ node_ref_color = Color(0.0, 0.5, 0);
+ annotation_color = Color(0.8, 0.37, 0);
+ string_name_color = Color(0.8, 0.56, 0.45);
}
EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/global_function_color", global_function_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);
@@ -641,6 +715,10 @@ void GDScriptSyntaxHighlighter::_update_cache() {
function_definition_color,
true);
EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/gdscript/global_function_color",
+ global_function_color,
+ true);
+ EditorSettings::get_singleton()->set_initial_value(
"text_editor/theme/highlighting/gdscript/node_path_color",
node_path_color,
true);
@@ -659,6 +737,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color");
+ global_function_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/global_function_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");
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 7987582f07..aceb644658 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_highlighter.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_highlighter.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_HIGHLIGHTER_H
#define GDSCRIPT_HIGHLIGHTER_H
@@ -47,8 +47,10 @@ private:
Vector<ColorRegion> color_regions;
HashMap<int, int> color_region_cache;
- HashMap<StringName, Color> keywords;
+ HashMap<StringName, Color> class_names;
+ HashMap<StringName, Color> reserved_keywords;
HashMap<StringName, Color> member_keywords;
+ HashSet<StringName> global_functions;
enum Type {
NONE,
@@ -67,10 +69,11 @@ private:
TYPE,
};
- // colours
+ // Colors.
Color font_color;
Color symbol_color;
Color function_color;
+ Color global_function_color;
Color function_definition_color;
Color built_in_type_color;
Color number_color;
@@ -88,7 +91,7 @@ public:
virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
virtual String _get_name() const override;
- virtual Array _get_supported_languages() const override;
+ virtual PackedStringArray _get_supported_languages() const override;
virtual Ref<EditorSyntaxHighlighter> _create() const override;
};
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index 9b540b16f2..00d50d1737 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_translation_parser_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_translation_parser_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_translation_parser_plugin.h"
@@ -41,7 +41,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
// Extract all translatable strings using the parsed tree from GDSriptParser.
// The strategy is to find all ExpressionNode and AssignmentNode from the tree and extract strings if relevant, i.e
// Search strings in ExpressionNode -> CallNode -> tr(), set_text(), set_placeholder() etc.
- // Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
+ // Search strings in AssignmentNode -> text = "__", tooltip_text = "__" etc.
Error err;
Ref<Resource> loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
@@ -221,7 +221,7 @@ void GDScriptEditorTranslationParserPlugin::_assess_assignment(GDScriptParser::A
}
if (assignment_patterns.has(assignee_name) && p_assignment->assigned_value->type == GDScriptParser::Node::LITERAL) {
- // If the assignment is towards one of the extract patterns (text, hint_tooltip etc.), and the value is a string literal, we collect the string.
+ // If the assignment is towards one of the extract patterns (text, tooltip_text etc.), and the value is a string literal, we collect the string.
ids->push_back(static_cast<GDScriptParser::LiteralNode *>(p_assignment->assigned_value)->value);
} else if (assignee_name == fd_filters && p_assignment->assigned_value->type == GDScriptParser::Node::CALL) {
// FileDialog.filters accepts assignment in the form of PackedStringArray. For example,
@@ -330,10 +330,10 @@ void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser:
GDScriptEditorTranslationParserPlugin::GDScriptEditorTranslationParserPlugin() {
assignment_patterns.insert("text");
assignment_patterns.insert("placeholder_text");
- assignment_patterns.insert("hint_tooltip");
+ assignment_patterns.insert("tooltip_text");
first_arg_patterns.insert("set_text");
- first_arg_patterns.insert("set_tooltip");
+ first_arg_patterns.insert("set_tooltip_text");
first_arg_patterns.insert("set_placeholder");
first_arg_patterns.insert("add_tab");
first_arg_patterns.insert("add_check_item");
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 969a50f48c..7e6e381e3f 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_translation_parser_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_translation_parser_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
diff --git a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd
index a379d915a9..b8fc8c75dc 100644
--- a/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd
+++ b/modules/gdscript/editor/script_templates/CharacterBody2D/basic_movement.gd
@@ -6,7 +6,7 @@ extends _BASE_
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
-# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
diff --git a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd
index 360b199e56..53bc606c9a 100644
--- a/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd
+++ b/modules/gdscript/editor/script_templates/CharacterBody3D/basic_movement.gd
@@ -6,7 +6,7 @@ extends _BASE_
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
-# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
+# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index d752bef14f..ffe01eaa18 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript.h"
@@ -111,9 +111,9 @@ GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) {
if (p_script->initializer) {
return p_script->initializer;
} else {
- GDScript *base = p_script->_base;
- if (base != nullptr) {
- return _super_constructor(base);
+ GDScript *base_src = p_script->_base;
+ if (base_src != nullptr) {
+ return _super_constructor(base_src);
} else {
return nullptr;
}
@@ -121,9 +121,9 @@ GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) {
}
void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) {
- GDScript *base = p_script->_base;
- if (base != nullptr) {
- _super_implicit_constructor(base, p_instance, r_error);
+ GDScript *base_src = p_script->_base;
+ if (base_src != nullptr) {
+ _super_implicit_constructor(base_src, p_instance, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
return;
}
@@ -151,7 +151,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
/* STEP 2, INITIALIZE AND CONSTRUCT */
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
instances.insert(instance->owner);
}
@@ -160,7 +160,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
instance->script = Ref<GDScript>();
instance->owner->set_script_instance(nullptr);
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
instances.erase(p_owner);
}
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
@@ -177,7 +177,7 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
instance->script = Ref<GDScript>();
instance->owner->set_script_instance(nullptr);
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
instances.erase(p_owner);
}
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance.");
@@ -290,7 +290,9 @@ void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_
#endif
mi.arguments.push_back(arginfo);
}
-
+#ifdef TOOLS_ENABLED
+ mi.default_arguments.append_array(func->get_default_arg_values());
+#endif
mi.return_val = func->get_return_type();
r_list->push_back(mi);
}
@@ -325,16 +327,23 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
}
+
+#ifdef TOOLS_ENABLED
+ r_list->push_back(sptr->get_class_category());
+#endif // TOOLS_ENABLED
+
+ for (const PropertyInfo &E : props) {
+ r_list->push_back(E);
+ }
+
+ props.clear();
+
if (!p_include_base) {
break;
}
sptr = sptr->_base;
}
-
- for (const PropertyInfo &E : props) {
- r_list->push_back(E);
- }
}
void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const {
@@ -387,9 +396,9 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
if (top->native.is_valid()) {
if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
+ GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be assigned to an object of type: '" + p_this->get_class() + "'");
}
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type '" + p_this->get_class() + "'" + ".");
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be assigned to an object of type '" + p_this->get_class() + "'" + ".");
}
}
@@ -409,7 +418,7 @@ PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this)
}
bool GDScript::instance_has(const Object *p_this) const {
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
return instances.has((Object *)p_this);
}
@@ -434,10 +443,6 @@ void GDScript::set_source_code(const String &p_code) {
#ifdef TOOLS_ENABLED
void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
- if (base_cache.is_valid()) {
- base_cache->_update_exports_values(values, propnames);
- }
-
for (const KeyValue<StringName, Variant> &E : member_default_values_cache) {
values[E.key] = E.value;
}
@@ -445,6 +450,10 @@ void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List
for (const PropertyInfo &E : members_cache) {
propnames.push_back(E);
}
+
+ if (base_cache.is_valid()) {
+ base_cache->_update_exports_values(values, propnames);
+ }
}
void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
@@ -469,7 +478,7 @@ void GDScript::_clear_doc() {
void GDScript::_update_doc() {
_clear_doc();
- doc.script_path = "\"" + get_path().get_slice("://", 1) + "\"";
+ doc.script_path = vformat(R"("%s")", get_script_path().get_slice("://", 1));
if (!name.is_empty()) {
doc.name = name;
} else {
@@ -611,11 +620,11 @@ void GDScript::_update_doc() {
}
if (!is_enum) {
DocData::ConstantDoc constant_doc;
- String doc_description;
+ String const_description;
if (doc_constants.has(E.key)) {
- doc_description = doc_constants[E.key];
+ const_description = doc_constants[E.key];
}
- DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_description);
+ DocData::constant_doc_from_variant(constant_doc, E.key, E.value, const_description);
doc.constants.push_back(constant_doc);
}
}
@@ -665,36 +674,14 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
base_cache = Ref<GDScript>();
}
- if (c->extends_used) {
- String path = "";
- if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) {
- path = c->extends_path;
- if (path.is_relative_path()) {
- String base = get_path();
- if (base.is_empty() || base.is_relative_path()) {
- ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data());
- } else {
- path = base.get_base_dir().plus_file(path);
- }
- }
- } else if (c->extends.size() != 0) {
- const StringName &base = c->extends[0];
-
- if (ScriptServer::is_global_class(base)) {
- path = ScriptServer::get_global_class_path(base);
- }
- }
-
- if (!path.is_empty()) {
- if (path != get_path()) {
- Ref<GDScript> bf = ResourceLoader::load(path);
-
- if (bf.is_valid()) {
- base_cache = bf;
- bf->inheriters_cache.insert(get_instance_id());
- }
- } else {
- ERR_PRINT(("Path extending itself in " + path).utf8().get_data());
+ GDScriptParser::DataType base_type = parser.get_tree()->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ Ref<GDScript> bf = GDScriptCache::get_full_script(base_type.script_path, err, path);
+ if (err == OK) {
+ bf = Ref<GDScript>(bf->find_class(base_type.class_type->fqcn));
+ if (bf.is_valid()) {
+ base_cache = bf;
+ bf->inheriters_cache.insert(get_instance_id());
}
}
}
@@ -703,6 +690,8 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
member_default_values_cache.clear();
_signals.clear();
+ members_cache.push_back(get_class_category());
+
for (int i = 0; i < c->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = c->members[i];
@@ -716,6 +705,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
Variant default_value;
if (member.variable->initializer && member.variable->initializer->is_constant) {
default_value = member.variable->initializer->reduced_value;
+ GDScriptCompiler::convert_to_initializer_type(default_value, member.variable);
}
member_default_values_cache[member.variable->identifier->name] = default_value;
} break;
@@ -728,6 +718,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
}
_signals[member.signal->identifier->name] = parameters_names;
} break;
+ case GDScriptParser::ClassNode::Member::GROUP: {
+ members_cache.push_back(member.annotation->export_info);
+ } break;
default:
break; // Nothing.
}
@@ -811,25 +804,23 @@ void GDScript::update_exports() {
#endif
}
-void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) {
- p_sc->path = p_path;
- for (KeyValue<StringName, Ref<GDScript>> &E : p_sc->subclasses) {
- _set_subclass_path(E.value, p_path);
- }
-}
-
String GDScript::_get_debug_path() const {
if (is_built_in() && !get_name().is_empty()) {
- return get_name() + " (" + get_path() + ")";
+ return vformat("%s(%s)", get_name(), get_script_path());
} else {
- return get_path();
+ return get_script_path();
}
}
Error GDScript::reload(bool p_keep_state) {
+ if (reloading) {
+ return OK;
+ }
+ reloading = true;
+
bool has_instances;
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
has_instances = instances.size();
}
@@ -846,9 +837,10 @@ Error GDScript::reload(bool p_keep_state) {
basedir = basedir.get_base_dir();
}
-// Loading a template, don't parse.
+ // Loading a template, don't parse.
#ifdef TOOLS_ENABLED
if (EditorPaths::get_singleton() && basedir.begins_with(EditorPaths::get_singleton()->get_project_script_templates_dir())) {
+ reloading = false;
return OK;
}
#endif
@@ -858,11 +850,10 @@ Error GDScript::reload(bool p_keep_state) {
if (source_path.is_empty()) {
source_path = get_path();
}
- if (!source_path.is_empty()) {
- MutexLock lock(GDScriptCache::singleton->lock);
- if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
- GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
- }
+ Ref<GDScript> cached_script = GDScriptCache::get_cached_script(source_path);
+ if (!source_path.is_empty() && cached_script.is_null()) {
+ MutexLock lock(GDScriptCache::singleton->mutex);
+ GDScriptCache::singleton->shallow_gdscript_cache[source_path] = Ref<GDScript>(this);
}
}
@@ -875,6 +866,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);
+ reloading = false;
return ERR_PARSE_ERROR;
}
@@ -891,6 +883,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();
}
+ reloading = false;
return ERR_PARSE_ERROR;
}
@@ -899,18 +892,16 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
-#ifdef TOOLS_ENABLED
- _update_doc();
-#endif
-
if (err) {
if (can_run) {
if (EngineDebugger::is_active()) {
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);
+ reloading = false;
return ERR_COMPILATION_FAILED;
} else {
+ reloading = false;
return err;
}
}
@@ -918,19 +909,12 @@ Error GDScript::reload(bool p_keep_state) {
for (const GDScriptWarning &warning : parser.get_warnings()) {
if (EngineDebugger::is_active()) {
Vector<ScriptLanguage::StackInfo> si;
- EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si);
+ EngineDebugger::get_script_debugger()->send_error("", get_script_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si);
}
}
#endif
- valid = true;
-
- for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
- _set_subclass_path(E.value, path);
- }
-
- _init_rpc_methods_properties();
-
+ reloading = false;
return OK;
}
@@ -1023,20 +1007,29 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
void GDScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new"));
-
- ClassDB::bind_method(D_METHOD("get_as_byte_code"), &GDScript::get_as_byte_code);
}
-Vector<uint8_t> GDScript::get_as_byte_code() const {
- return Vector<uint8_t>();
-};
+void GDScript::set_path(const String &p_path, bool p_take_over) {
+ String old_path = path;
+ if (is_root_script()) {
+ Script::set_path(p_path, p_take_over);
+ }
+ this->path = p_path;
+ GDScriptCache::move_script(old_path, p_path);
+ for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
+ kv.value->set_path(p_path, p_take_over);
+ }
+}
-// TODO: Fully remove this. There's not this kind of "bytecode" anymore.
-Error GDScript::load_byte_code(const String &p_path) {
- return ERR_COMPILATION_FAILED;
+String GDScript::get_script_path() const {
+ return path;
}
Error GDScript::load_source_code(const String &p_path) {
+ if (p_path.is_empty() || p_path.begins_with("gdscript://") || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
+ return OK;
+ }
+
Vector<uint8_t> sourcef;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -1063,10 +1056,12 @@ Error GDScript::load_source_code(const String &p_path) {
}
source = s;
+ path = p_path;
#ifdef TOOLS_ENABLED
source_changed_cache = true;
-#endif
- path = p_path;
+ set_edited(false);
+ set_last_modified_time(FileAccess::get_modified_time(path));
+#endif // TOOLS_ENABLED
return OK;
}
@@ -1106,6 +1101,131 @@ bool GDScript::inherits_script(const Ref<Script> &p_script) const {
return false;
}
+GDScript *GDScript::find_class(const String &p_qualified_name) {
+ String first = p_qualified_name.get_slice("::", 0);
+
+ Vector<String> class_names;
+ GDScript *result = nullptr;
+ // Empty initial name means start here.
+ if (first.is_empty() || first == name) {
+ class_names = p_qualified_name.split("::");
+ result = this;
+ } else if (p_qualified_name.begins_with(get_root_script()->path)) {
+ // Script path could have a class path separator("::") in it.
+ class_names = p_qualified_name.trim_prefix(get_root_script()->path).split("::");
+ result = get_root_script();
+ } else if (HashMap<StringName, Ref<GDScript>>::Iterator E = subclasses.find(first)) {
+ class_names = p_qualified_name.split("::");
+ result = E->value.ptr();
+ } else if (_owner != nullptr) {
+ // Check parent scope.
+ return _owner->find_class(p_qualified_name);
+ }
+
+ // Starts at index 1 because index 0 was handled above.
+ for (int i = 1; result != nullptr && i < class_names.size(); i++) {
+ String current_name = class_names[i];
+ if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) {
+ result = E->value.ptr();
+ } else {
+ // Couldn't find inner class.
+ return nullptr;
+ }
+ }
+
+ return result;
+}
+
+bool GDScript::has_class(const GDScript *p_script) {
+ String fqn = p_script->fully_qualified_name;
+ if (fully_qualified_name.is_empty() && fqn.get_slice("::", 0).is_empty()) {
+ return p_script == this;
+ } else if (fqn.begins_with(fully_qualified_name)) {
+ return p_script == find_class(fqn.trim_prefix(fully_qualified_name));
+ }
+ return false;
+}
+
+GDScript *GDScript::get_root_script() {
+ GDScript *result = this;
+ while (result->_owner) {
+ result = result->_owner;
+ }
+ return result;
+}
+
+RBSet<GDScript *> GDScript::get_dependencies() {
+ RBSet<GDScript *> dependencies;
+
+ _get_dependencies(dependencies, this);
+ dependencies.erase(this);
+
+ return dependencies;
+}
+
+RBSet<GDScript *> GDScript::get_inverted_dependencies() {
+ RBSet<GDScript *> inverted_dependencies;
+
+ List<GDScript *> scripts;
+ {
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
+
+ SelfList<GDScript> *elem = GDScriptLanguage::singleton->script_list.first();
+ while (elem) {
+ scripts.push_back(elem->self());
+ elem = elem->next();
+ }
+ }
+
+ for (GDScript *scr : scripts) {
+ if (scr == nullptr || scr == this || scr->destructing) {
+ continue;
+ }
+
+ RBSet<GDScript *> scr_dependencies = scr->get_dependencies();
+ if (scr_dependencies.has(this)) {
+ inverted_dependencies.insert(scr);
+ }
+ }
+
+ return inverted_dependencies;
+}
+
+RBSet<GDScript *> GDScript::get_must_clear_dependencies() {
+ RBSet<GDScript *> dependencies = get_dependencies();
+ RBSet<GDScript *> must_clear_dependencies;
+ HashMap<GDScript *, RBSet<GDScript *>> inverted_dependencies;
+
+ for (GDScript *E : dependencies) {
+ inverted_dependencies.insert(E, E->get_inverted_dependencies());
+ }
+
+ RBSet<GDScript *> cant_clear;
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ for (GDScript *F : E.value) {
+ if (!dependencies.has(F)) {
+ cant_clear.insert(E.key);
+ for (GDScript *G : E.key->get_dependencies()) {
+ cant_clear.insert(G);
+ }
+ break;
+ }
+ }
+ }
+
+ for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) {
+ if (cant_clear.has(E.key) || ScriptServer::is_global_class(E.key->get_fully_qualified_name())) {
+ continue;
+ }
+ must_clear_dependencies.insert(E.key);
+ }
+
+ cant_clear.clear();
+ dependencies.clear();
+ inverted_dependencies.clear();
+ return must_clear_dependencies;
+}
+
bool GDScript::has_script_signal(const StringName &p_signal) const {
if (_signals.has(p_signal)) {
return true;
@@ -1167,18 +1287,76 @@ String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript)
return class_name;
}
+GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
+ Object *obj = p_variant;
+ if (obj == nullptr || obj->get_instance_id().is_null()) {
+ return nullptr;
+ }
+ return Object::cast_to<GDScript>(obj);
+}
+
+void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
+ if (p_dependencies.has(this)) {
+ return;
+ }
+ p_dependencies.insert(this);
+
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ if (E.value == nullptr) {
+ continue;
+ }
+ for (const Variant &V : E.value->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ if (implicit_initializer) {
+ for (const Variant &V : implicit_initializer->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ if (implicit_ready) {
+ for (const Variant &V : implicit_ready->constants) {
+ GDScript *scr = _get_gdscript_from_variant(V);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+ }
+
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ if (E.value != p_except) {
+ E.value->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ GDScript *scr = _get_gdscript_from_variant(E.value);
+ if (scr != nullptr && scr != p_except) {
+ scr->_get_dependencies(p_dependencies, p_except);
+ }
+ }
+}
+
GDScript::GDScript() :
script_list(this) {
-#ifdef DEBUG_ENABLED
{
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
GDScriptLanguage::get_singleton()->script_list.add(&script_list);
}
-#endif
+
+ path = vformat("gdscript://%d.gd", get_instance_id());
}
-void GDScript::_save_orphaned_subclasses() {
+void GDScript::_save_orphaned_subclasses(GDScript::ClearData *p_clear_data) {
struct ClassRefWithName {
ObjectID id;
String fully_qualified_name;
@@ -1194,8 +1372,17 @@ void GDScript::_save_orphaned_subclasses() {
}
// clear subclasses to allow unused subclasses to be deleted
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ p_clear_data->scripts.insert(E.value);
+ }
subclasses.clear();
// subclasses are also held by constants, clear those as well
+ for (KeyValue<StringName, Variant> &E : constants) {
+ GDScript *gdscr = _get_gdscript_from_variant(E.value);
+ if (gdscr != nullptr) {
+ p_clear_data->scripts.insert(gdscr);
+ }
+ }
constants.clear();
// keep orphan subclass only for subclasses that are still in use
@@ -1217,58 +1404,59 @@ void GDScript::_init_rpc_methods_properties() {
rpc_config = base->rpc_config.duplicate();
}
- GDScript *cscript = this;
- HashMap<StringName, Ref<GDScript>>::Iterator sub_E = subclasses.begin();
- while (cscript) {
- // RPC Methods
- for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
- 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;
- }
-
- if (sub_E) {
- cscript = sub_E->value.ptr();
- } else {
- cscript = nullptr;
+ // RPC Methods
+ for (KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ Variant config = E.value->get_rpc_config();
+ if (config.get_type() != Variant::NIL) {
+ rpc_config[E.value->get_name()] = config;
}
}
}
-GDScript::~GDScript() {
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+void GDScript::clear(GDScript::ClearData *p_clear_data) {
+ if (clearing) {
+ return;
+ }
+ clearing = true;
- while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
- // Order matters since clearing the stack may already cause
- // the GDSCriptFunctionState to be destroyed and thus removed from the list.
- pending_func_states.remove(E);
- E->self()->_clear_stack();
- }
+ GDScript::ClearData data;
+ GDScript::ClearData *clear_data = p_clear_data;
+ bool is_root = false;
+
+ // If `clear_data` is `nullptr`, it means that it's the root.
+ // The root is in charge to clear functions and scripts of itself and its dependencies
+ if (clear_data == nullptr) {
+ clear_data = &data;
+ is_root = true;
+ }
+
+ RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
+ for (GDScript *E : must_clear_dependencies) {
+ clear_data->scripts.insert(E);
+ E->clear(clear_data);
}
for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- memdelete(E.value);
+ clear_data->functions.insert(E.value);
}
+ member_functions.clear();
- if (implicit_initializer) {
- memdelete(implicit_initializer);
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : member_indices) {
+ clear_data->scripts.insert(E.value.data_type.script_type_ref);
+ E.value.data_type.script_type_ref = Ref<Script>();
}
- if (implicit_ready) {
- memdelete(implicit_ready);
+ if (implicit_initializer) {
+ clear_data->functions.insert(implicit_initializer);
+ implicit_initializer = nullptr;
}
- if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
- GDScriptCache::remove_script(get_path());
+ if (implicit_ready) {
+ clear_data->functions.insert(implicit_ready);
+ implicit_ready = nullptr;
}
- _save_orphaned_subclasses();
+ _save_orphaned_subclasses(clear_data);
#ifdef TOOLS_ENABLED
// Clearing inner class doc, script doc only cleared when the script source deleted.
@@ -1277,13 +1465,50 @@ GDScript::~GDScript() {
}
#endif
-#ifdef DEBUG_ENABLED
+ // If it's not the root, skip clearing the data
+ if (is_root) {
+ // All dependencies have been accounted for
+ for (GDScriptFunction *E : clear_data->functions) {
+ memdelete(E);
+ }
+ for (Ref<Script> &E : clear_data->scripts) {
+ Ref<GDScript> gdscr = E;
+ if (gdscr.is_valid()) {
+ GDScriptCache::remove_script(gdscr->get_path());
+ }
+ }
+ clear_data->clear();
+ }
+}
+
+GDScript::~GDScript() {
+ if (destructing) {
+ return;
+ }
+ destructing = true;
+
+ clear();
+
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
+
+ while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
+ // Order matters since clearing the stack may already cause
+ // the GDScriptFunctionState to be destroyed and thus removed from the list.
+ pending_func_states.remove(E);
+ E->self()->_clear_stack();
+ }
+ }
+
{
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
}
-#endif
+
+ if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
+ GDScriptCache::remove_script(get_path());
+ }
}
//////////////////////////////
@@ -1510,12 +1735,59 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
props.push_front(sptr->member_info[msort[i].name]);
}
+#ifdef TOOLS_ENABLED
+ p_properties->push_back(sptr->get_class_category());
+#endif // TOOLS_ENABLED
+
+ for (const PropertyInfo &prop : props) {
+ p_properties->push_back(prop);
+ }
+
+ props.clear();
+
sptr = sptr->_base;
}
+}
- for (const PropertyInfo &E : props) {
- p_properties->push_back(E);
+bool GDScriptInstance::property_can_revert(const StringName &p_name) const {
+ Variant name = p_name;
+ const Variant *args[1] = { &name };
+
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._property_can_revert);
+ if (E) {
+ Callable::CallError err;
+ Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
+ if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) {
+ return true;
+ }
+ }
+ sptr = sptr->_base;
+ }
+
+ return false;
+}
+
+bool GDScriptInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
+ Variant name = p_name;
+ const Variant *args[1] = { &name };
+
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._property_get_revert);
+ if (E) {
+ Callable::CallError err;
+ Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
+ if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
+ r_ret = ret;
+ return true;
+ }
+ }
+ sptr = sptr->_base;
}
+
+ return false;
}
void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
@@ -1659,7 +1931,7 @@ GDScriptInstance::GDScriptInstance() {
}
GDScriptInstance::~GDScriptInstance() {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) {
// Order matters since clearing the stack may already cause
@@ -1702,6 +1974,16 @@ void GDScriptLanguage::add_named_global_constant(const StringName &p_name, const
named_globals[p_name] = p_value;
}
+Variant GDScriptLanguage::get_any_global_constant(const StringName &p_name) {
+ if (named_globals.has(p_name)) {
+ return named_globals[p_name];
+ }
+ if (globals.has(p_name)) {
+ return _global_array[globals[p_name]];
+ }
+ ERR_FAIL_V_MSG(Variant(), vformat("Could not find any global constant with name: %s.", p_name));
+}
+
void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
ERR_FAIL_COND(!named_globals.has(p_name));
named_globals.erase(p_name);
@@ -1758,11 +2040,47 @@ Error GDScriptLanguage::execute_file(const String &p_path) {
}
void GDScriptLanguage::finish() {
+ if (_call_stack) {
+ memdelete_arr(_call_stack);
+ _call_stack = nullptr;
+ }
+
+ // Clear the cache before parsing the script_list
+ GDScriptCache::clear();
+
+ // Clear dependencies between scripts, to ensure cyclic references are broken
+ // (to avoid leaks at exit).
+ SelfList<GDScript> *s = script_list.first();
+ while (s) {
+ // This ensures the current script is not released before we can check
+ // what's the next one in the list (we can't get the next upfront because we
+ // don't know if the reference breaking will cause it -or any other after
+ // it, for that matter- to be released so the next one is not the same as
+ // before).
+ Ref<GDScript> scr = s->self();
+ if (scr.is_valid()) {
+ for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) {
+ GDScriptFunction *func = E.value;
+ for (int i = 0; i < func->argument_types.size(); i++) {
+ func->argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ func->return_type.script_type_ref = Ref<Script>();
+ }
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
+ }
+
+ // Clear backup for scripts that could slip out of the cyclic reference
+ // check
+ scr->clear();
+ }
+ s = s->next();
+ }
}
void GDScriptLanguage::profiling_start() {
#ifdef DEBUG_ENABLED
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -1784,7 +2102,7 @@ void GDScriptLanguage::profiling_start() {
void GDScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
profiling = false;
#endif
@@ -1794,7 +2112,7 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr,
int current = 0;
#ifdef DEBUG_ENABLED
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -1817,7 +2135,7 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_
int current = 0;
#ifdef DEBUG_ENABLED
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -1863,11 +2181,12 @@ void GDScriptLanguage::reload_all_scripts() {
print_verbose("GDScript: Reloading all scripts");
List<Ref<GDScript>> scripts;
{
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
- if (elem->self()->get_path().is_resource_file()) {
+ // Scripts will reload all subclasses, so only reload root scripts.
+ if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
print_verbose("GDScript: Found: " + elem->self()->get_path());
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
@@ -1879,10 +2198,10 @@ void GDScriptLanguage::reload_all_scripts() {
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (Ref<GDScript> &script : scripts) {
- print_verbose("GDScript: Reloading: " + script->get_path());
- script->load_source_code(script->get_path());
- script->reload(true);
+ for (Ref<GDScript> &scr : scripts) {
+ print_verbose("GDScript: Reloading: " + scr->get_path());
+ scr->load_source_code(scr->get_path());
+ scr->reload(true);
}
#endif
}
@@ -1892,11 +2211,12 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
List<Ref<GDScript>> scripts;
{
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScript> *elem = script_list.first();
while (elem) {
- if (elem->self()->get_path().is_resource_file()) {
+ // Scripts will reload all subclasses, so only reload root scripts.
+ if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) {
scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident
}
elem = elem->next();
@@ -1911,21 +2231,21 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (Ref<GDScript> &script : scripts) {
- bool reload = script == p_script || to_reload.has(script->get_base());
+ for (Ref<GDScript> &scr : scripts) {
+ bool reload = scr == p_script || to_reload.has(scr->get_base());
if (!reload) {
continue;
}
- to_reload.insert(script, HashMap<ObjectID, List<Pair<StringName, Variant>>>());
+ to_reload.insert(scr, HashMap<ObjectID, List<Pair<StringName, Variant>>>());
if (!p_soft_reload) {
//save state and remove script from instances
- HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
+ HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[scr];
- while (script->instances.front()) {
- Object *obj = script->instances.front()->get();
+ while (scr->instances.front()) {
+ Object *obj = scr->instances.front()->get();
//save instance info
List<Pair<StringName, Variant>> state;
if (obj->get_script_instance()) {
@@ -1938,8 +2258,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//same thing for placeholders
#ifdef TOOLS_ENABLED
- while (script->placeholders.size()) {
- Object *obj = (*script->placeholders.begin())->get_owner();
+ while (scr->placeholders.size()) {
+ Object *obj = (*scr->placeholders.begin())->get_owner();
//save instance info
if (obj->get_script_instance()) {
@@ -1949,13 +2269,13 @@ 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.begin());
+ scr->placeholders.erase(*scr->placeholders.begin());
}
}
#endif
- for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : script->pending_reload_state) {
+ for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : scr->pending_reload_state) {
map[F.key] = F.value; //pending to reload, use this one instead
}
}
@@ -1980,9 +2300,9 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
obj->set_script(scr);
- ScriptInstance *script_instance = obj->get_script_instance();
+ ScriptInstance *script_inst = obj->get_script_instance();
- if (!script_instance) {
+ if (!script_inst) {
//failed, save reload state for next time if not saved
if (!scr->pending_reload_state.has(obj->get_instance_id())) {
scr->pending_reload_state[obj->get_instance_id()] = saved_state;
@@ -1990,14 +2310,14 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
continue;
}
- if (script_instance->is_placeholder() && scr->is_placeholder_fallback_enabled()) {
- PlaceHolderScriptInstance *placeholder = static_cast<PlaceHolderScriptInstance *>(script_instance);
+ if (script_inst->is_placeholder() && scr->is_placeholder_fallback_enabled()) {
+ PlaceHolderScriptInstance *placeholder = static_cast<PlaceHolderScriptInstance *>(script_inst);
for (List<Pair<StringName, Variant>>::Element *G = saved_state.front(); G; G = G->next()) {
placeholder->property_set_fallback(G->get().first, G->get().second);
}
} else {
for (List<Pair<StringName, Variant>>::Element *G = saved_state.front(); G; G = G->next()) {
- script_instance->set(G->get().first, G->get().second);
+ script_inst->set(G->get().first, G->get().second);
}
}
@@ -2015,7 +2335,7 @@ void GDScriptLanguage::frame() {
#ifdef DEBUG_ENABLED
if (profiling) {
- MutexLock lock(this->lock);
+ MutexLock lock(this->mutex);
SelfList<GDScriptFunction> *elem = function_list.first();
while (elem) {
@@ -2143,7 +2463,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
*r_icon_path = c->icon_path;
} else if (c->icon_path.is_relative_path()) {
- *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
+ *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
}
}
if (r_base_type) {
@@ -2171,7 +2491,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
String subpath = subclass->extends_path;
if (subpath.is_relative_path()) {
- subpath = path.get_base_dir().plus_file(subpath).simplify_path();
+ subpath = path.get_base_dir().path_join(subpath).simplify_path();
}
if (OK != subparser.parse(subsource, subpath, false)) {
@@ -2228,6 +2548,8 @@ GDScriptLanguage::GDScriptLanguage() {
strings._set = StaticCString::create("_set");
strings._get = StaticCString::create("_get");
strings._get_property_list = StaticCString::create("_get_property_list");
+ strings._property_can_revert = StaticCString::create("_property_can_revert");
+ strings._property_get_revert = StaticCString::create("_property_get_revert");
strings._script_source = StaticCString::create("script/source");
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
@@ -2236,8 +2558,7 @@ GDScriptLanguage::GDScriptLanguage() {
script_frame_time = 0;
_debug_call_stack_pos = 0;
- int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
- ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
+ int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"), 1024);
if (EngineDebugger::is_active()) {
//debugging enabled!
@@ -2258,44 +2579,12 @@ GDScriptLanguage::GDScriptLanguage() {
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);
+ GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_enabled);
}
#endif // DEBUG_ENABLED
}
GDScriptLanguage::~GDScriptLanguage() {
- if (_call_stack) {
- memdelete_arr(_call_stack);
- }
-
- // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
- SelfList<GDScript> *s = script_list.first();
- while (s) {
- GDScript *script = s->self();
- // This ensures the current script is not released before we can check what's the next one
- // in the list (we can't get the next upfront because we don't know if the reference breaking
- // will cause it -or any other after it, for that matter- to be released so the next one
- // is not the same as before).
- script->reference();
-
- for (KeyValue<StringName, GDScriptFunction *> &E : script->member_functions) {
- GDScriptFunction *func = E.value;
- for (int i = 0; i < func->argument_types.size(); i++) {
- func->argument_types.write[i].script_type_ref = Ref<Script>();
- }
- func->return_type.script_type_ref = Ref<Script>();
- }
- for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
- E.value.data_type.script_type_ref = Ref<Script>();
- }
-
- s = s->next();
- script->unreference();
- }
-
singleton = nullptr;
}
@@ -2317,6 +2606,26 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
return Ref<GDScript>(Object::cast_to<GDScript>(obj));
}
+Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String &p_name) {
+ {
+ MutexLock lock(mutex);
+
+ SelfList<GDScript> *elem = script_list.first();
+ while (elem) {
+ GDScript *scr = elem->self();
+ if (scr->fully_qualified_name == p_name) {
+ return scr;
+ }
+ elem = elem->next();
+ }
+ }
+
+ Ref<GDScript> scr;
+ scr.instantiate();
+ scr->fully_qualified_name = p_name;
+ return scr;
+}
+
/*************** RESOURCE ***************/
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) {
@@ -2325,27 +2634,22 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str
}
Error err;
- Ref<GDScript> script = GDScriptCache::get_full_script(p_path, err);
+ Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE);
- // TODO: Reintroduce binary and encrypted scripts.
-
- if (script.is_null()) {
+ if (scr.is_null()) {
// Don't fail loading because of parsing error.
- script.instantiate();
+ scr.instantiate();
}
if (r_error) {
*r_error = OK;
}
- return script;
+ return scr;
}
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
- // TODO: Reintroduce binary and encrypted scripts.
- // p_extensions->push_back("gdc");
- // p_extensions->push_back("gde");
}
bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
@@ -2354,8 +2658,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
- // TODO: Reintroduce binary and encrypted scripts.
- if (el == "gd" /*|| el == "gdc" || el == "gde"*/) {
+ if (el == "gd") {
return "GDScript";
}
return "";
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 5123cccddd..71184ac2da 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_H
#define GDSCRIPT_H
@@ -61,6 +61,7 @@ class GDScript : public Script {
GDCLASS(GDScript, Script);
bool tool = false;
bool valid = false;
+ bool reloading = false;
struct MemberInfo {
int index = 0;
@@ -69,6 +70,15 @@ class GDScript : public Script {
GDScriptDataType data_type;
};
+ struct ClearData {
+ RBSet<GDScriptFunction *> functions;
+ RBSet<Ref<Script>> scripts;
+ void clear() {
+ functions.clear();
+ scripts.clear();
+ }
+ };
+
friend class GDScriptInstance;
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
@@ -124,6 +134,8 @@ class GDScript : public Script {
int subclass_count = 0;
RBSet<Object *> instances;
+ bool destructing = false;
+ bool clearing = false;
//exported members
String source;
String path;
@@ -137,7 +149,6 @@ class GDScript : public Script {
void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error);
GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
- void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path);
String _get_debug_path() const;
#ifdef TOOLS_ENABLED
@@ -154,7 +165,7 @@ class GDScript : public Script {
bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false, PlaceHolderScriptInstance *p_instance_to_update = nullptr);
- void _save_orphaned_subclasses();
+ void _save_orphaned_subclasses(GDScript::ClearData *p_clear_data);
void _init_rpc_methods_properties();
void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const;
@@ -164,6 +175,9 @@ class GDScript : public Script {
// This method will map the class name from "RefCounted" to "MyClass.InnerClass".
static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
+ GDScript *_get_gdscript_from_variant(const Variant &p_variant);
+ void _get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except);
+
protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -174,10 +188,17 @@ protected:
static void _bind_methods();
public:
+ void clear(GDScript::ClearData *p_clear_data = nullptr);
+
virtual bool is_valid() const override { return valid; }
bool inherits_script(const Ref<Script> &p_script) const override;
+ GDScript *find_class(const String &p_qualified_name);
+ bool has_class(const GDScript *p_script);
+ GDScript *get_root_script();
+ bool is_root_script() const { return _owner == nullptr; }
+ String get_fully_qualified_name() const { return fully_qualified_name; }
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; }
@@ -189,6 +210,10 @@ public:
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
+ RBSet<GDScript *> get_dependencies();
+ RBSet<GDScript *> get_inverted_dependencies();
+ RBSet<GDScript *> get_must_clear_dependencies();
+
virtual bool has_script_signal(const StringName &p_signal) const override;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
@@ -222,11 +247,9 @@ public:
virtual Error reload(bool p_keep_state = false) override;
- void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+ String get_script_path() const;
Error load_source_code(const String &p_path);
- Error load_byte_code(const String &p_path);
-
- Vector<uint8_t> get_as_byte_code() const;
bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
@@ -266,6 +289,7 @@ class GDScriptInstance : public ScriptInstance {
friend class GDScriptLambdaCallable;
friend class GDScriptLambdaSelfCallable;
friend class GDScriptCompiler;
+ friend class GDScriptCache;
friend struct GDScriptUtilityFunctionsDefinitions;
ObjectID owner_id;
@@ -287,6 +311,9 @@ public:
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const;
+ virtual bool property_can_revert(const StringName &p_name) const;
+ virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const;
+
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -339,7 +366,7 @@ class GDScriptLanguage : public ScriptLanguage {
friend class GDScriptInstance;
- Mutex lock;
+ Mutex mutex;
friend class GDScript;
@@ -411,7 +438,7 @@ public:
csi.write[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
if (_call_stack[i].function) {
csi.write[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
- csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path();
+ csi.write[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_script_path();
}
}
return csi;
@@ -423,6 +450,8 @@ public:
StringName _set;
StringName _get;
StringName _get_property_list;
+ StringName _property_can_revert;
+ StringName _property_get_revert;
StringName _script_source;
} strings;
@@ -431,6 +460,9 @@ public:
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
_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; }
+ // These two functions should be used when behavior needs to be consistent between in-editor and running the scene
+ bool has_any_global_constant(const StringName &p_name) { return named_globals.has(p_name) || globals.has(p_name); }
+ Variant get_any_global_constant(const StringName &p_name);
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
@@ -509,6 +541,8 @@ public:
void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
+ Ref<GDScript> get_script_by_fully_qualified_name(const String &p_name);
+
GDScriptLanguage();
~GDScriptLanguage();
};
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 7ed440929c..e04a962dcb 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1,37 +1,38 @@
-/*************************************************************************/
-/* gdscript_analyzer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_analyzer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_analyzer.h"
#include "core/config/engine.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
@@ -39,6 +40,10 @@
#include "core/templates/hash_map.h"
#include "gdscript.h"
#include "gdscript_utility_functions.h"
+#include "scene/resources/packed_scene.h"
+
+#define UNNAMED_ENUM "<anonymous enum>"
+#define ENUM_SEPARATOR "::"
static MethodInfo info_from_utility_func(const StringName &p_function) {
ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
@@ -63,9 +68,6 @@ static MethodInfo info_from_utility_func(const StringName &p_function) {
pi.name = "arg" + itos(i + 1);
#endif
pi.type = Variant::get_utility_function_argument_type(p_function, i);
- if (pi.type == Variant::NIL) {
- pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
info.arguments.push_back(pi);
}
}
@@ -98,19 +100,45 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::NATIVE;
type.builtin_type = Variant::OBJECT;
- type.is_constant = true;
type.native_type = p_class_name;
+ type.is_constant = true;
type.is_meta_type = true;
return type;
}
-static GDScriptParser::DataType make_native_enum_type(const StringName &p_native_class, const StringName &p_enum_name) {
+static GDScriptParser::DataType make_script_meta_type(const Ref<Script> &p_script) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::ENUM;
- type.builtin_type = Variant::INT;
+ type.kind = GDScriptParser::DataType::SCRIPT;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = p_script->get_instance_base_type();
+ type.script_type = p_script;
+ type.script_path = p_script->get_path();
type.is_constant = true;
type.is_meta_type = true;
+ return type;
+}
+
+// In enum types, native_type is used to store the class (native or otherwise) that the enum belongs to.
+// This disambiguates between similarly named enums in base classes or outer classes
+static GDScriptParser::DataType make_enum_type(const StringName &p_enum_name, const String &p_base_name, const bool p_meta = false) {
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::ENUM;
+ type.builtin_type = p_meta ? Variant::DICTIONARY : Variant::INT;
+ type.enum_type = p_enum_name;
+ type.is_constant = true;
+ type.is_meta_type = p_meta;
+
+ // For enums, native_type is only used to check compatibility in is_type_compatible()
+ // We can set anything readable here for error messages, as long as it uniquely identifies the type of the enum
+ type.native_type = p_base_name + ENUM_SEPARATOR + p_enum_name;
+
+ return type;
+}
+
+static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_name, const StringName &p_native_class, const bool p_meta = true) {
+ GDScriptParser::DataType type = make_enum_type(p_enum_name, p_native_class, p_meta);
List<StringName> enum_values;
ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values);
@@ -132,7 +160,20 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
return type;
}
-bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class) {
+static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) {
+ // Check that an enum has a given value, not key.
+ // Make sure that implicit conversion to int64_t is sensible before calling!
+ HashMap<StringName, int64_t>::ConstIterator i = p_type.enum_values.begin();
+ while (i) {
+ if (i->value == p_val) {
+ return i->key;
+ }
+ ++i;
+ }
+ return StringName();
+}
+
+bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) {
if (p_class->members_indices.has(p_member_name)) {
int index = p_class->members_indices[p_member_name];
const GDScriptParser::ClassNode::Member *member = &p_class->members[index];
@@ -145,6 +186,9 @@ bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName
member->type == GDScriptParser::ClassNode::Member::SIGNAL) {
return true;
}
+ if (p_member->type != GDScriptParser::Node::FUNCTION && member->type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ return true;
+ }
}
return false;
@@ -160,6 +204,9 @@ bool GDScriptAnalyzer::has_member_name_conflict_in_native_type(const StringName
if (ClassDB::has_integer_constant(p_native_type_string, p_member_name)) {
return true;
}
+ if (p_member_name == CoreStringNames::get_singleton()->_script) {
+ return true;
+ }
return false;
}
@@ -184,17 +231,22 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
}
Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) {
+ // TODO check outer classes for static members only
const GDScriptParser::DataType *current_data_type = &p_class_node->base_type;
while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
- if (has_member_name_conflict_in_script_class(p_member_name, current_class_node)) {
- push_error(vformat(R"(The member "%s" already exists in a parent class.)", p_member_name),
- p_member_node);
+ if (has_member_name_conflict_in_script_class(p_member_name, current_class_node, p_member_node)) {
+ String parent_class_name = current_class_node->fqcn;
+ if (current_class_node->identifier != nullptr) {
+ parent_class_name = current_class_node->identifier->name;
+ }
+ push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, parent_class_name), p_member_node);
return ERR_PARSE_ERROR;
}
current_data_type = &current_class_node->base_type;
}
+ // No need for native class recursion because Node exposes all Object's properties.
if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) {
if (current_data_type->native_type != StringName()) {
return check_native_member_name_conflict(
@@ -207,25 +259,80 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
return OK;
}
-Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
- if (p_class->base_type.is_set()) {
- // Already resolved
+void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) {
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_NULL(p_list);
+
+ if (p_list->find(p_node) != nullptr) {
+ return;
+ }
+
+ p_list->push_back(p_node);
+
+ // TODO: Try to solve class inheritance if not yet resolving.
+
+ // Prioritize node base type over its outer class
+ if (p_node->base_type.class_type != nullptr) {
+ get_class_node_current_scope_classes(p_node->base_type.class_type, p_list);
+ }
+
+ if (p_node->outer != nullptr) {
+ get_class_node_current_scope_classes(p_node->outer, p_list);
+ }
+}
+
+Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
+ if (p_class->base_type.is_resolving()) {
+ push_error(vformat(R"(Could not resolve class "%s": Cyclic reference.)", type_from_metatype(p_class->get_datatype()).to_string()), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ if (!p_class->base_type.has_no_type()) {
+ // Already resolved.
return OK;
}
- if (p_class == parser->head) {
- if (p_class->identifier) {
- p_class->fqcn = p_class->identifier->name;
- } else {
- p_class->fqcn = parser->script_path;
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return ERR_PARSE_ERROR;
}
- } else {
- p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name);
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not parse script "%s": %s.)", script_path, error_names[err]), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ ERR_FAIL_COND_V_MSG(!parser_ref->get_parser()->has_class(p_class), ERR_PARSE_ERROR, R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_inheritance(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->fqcn), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
}
+ GDScriptParser::ClassNode *previous_class = parser->current_class;
+ parser->current_class = p_class;
+
if (p_class->identifier) {
StringName class_name = p_class->identifier->name;
- if (class_exists(class_name)) {
+ if (GDScriptParser::get_builtin_type(class_name) < Variant::VARIANT_MAX) {
+ push_error(vformat(R"(Class "%s" hides a built-in type.)", class_name), p_class->identifier);
+ } else if (class_exists(class_name)) {
push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier);
} else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) {
push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier);
@@ -234,7 +341,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
}
}
- GDScriptParser::DataType result;
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_class->base_type = resolving_datatype;
// Set datatype for class.
GDScriptParser::DataType class_type;
@@ -247,6 +356,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.builtin_type = Variant::OBJECT;
p_class->set_datatype(class_type);
+ GDScriptParser::DataType result;
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
@@ -260,21 +370,21 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (!p_class->extends_path.is_empty()) {
if (p_class->extends_path.is_relative_path()) {
- p_class->extends_path = class_type.script_path.get_base_dir().plus_file(p_class->extends_path).simplify_path();
+ p_class->extends_path = class_type.script_path.get_base_dir().path_join(p_class->extends_path).simplify_path();
}
- Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> ext_parser = get_parser_for(p_class->extends_path);
+ if (ext_parser.is_null()) {
push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ext_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class);
return err;
}
- base = parser->get_parser()->head->get_datatype();
+ base = ext_parser->get_parser()->head->get_datatype();
} else {
if (p_class->extends.is_empty()) {
push_error("Could not resolve an empty super class path.", p_class);
@@ -289,48 +399,50 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (base_path == parser->script_path) {
base = parser->head->get_datatype();
} else {
- Ref<GDScriptParserRef> parser = get_parser_for(base_path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> base_parser = get_parser_for(base_path);
+ if (base_parser.is_null()) {
push_error(vformat(R"(Could not resolve super class "%s".)", name), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = base_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
- base = parser->get_parser()->head->get_datatype();
+ base = base_parser->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
- if (info.path.get_extension().to_lower() != ".gd") {
+ if (info.path.get_extension().to_lower() != GDScriptLanguage::get_singleton()->get_extension()) {
push_error(vformat(R"(Singleton %s is not a GDScript.)", info.name), p_class);
return ERR_PARSE_ERROR;
}
- Ref<GDScriptParserRef> parser = get_parser_for(info.path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> info_parser = get_parser_for(info.path);
+ if (info_parser.is_null()) {
push_error(vformat(R"(Could not parse singleton from "%s".)", info.path), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = info_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
- } else if (class_exists(name) && ClassDB::can_instantiate(name)) {
+ base = info_parser->get_parser()->head->get_datatype();
+ } else if (class_exists(name)) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
// Look for other classes in script.
- GDScriptParser::ClassNode *look_class = p_class;
bool found = false;
- while (look_class != nullptr) {
+ List<GDScriptParser::ClassNode *> script_classes;
+ get_class_node_current_scope_classes(p_class, &script_classes);
+ for (GDScriptParser::ClassNode *look_class : script_classes) {
if (look_class->identifier && look_class->identifier->name == name) {
if (!look_class->get_datatype().is_set()) {
- Error err = resolve_inheritance(look_class, false);
+ Error err = resolve_class_inheritance(look_class, p_class);
if (err) {
return err;
}
@@ -339,19 +451,12 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
found = true;
break;
}
- if (look_class->members_indices.has(name) && look_class->get_member(name).type == GDScriptParser::ClassNode::Member::CLASS) {
- GDScriptParser::ClassNode::Member member = look_class->get_member(name);
- if (!member.m_class->get_datatype().is_set()) {
- Error err = resolve_inheritance(member.m_class, false);
- if (err) {
- return err;
- }
- }
- base = member.m_class->get_datatype();
+ if (look_class->has_member(name)) {
+ resolve_class_member(look_class, name, p_class);
+ base = look_class->get_member(name).get_datatype();
found = true;
break;
}
- look_class = look_class->outer;
}
if (!found) {
@@ -384,7 +489,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
result = base;
}
- if (!result.is_set()) {
+ if (!result.is_set() || result.has_no_type()) {
// TODO: More specific error messages.
push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->identifier == nullptr ? "<main>" : p_class->identifier->name), p_class);
return ERR_PARSE_ERROR;
@@ -404,10 +509,21 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
+ parser->current_class = previous_class;
+
+ return OK;
+}
+
+Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ Error err = resolve_class_inheritance(p_class);
+ if (err) {
+ return err;
+ }
+
if (p_recursive) {
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
- Error err = resolve_inheritance(p_class->members[i].m_class, true);
+ err = resolve_class_inheritance(p_class->members[i].m_class, true);
if (err) {
return err;
}
@@ -419,14 +535,29 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
}
GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::TypeNode *p_type) {
- GDScriptParser::DataType result;
+ GDScriptParser::DataType bad_type;
+ bad_type.kind = GDScriptParser::DataType::VARIANT;
+ bad_type.type_source = GDScriptParser::DataType::INFERRED;
if (p_type == nullptr) {
- result.kind = GDScriptParser::DataType::VARIANT;
- return result;
+ return bad_type;
+ }
+
+ if (p_type->get_datatype().is_resolving()) {
+ push_error(R"(Could not resolve datatype: Cyclic reference.)", p_type);
+ return bad_type;
+ }
+
+ if (!p_type->get_datatype().has_no_type()) {
+ return p_type->get_datatype();
}
- result.type_source = result.ANNOTATED_EXPLICIT;
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_type->set_datatype(resolving_datatype);
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.builtin_type = Variant::OBJECT;
if (p_type->type_chain.is_empty()) {
@@ -440,39 +571,28 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name;
if (first == SNAME("Variant")) {
- result.kind = GDScriptParser::DataType::VARIANT;
if (p_type->type_chain.size() > 1) {
- push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
+ // TODO: Variant does actually have a nested Type though.
+ push_error(R"(Variant doesn't contain nested types.)", p_type->type_chain[1]);
+ return bad_type;
}
- return result;
- }
-
- if (first == SNAME("Object")) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ } else if (first == SNAME("Object")) {
+ // Object is treated like a native type, not a built-in.
result.kind = GDScriptParser::DataType::NATIVE;
result.native_type = SNAME("Object");
- if (p_type->type_chain.size() > 1) {
- push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
- }
- return result;
- }
-
- if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
+ } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
// Built-in types.
if (p_type->type_chain.size() > 1) {
push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
+ return bad_type;
}
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = GDScriptParser::get_builtin_type(first);
if (result.builtin_type == Variant::ARRAY) {
- GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
-
+ GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
- container_type.is_meta_type = false;
- container_type.is_constant = false;
result.set_container_element_type(container_type);
}
}
@@ -484,84 +604,88 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (parser->script_path == ScriptServer::get_global_class_path(first)) {
result = parser->head->get_datatype();
} else {
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
- if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
- return GDScriptParser::DataType();
+ String path = ScriptServer::get_global_class_path(first);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
+ return bad_type;
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result = make_script_meta_type(ResourceLoader::load(path, "Script"));
}
- result = ref->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
Ref<GDScriptParserRef> ref = get_parser_for(autoload.path);
- if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
result = ref->get_parser()->head->get_datatype();
} else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
// Native enum in current class.
- result = make_native_enum_type(parser->current_class->base_type.native_type, first);
+ result = make_native_enum_type(first, parser->current_class->base_type.native_type);
} else {
// Classes in current scope.
- GDScriptParser::ClassNode *script_class = parser->current_class;
+ List<GDScriptParser::ClassNode *> script_classes;
bool found = false;
- while (!found && script_class != nullptr) {
+ get_class_node_current_scope_classes(parser->current_class, &script_classes);
+ for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (found) {
+ break;
+ }
+
if (script_class->identifier && script_class->identifier->name == first) {
result = script_class->get_datatype();
- found = true;
break;
}
if (script_class->members_indices.has(first)) {
- GDScriptParser::ClassNode::Member member = script_class->members[script_class->members_indices[first]];
+ resolve_class_member(script_class, first, p_type);
+
+ GDScriptParser::ClassNode::Member member = script_class->get_member(first);
switch (member.type) {
case GDScriptParser::ClassNode::Member::CLASS:
- result = member.m_class->get_datatype();
+ result = member.get_datatype();
found = true;
break;
case GDScriptParser::ClassNode::Member::ENUM:
- result = member.m_enum->get_datatype();
+ result = member.get_datatype();
found = true;
break;
case GDScriptParser::ClassNode::Member::CONSTANT:
- if (member.constant->get_datatype().is_meta_type) {
- result = member.constant->get_datatype();
- result.is_meta_type = false;
+ if (member.get_datatype().is_meta_type) {
+ result = member.get_datatype();
found = true;
break;
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
if (gdscript.is_valid()) {
- Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_path());
- if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_path()), p_type);
- return GDScriptParser::DataType();
+ Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type);
+ return bad_type;
}
result = ref->get_parser()->head->get_datatype();
- result.is_meta_type = false;
} else {
- Ref<GDScript> script = member.constant->initializer->reduced_value;
- result.kind = GDScriptParser::DataType::SCRIPT;
- result.builtin_type = Variant::OBJECT;
- result.script_type = script;
- result.script_path = script->get_path();
- result.native_type = script->get_instance_base_type();
+ result = make_script_meta_type(member.constant->initializer->reduced_value);
}
+ found = true;
break;
}
[[fallthrough]];
default:
push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
}
- script_class = script_class->outer;
}
}
if (!result.is_set()) {
push_error(vformat(R"("%s" was not found in the current scope.)", first), p_type);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
if (p_type->type_chain.size() > 1) {
@@ -572,12 +696,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = p_type->type_chain[i]->get_datatype();
if (!result.is_set()) {
push_error(vformat(R"(Could not find type "%s" under base "%s".)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
} else if (!result.is_meta_type) {
push_error(vformat(R"(Member "%s" under base "%s" is not a valid type.)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
}
} else if (result.kind == GDScriptParser::DataType::NATIVE) {
@@ -585,14 +707,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
if (p_type->type_chain.size() > 2) {
push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
+ return bad_type;
} else {
- result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name);
+ result = make_native_enum_type(p_type->type_chain[1]->name, result.native_type);
}
+ } else {
+ push_error(vformat(R"(Could not find type "%s" in "%s".)", p_type->type_chain[1]->name, first), p_type->type_chain[1]);
+ return bad_type;
}
} else {
push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
}
@@ -604,98 +729,76 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return result;
}
-void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class) {
- if (p_class->resolved_interface) {
+void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source) {
+ ERR_FAIL_COND(!p_class->has_member(p_name));
+ resolve_class_member(p_class, p_class->members_indices[p_name], p_source);
+}
+
+void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source) {
+ ERR_FAIL_INDEX(p_index, p_class->members.size());
+
+ GDScriptParser::ClassNode::Member &member = p_class->members.write[p_index];
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = member.get_source_node();
+ }
+
+ if (member.get_datatype().is_resolving()) {
+ push_error(vformat(R"(Could not resolve member "%s": Cyclic reference.)", member.get_name()), p_source);
return;
}
- p_class->resolved_interface = true;
- GDScriptParser::ClassNode *previous_class = parser->current_class;
- parser->current_class = p_class;
+ if (member.get_datatype().is_set()) {
+ return;
+ }
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source);
+ return;
+ }
- switch (member.type) {
- case GDScriptParser::ClassNode::Member::VARIABLE: {
- check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s (While resolving "%s").)", script_path, error_names[err], member.get_name()), p_source);
+ return;
+ }
- GDScriptParser::DataType datatype;
- datatype.kind = GDScriptParser::DataType::VARIANT;
- datatype.type_source = GDScriptParser::DataType::UNDETECTED;
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
- GDScriptParser::DataType specified_type;
- if (member.variable->datatype_specifier != nullptr) {
- specified_type = resolve_datatype(member.variable->datatype_specifier);
- specified_type.is_meta_type = false;
- }
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
- if (member.variable->initializer != nullptr) {
- member.variable->set_datatype(datatype); // Allow recursive usage.
- reduce_expression(member.variable->initializer);
- if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
- // Typed array.
- GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
- // Can only infer typed array if it has elements.
- if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
- update_array_literal_element_type(specified_type, array);
- }
- }
- datatype = member.variable->initializer->get_datatype();
- if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
- datatype.type_source = GDScriptParser::DataType::INFERRED;
- }
- }
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_member(p_class, p_index);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve member "%s".)", member.get_name()), p_source);
+ }
- // Check if initializer is an unset identifier (ie: a variable within scope, but declared below)
- if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) {
- if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) {
- GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer);
- push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer);
- } else {
- ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
- }
- }
+ return;
+ }
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ // If it's already resolving, that's ok.
+ if (!p_class->base_type.is_resolving()) {
+ Error err = resolve_class_inheritance(p_class);
+ if (err) {
+ return;
+ }
+ }
- 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.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
- }
- }
- } 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;
- }
+ GDScriptParser::ClassNode *previous_class = parser->current_class;
+ parser->current_class = p_class;
- datatype.is_constant = false;
- member.variable->set_datatype(datatype);
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+
+ {
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
+ member.variable->set_datatype(resolving_datatype);
+ resolve_variable(member.variable, false);
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
@@ -704,50 +807,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} break;
case GDScriptParser::ClassNode::Member::CONSTANT: {
check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
-
- reduce_expression(member.constant->initializer);
-
- GDScriptParser::DataType specified_type;
-
- if (member.constant->datatype_specifier != nullptr) {
- specified_type = resolve_datatype(member.constant->datatype_specifier);
- specified_type.is_meta_type = false;
- }
-
- GDScriptParser::DataType datatype;
- if (member.constant->initializer) {
- datatype = member.constant->initializer->get_datatype();
- if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
- GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer);
- const_fold_array(array);
-
- // Can only infer typed array if it has elements.
- if (array->elements.size() > 0 || (member.constant->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
- update_array_literal_element_type(specified_type, array);
- }
- } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
- }
-
- if (!member.constant->initializer->is_constant) {
- push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
- }
-
- if (member.constant->datatype_specifier != nullptr) {
- datatype = specified_type;
-
- if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
- push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
- } else if (datatype.builtin_type == Variant::INT && member.constant->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
- parser->push_warning(member.constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
- }
- }
- }
- datatype.is_constant = true;
-
- member.constant->set_datatype(datatype);
+ member.constant->set_datatype(resolving_datatype);
+ resolve_constant(member.constant, false);
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
@@ -757,18 +818,20 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::SIGNAL: {
check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal);
+ member.signal->set_datatype(resolving_datatype);
+
+ // This is the _only_ way to declare a signal. Therefore, we can generate its
+ // MethodInfo inline so it's a tiny bit more efficient.
+ MethodInfo mi = MethodInfo(member.signal->identifier->name);
+
for (int j = 0; j < member.signal->parameters.size(); j++) {
- GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier);
- signal_type.is_meta_type = false;
- member.signal->parameters[j]->set_datatype(signal_type);
+ GDScriptParser::ParameterNode *param = member.signal->parameters[j];
+ GDScriptParser::DataType param_type = type_from_metatype(resolve_datatype(param->datatype_specifier));
+ param->set_datatype(param_type);
+ mi.arguments.push_back(PropertyInfo(param_type.builtin_type, param->identifier->name));
+ // TODO: add signal parameter default values
}
- // TODO: Make MethodInfo from signal.
- GDScriptParser::DataType signal_type;
- signal_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- signal_type.kind = GDScriptParser::DataType::BUILTIN;
- signal_type.builtin_type = Variant::SIGNAL;
-
- member.signal->set_datatype(signal_type);
+ member.signal->set_datatype(make_signal_type(mi));
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
@@ -778,18 +841,13 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::ENUM: {
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
- GDScriptParser::DataType enum_type;
- enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- enum_type.kind = GDScriptParser::DataType::ENUM;
- enum_type.builtin_type = Variant::DICTIONARY;
- enum_type.enum_type = member.m_enum->identifier->name;
- enum_type.native_type = p_class->fqcn + "." + member.m_enum->identifier->name;
- enum_type.is_meta_type = true;
- enum_type.is_constant = true;
+ member.m_enum->set_datatype(resolving_datatype);
+ GDScriptParser::DataType enum_type = make_enum_type(member.m_enum->identifier->name, p_class->fqcn, true);
- // Enums can't be nested, so we can safely override this.
+ const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
+ Dictionary dictionary;
for (int j = 0; j < member.m_enum->values.size(); j++) {
GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j];
@@ -813,11 +871,14 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
enum_type.enum_values[element.identifier->name] = element.value;
+ dictionary[String(element.identifier->name)] = element.value;
}
- current_enum = nullptr;
+ current_enum = prev_enum;
+ dictionary.set_read_only(true);
member.m_enum->set_datatype(enum_type);
+ member.m_enum->dictionary = dictionary;
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
@@ -825,15 +886,18 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
- resolve_function_signature(member.function);
+ resolve_function_signature(member.function, p_source);
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ member.enum_value.identifier->set_datatype(resolving_datatype);
+
if (member.enum_value.custom_value) {
check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value);
+ const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.enum_value.parent_enum;
reduce_expression(member.enum_value.custom_value);
- current_enum = nullptr;
+ current_enum = prev_enum;
if (!member.enum_value.custom_value->is_constant) {
push_error(R"(Enum values must be constant.)", member.enum_value.custom_value);
@@ -847,18 +911,26 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum);
if (member.enum_value.index > 0) {
- member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1;
+ const GDScriptParser::EnumNode::Value &prev_value = member.enum_value.parent_enum->values[member.enum_value.index - 1];
+ resolve_class_member(p_class, prev_value.identifier->name, member.enum_value.identifier);
+ member.enum_value.value = prev_value.value + 1;
} else {
member.enum_value.value = 0;
}
member.enum_value.resolved = true;
}
+
// Also update the original references.
- member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value;
- p_class->members.write[i].enum_value = member.enum_value;
+ member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
+
+ member.enum_value.identifier->set_datatype(make_enum_type(UNNAMED_ENUM, p_class->fqcn, false));
} break;
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
+ // If it's already resolving, that's ok.
+ if (!member.m_class->base_type.is_resolving()) {
+ resolve_class_inheritance(member.m_class, p_source);
+ }
break;
case GDScriptParser::ClassNode::Member::GROUP:
// No-op, but needed to silence warnings.
@@ -869,28 +941,123 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
- // Recurse nested classes.
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
- continue;
+ parser->current_class = previous_class;
+}
+
+void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
+ if (!p_class->resolved_interface) {
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_interface(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ }
+
+ return;
+ }
+ p_class->resolved_interface = true;
+
+ if (resolve_class_inheritance(p_class) != OK) {
+ return;
}
- resolve_class_interface(member.m_class);
+ GDScriptParser::DataType base_type = p_class->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ GDScriptParser::ClassNode *base_class = base_type.class_type;
+ resolve_class_interface(base_class, p_class);
+ }
+
+ for (int i = 0; i < p_class->members.size(); i++) {
+ resolve_class_member(p_class, i);
+ }
}
+}
- parser->current_class = previous_class;
+void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ resolve_class_interface(p_class);
+
+ if (p_recursive) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ resolve_class_interface(member.m_class, true);
+ }
+ }
+ }
}
-void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
+void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
if (p_class->resolved_body) {
return;
}
+
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_body(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ }
+
+ return;
+ }
+
p_class->resolved_body = true;
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
+ resolve_class_interface(p_class, p_source);
+
+ GDScriptParser::DataType base_type = p_class->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ GDScriptParser::ClassNode *base_class = base_type.class_type;
+ resolve_class_body(base_class, p_class);
+ }
+
// Do functions and properties now.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
@@ -933,18 +1100,6 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
}
- parser->current_class = previous_class;
-
- // Recurse nested classes.
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
- continue;
- }
-
- resolve_class_body(member.m_class);
- }
-
// Check unused variables and datatypes of property getters and setters.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
@@ -973,21 +1128,26 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
if (getter_function == nullptr) {
push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable);
-
- } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) {
- push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
-
- } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) {
- push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
-
} else {
- has_valid_getter = true;
+ GDScriptParser::DataType return_datatype = getter_function->datatype;
+ if (getter_function->return_type != nullptr) {
+ return_datatype = getter_function->return_type->datatype;
+ return_datatype.is_meta_type = false;
+ }
+ if (getter_function->parameters.size() != 0 || return_datatype.has_no_type()) {
+ push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
+ } else if (!is_type_compatible(member.variable->datatype, return_datatype, true)) {
+ push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", return_datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+
+ } else {
+ has_valid_getter = true;
#ifdef DEBUG_ENABLED
- if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) {
- parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
- }
+ if (member.variable->datatype.builtin_type == Variant::INT && return_datatype.builtin_type == Variant::FLOAT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
#endif
+ }
}
}
@@ -1027,28 +1187,41 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
}
}
+
+ parser->current_class = previous_class;
+}
+
+void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ resolve_class_body(p_class);
+
+ if (p_recursive) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ resolve_class_body(member.m_class, true);
+ }
+ }
+ }
}
-void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
+void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) {
ERR_FAIL_COND_MSG(p_node == nullptr, "Trying to resolve type of a null node.");
switch (p_node->type) {
case GDScriptParser::Node::NONE:
break; // Unreachable.
case GDScriptParser::Node::CLASS:
- resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node));
- resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node));
+ if (OK == resolve_class_inheritance(static_cast<GDScriptParser::ClassNode *>(p_node), true)) {
+ resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node), true);
+ resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node), true);
+ }
break;
case GDScriptParser::Node::CONSTANT:
- resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
+ resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node), true);
break;
case GDScriptParser::Node::FOR:
resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
break;
- case GDScriptParser::Node::FUNCTION:
- resolve_function_signature(static_cast<GDScriptParser::FunctionNode *>(p_node));
- resolve_function_body(static_cast<GDScriptParser::FunctionNode *>(p_node));
- break;
case GDScriptParser::Node::IF:
resolve_if(static_cast<GDScriptParser::IfNode *>(p_node));
break;
@@ -1056,7 +1229,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
resolve_suite(static_cast<GDScriptParser::SuiteNode *>(p_node));
break;
case GDScriptParser::Node::VARIABLE:
- resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node));
+ resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node), true);
break;
case GDScriptParser::Node::WHILE:
resolve_while(static_cast<GDScriptParser::WhileNode *>(p_node));
@@ -1102,12 +1275,13 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
case GDScriptParser::Node::SUBSCRIPT:
case GDScriptParser::Node::TERNARY_OPERATOR:
case GDScriptParser::Node::UNARY_OPERATOR:
- reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), true);
+ reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), p_is_root);
break;
case GDScriptParser::Node::BREAK:
case GDScriptParser::Node::BREAKPOINT:
case GDScriptParser::Node::CONTINUE:
case GDScriptParser::Node::ENUM:
+ case GDScriptParser::Node::FUNCTION:
case GDScriptParser::Node::PASS:
case GDScriptParser::Node::SIGNAL:
// Nothing to do.
@@ -1119,7 +1293,18 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno
// TODO: Add second validation function for annotations, so they can use checked types.
}
-void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function) {
+void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) {
+ if (p_source == nullptr) {
+ p_source = p_function;
+ }
+
+ StringName function_name = p_function->identifier != nullptr ? p_function->identifier->name : StringName();
+
+ if (p_function->get_datatype().is_resolving()) {
+ push_error(vformat(R"(Could not resolve function "%s": Cyclic reference.)", function_name), p_source);
+ return;
+ }
+
if (p_function->resolved_signature) {
return;
}
@@ -1128,6 +1313,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
+ GDScriptParser::DataType prev_datatype = p_function->get_datatype();
+
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_function->set_datatype(resolving_datatype);
+
#ifdef TOOLS_ENABLED
int default_value_count = 0;
#endif // TOOLS_ENABLED
@@ -1136,22 +1327,24 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
resolve_parameter(p_function->parameters[i]);
#ifdef DEBUG_ENABLED
if (p_function->parameters[i]->usages == 0 && !String(p_function->parameters[i]->identifier->name).begins_with("_")) {
- parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
+ parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, function_name, p_function->parameters[i]->identifier->name);
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
- if (p_function->parameters[i]->default_value) {
+ if (p_function->parameters[i]->initializer) {
default_value_count++;
- if (p_function->parameters[i]->default_value->is_constant) {
- p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ if (p_function->parameters[i]->initializer->is_constant) {
+ p_function->default_arg_values.push_back(p_function->parameters[i]->initializer->reduced_value);
+ } else {
+ p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
}
#endif // TOOLS_ENABLED
}
- if (p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) {
+ if (!p_is_lambda && function_name == GDScriptLanguage::get_singleton()->strings._init) {
// Constructor.
GDScriptParser::DataType return_type = parser->current_class->get_datatype();
return_type.is_meta_type = false;
@@ -1164,7 +1357,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
} else {
if (p_function->return_type != nullptr) {
- p_function->set_datatype(resolve_datatype(p_function->return_type));
+ p_function->set_datatype(type_from_metatype(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.
@@ -1183,7 +1376,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
int default_par_count = 0;
bool is_static = false;
bool is_vararg = false;
- if (get_function_signature(p_function, false, base_type, p_function->identifier->name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg)) {
+ if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg)) {
bool valid = p_function->is_static == is_static;
valid = valid && parent_return_type == p_function->get_datatype();
@@ -1198,11 +1391,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!valid) {
// Compute parent signature as a string to show in the error message.
- String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
- if (parent_signature == "null") {
- parent_signature = "void";
- }
- parent_signature += " " + p_function->identifier->name.operator String() + "(";
+ String parent_signature = String(function_name) + "(";
int j = 0;
for (const GDScriptParser::DataType &par_type : parameters_types) {
if (j > 0) {
@@ -1219,17 +1408,29 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
j++;
}
- parent_signature += ")";
+ parent_signature += ") -> ";
+
+ const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ if (return_type == "null") {
+ parent_signature += "void";
+ } else {
+ parent_signature += return_type;
+ }
+
push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function);
}
}
#endif // TOOLS_ENABLED
}
+ if (p_function->get_datatype().is_resolving()) {
+ p_function->set_datatype(prev_datatype);
+ }
+
parser->current_function = previous_function;
}
-void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function) {
+void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda) {
if (p_function->resolved_body) {
return;
}
@@ -1247,7 +1448,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
} else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) {
- if (!p_function->body->has_return && p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init) {
+ if (!p_function->body->has_return && (p_is_lambda || p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init)) {
push_error(R"(Not all code paths return a value.)", p_function);
}
}
@@ -1306,6 +1507,131 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
}
}
+void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind) {
+ GDScriptParser::DataType type;
+ type.kind = GDScriptParser::DataType::VARIANT;
+
+ bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
+
+ GDScriptParser::DataType specified_type;
+ bool has_specified_type = p_assignable->datatype_specifier != nullptr;
+ if (has_specified_type) {
+ specified_type = type_from_metatype(resolve_datatype(p_assignable->datatype_specifier));
+ type = specified_type;
+ }
+
+ if (p_assignable->initializer != nullptr) {
+ reduce_expression(p_assignable->initializer);
+
+ if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
+ if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ }
+
+ if (is_constant) {
+ if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true);
+ } else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true);
+ }
+ if (!p_assignable->initializer->is_constant) {
+ push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
+ }
+ }
+
+ GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype();
+
+ if (p_assignable->infer_datatype) {
+ if (!initializer_type.is_set() || initializer_type.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+ } else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+ } else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) {
+ push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+ }
+ } else {
+ if (!initializer_type.is_set()) {
+ push_error(vformat(R"(Could not resolve type for %s "%s".)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
+ }
+ }
+
+ if (!has_specified_type) {
+ type = initializer_type;
+
+ if (!type.is_set() || (type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL && !is_constant)) {
+ type.kind = GDScriptParser::DataType::VARIANT;
+ }
+
+ if (p_assignable->infer_datatype || is_constant) {
+ type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ } else {
+ type.type_source = GDScriptParser::DataType::INFERRED;
+ }
+ } else if (!specified_type.is_variant()) {
+ if (initializer_type.is_variant() || !initializer_type.is_hard_type()) {
+ mark_node_unsafe(p_assignable->initializer);
+ p_assignable->use_conversion_assign = true;
+ if (!initializer_type.is_variant() && !is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
+ downgrade_node_type_source(p_assignable->initializer);
+ }
+ } else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
+ if (!is_constant && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
+ mark_node_unsafe(p_assignable->initializer);
+ p_assignable->use_conversion_assign = true;
+ } else {
+ push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
+ }
+#ifdef DEBUG_ENABLED
+ } else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ }
+ }
+
+ type.is_constant = is_constant;
+ p_assignable->set_datatype(type);
+}
+
+void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local) {
+ static constexpr const char *kind = "variable";
+ resolve_assignable(p_variable, kind);
+
+#ifdef DEBUG_ENABLED
+ if (p_is_local) {
+ if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
+ parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
+ } else if (p_variable->assignments == 0) {
+ parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
+ }
+
+ is_shadowing(p_variable->identifier, kind);
+ }
+#endif
+}
+
+void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local) {
+ static constexpr const char *kind = "constant";
+ resolve_assignable(p_constant, kind);
+
+#ifdef DEBUG_ENABLED
+ if (p_is_local) {
+ if (p_constant->usages == 0) {
+ parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
+ }
+
+ is_shadowing(p_constant->identifier, kind);
+ }
+#endif
+}
+
+void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
+ static constexpr const char *kind = "parameter";
+ resolve_assignable(p_parameter, kind);
+}
+
void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
reduce_expression(p_if->condition);
@@ -1365,7 +1691,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (all_is_constant) {
switch (args.size()) {
case 1:
- reduced = args[0];
+ reduced = (int32_t)args[0];
break;
case 2:
reduced = Vector2i(args[0], args[1]);
@@ -1396,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (list_resolved) {
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
variable_type.kind = GDScriptParser::DataType::BUILTIN;
- variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
- p_for->variable->set_datatype(variable_type);
+ variable_type.builtin_type = Variant::INT;
} else if (p_for->list) {
- resolve_node(p_for->list);
- if (p_for->list->datatype.has_container_element_type()) {
- variable_type = p_for->list->datatype.get_container_element_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else if (p_for->list->datatype.is_typed_container_type()) {
- variable_type = p_for->list->datatype.get_typed_container_type();
- variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else {
- // Last resort
- // TODO: Must other cases be handled? Must we mark as unsafe?
- variable_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ resolve_node(p_for->list, false);
+ GDScriptParser::DataType list_type = p_for->list->get_datatype();
+ if (!list_type.is_hard_type()) {
+ mark_node_unsafe(p_for->list);
+ }
+ if (list_type.is_variant()) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_for->list);
+ } else if (list_type.has_container_element_type()) {
+ variable_type = list_type.get_container_element_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.is_typed_container_type()) {
+ variable_type = list_type.get_typed_container_type();
+ variable_type.type_source = list_type.type_source;
+ } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = list_type.builtin_type;
+ } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::INT;
+ } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) {
+ variable_type.type_source = list_type.type_source;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::FLOAT;
+ } else if (list_type.builtin_type == Variant::OBJECT) {
+ GDScriptParser::DataType return_type;
+ List<GDScriptParser::DataType> par_types;
+ int default_arg_count = 0;
+ bool is_static = false;
+ bool is_vararg = false;
+ if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+ variable_type = return_type;
+ variable_type.type_source = list_type.type_source;
+ } else if (!list_type.is_hard_type()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list);
+ }
+ } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) {
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list);
}
}
if (p_for->variable) {
@@ -1427,171 +1784,18 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
- resolve_node(p_while->condition);
+ resolve_node(p_while->condition, false);
resolve_suite(p_while->loop);
p_while->set_datatype(p_while->loop->get_datatype());
}
-void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable) {
- GDScriptParser::DataType type;
- type.kind = GDScriptParser::DataType::VARIANT; // By default.
-
- GDScriptParser::DataType specified_type;
- if (p_variable->datatype_specifier != nullptr) {
- specified_type = resolve_datatype(p_variable->datatype_specifier);
- specified_type.is_meta_type = false;
- }
-
- if (p_variable->initializer != nullptr) {
- reduce_expression(p_variable->initializer);
- if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
- // Typed array.
- GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
- // Can only infer typed array if it has elements.
- if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
- update_array_literal_element_type(specified_type, array);
- }
- }
-
- type = p_variable->initializer->get_datatype();
-
- if (p_variable->infer_datatype) {
- type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-
- if (type.has_no_type()) {
- push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value does not have a set type.)", p_variable->identifier->name), p_variable->initializer);
- } else if (type.is_variant()) {
- push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is a variant. Use explicit "Variant" type if this is intended.)", p_variable->identifier->name), p_variable->initializer);
- } else if (type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_variable->identifier->name), p_variable->initializer);
- }
- } else {
- type.type_source = GDScriptParser::DataType::INFERRED;
- }
-#ifdef DEBUG_ENABLED
- if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name);
- }
-#endif
- }
-
- if (p_variable->datatype_specifier != nullptr) {
- type = specified_type;
- type.is_meta_type = false;
-
- if (p_variable->initializer != nullptr) {
- if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_variable->initializer);
- p_variable->use_conversion_assign = true;
- }
-#ifdef DEBUG_ENABLED
- } else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
- parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
- }
- if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) {
- // TODO: Warn unsafe assign.
- mark_node_unsafe(p_variable->initializer);
- p_variable->use_conversion_assign = true;
- }
- }
- } else if (p_variable->infer_datatype) {
- if (type.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of variable "%s" because the initial value doesn't have a set type.)", p_variable->identifier->name), p_variable->identifier);
- }
- type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- }
-
- type.is_constant = false;
- p_variable->set_datatype(type);
-
-#ifdef DEBUG_ENABLED
- if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
- parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
- } else if (p_variable->assignments == 0) {
- parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
- }
-
- is_shadowing(p_variable->identifier, "variable");
-#endif
-}
-
-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) {
- 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));
- }
-
- if (!p_constant->initializer->is_constant) {
- push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
- }
-
- type = p_constant->initializer->get_datatype();
-
-#ifdef DEBUG_ENABLED
- if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
- }
-#endif
- }
-
- if (p_constant->datatype_specifier != nullptr) {
- 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
- } else if (explicit_type.builtin_type == Variant::INT && type.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
- }
- type = explicit_type;
- } else if (p_constant->infer_datatype) {
- if (type.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of constant "%s" because the initial value doesn't have a set type.)", p_constant->identifier->name), p_constant->identifier);
- }
- type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- }
-
- type.is_constant = true;
- p_constant->set_datatype(type);
-
-#ifdef DEBUG_ENABLED
- if (p_constant->usages == 0) {
- parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
- }
-
- is_shadowing(p_constant->identifier, "constant");
-#endif
-}
-
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
reduce_expression(p_assert->message);
- if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) {
- push_error(R"(Expected constant string for assert error message.)", p_assert->message);
+ if (!p_assert->message->get_datatype().has_no_type() && (p_assert->message->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_assert->message->get_datatype().builtin_type != Variant::STRING)) {
+ push_error(R"(Expected string for assert error message.)", p_assert->message);
}
}
@@ -1697,41 +1901,6 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->set_datatype(result);
}
-void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
- GDScriptParser::DataType result;
- result.kind = GDScriptParser::DataType::VARIANT;
-
- if (p_parameter->default_value != nullptr) {
- reduce_expression(p_parameter->default_value);
- result = p_parameter->default_value->get_datatype();
- if (p_parameter->infer_datatype) {
- result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- } else {
- result.type_source = GDScriptParser::DataType::INFERRED;
- }
- result.is_constant = false;
- }
-
- if (p_parameter->datatype_specifier != nullptr) {
- result = resolve_datatype(p_parameter->datatype_specifier);
- result.is_meta_type = false;
-
- if (p_parameter->default_value != nullptr) {
- if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) {
- push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
- } else if (p_parameter->default_value->get_datatype().is_variant()) {
- mark_node_unsafe(p_parameter);
- }
- }
- }
-
- if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) {
- push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value);
- }
-
- p_parameter->set_datatype(result);
-}
-
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
GDScriptParser::DataType result;
@@ -1751,6 +1920,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
}
}
+ if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
+ push_error("A void function cannot return a value.", p_return);
+ }
result = p_return->return_value->get_datatype();
} else {
// Return type is null by default.
@@ -1812,7 +1984,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression));
break;
case GDScriptParser::Node::CALL:
- reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), p_is_root);
+ reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), false, p_is_root);
break;
case GDScriptParser::Node::CAST:
reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression));
@@ -1948,6 +2120,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+ if (assignee_type.is_constant || (p_assignment->assignee->type == GDScriptParser::Node::SUBSCRIPT && static_cast<GDScriptParser::SubscriptNode *>(p_assignment->assignee)->base->is_constant)) {
+ push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
+ }
+
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) {
update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
@@ -1955,94 +2131,88 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
- if (assignee_type.is_constant) {
- push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
- }
-
+ bool assignee_is_variant = assignee_type.is_variant();
+ bool assignee_is_hard = assignee_type.is_hard_type();
+ bool assigned_is_variant = assigned_value_type.is_variant();
+ bool assigned_is_hard = assigned_value_type.is_hard_type();
bool compatible = true;
+ bool downgrades_assignee = false;
+ bool downgrades_assigned = false;
GDScriptParser::DataType op_type = assigned_value_type;
- if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE && !op_type.is_variant()) {
op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
+
+ if (assignee_is_variant) {
+ // variant assignee
+ mark_node_unsafe(p_assignment);
+ } else if (!compatible) {
+ // incompatible hard types and non-variant assignee
+ mark_node_unsafe(p_assignment);
+ if (assigned_is_variant) {
+ // incompatible hard non-variant assignee and hard variant assigned
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // incompatible hard non-variant types
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
+ }
+ } else if (op_type.type_source == GDScriptParser::DataType::UNDETECTED && !assigned_is_variant) {
+ // incompatible non-variant types (at least one weak)
+ downgrades_assignee = !assignee_is_hard;
+ downgrades_assigned = !assigned_is_hard;
+ }
}
p_assignment->set_datatype(op_type);
- if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
- if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
- if (!compatible) {
- if (assignee_type.is_hard_type()) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_assignment);
+ if (assignee_is_variant) {
+ if (!assignee_is_hard) {
+ // weak variant assignee
+ mark_node_unsafe(p_assignment);
+ }
+ } else {
+ if (assignee_is_hard && !assigned_is_hard) {
+ // hard non-variant assignee and weak assigned
+ mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
+ downgrades_assigned = downgrades_assigned || (!assigned_is_variant && !is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value));
+ } else if (compatible) {
+ if (op_type.is_variant()) {
+ // non-variant assignee and variant result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ // hard non-variant assignee and variant result
+ p_assignment->use_conversion_assign = true;
+ } else {
+ // weak non-variant assignee and variant result
+ downgrades_assignee = true;
+ }
+ } else if (!is_type_compatible(assignee_type, op_type, assignee_is_hard, p_assignment->assigned_value)) {
+ // non-variant assignee and incompatible result
+ mark_node_unsafe(p_assignment);
+ if (assignee_is_hard) {
+ if (is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
+ // hard non-variant assignee and maybe compatible result
p_assignment->use_conversion_assign = true;
+ } else {
+ // hard non-variant assignee and incompatible result
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
}
} else {
- // TODO: Warning in this case.
- mark_node_unsafe(p_assignment);
+ // weak non-variant assignee and incompatible result
+ downgrades_assignee = true;
}
}
- } else {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
}
}
- if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
- mark_node_unsafe(p_assignment);
- if (assignee_type.is_hard_type() && !assignee_type.is_variant()) {
- p_assignment->use_conversion_assign = true;
- }
+ if (downgrades_assignee) {
+ downgrade_node_type_source(p_assignment->assignee);
}
-
- if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
- // Change source type so it's not wrongly detected later.
- GDScriptParser::IdentifierNode *identifier = static_cast<GDScriptParser::IdentifierNode *>(p_assignment->assignee);
-
- switch (identifier->source) {
- case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
- GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->parameter_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
- GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
- }
- } break;
- case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
- GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
- if (!id_type.is_hard_type()) {
- id_type.kind = GDScriptParser::DataType::VARIANT;
- id_type.type_source = GDScriptParser::DataType::UNDETECTED;
- identifier->variable_source->set_datatype(id_type);
- }
- } break;
- default:
- // Nothing to do.
- break;
- }
+ if (downgrades_assigned) {
+ downgrade_node_type_source(p_assignment->assigned_value);
}
#ifdef DEBUG_ENABLED
- if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) {
- parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
- } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
+ if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
}
#endif
@@ -2135,7 +2305,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
GDScriptParser::DataType test_type = right_type;
test_type.is_meta_type = false;
- if (!is_type_compatible(test_type, p_binary_op->left_operand->get_datatype(), false)) {
+ if (!is_type_compatible(test_type, left_type, false)) {
push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)"), p_binary_op->left_operand);
p_binary_op->reduced_value = false;
} else {
@@ -2169,11 +2339,11 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
GDScriptParser::DataType test_type = right_type;
test_type.is_meta_type = false;
- if (!is_type_compatible(test_type, p_binary_op->left_operand->get_datatype(), false)) {
+ if (!is_type_compatible(test_type, left_type, false)) {
// Test reverse as well to consider for subtypes.
- if (!is_type_compatible(p_binary_op->left_operand->get_datatype(), test_type, false)) {
- if (p_binary_op->left_operand->get_datatype().is_hard_type()) {
- push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", p_binary_op->left_operand->get_datatype().to_string(), test_type.to_string()), p_binary_op->left_operand);
+ if (!is_type_compatible(left_type, test_type, false)) {
+ if (left_type.is_hard_type()) {
+ push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", left_type.to_string(), test_type.to_string()), p_binary_op->left_operand);
} else {
// TODO: Warning.
mark_node_unsafe(p_binary_op);
@@ -2259,7 +2429,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
- push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be %s but is %s.)", Variant::get_type_name(builtin_type), err.argument + 1,
+ push_error(vformat(R"(Invalid argument for %s constructor: argument %d should be "%s" but is "%s".)", Variant::get_type_name(builtin_type), err.argument + 1,
Variant::get_type_name(Variant::Type(err.expected)), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
break;
@@ -2319,7 +2489,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool types_match = true;
for (int i = 0; i < p_call->arguments.size(); i++) {
- GDScriptParser::DataType par_type = type_from_property(info.arguments[i]);
+ GDScriptParser::DataType par_type = type_from_property(info.arguments[i], true);
if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) {
types_match = false;
@@ -2357,6 +2527,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (GDScriptUtilityFunctions::function_exists(function_name)) {
MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+ if (!p_is_root && !p_is_await && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
+ }
+
if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
// Can call on compilation.
Vector<const Variant *> args;
@@ -2371,8 +2545,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
- type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
+ type_from_property(wrong_arg, true).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
@@ -2400,6 +2574,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (Variant::has_utility_function(function_name)) {
MethodInfo function_info = info_from_utility_func(function_name);
+ if (!p_is_root && !p_is_await && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
+ }
+
if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
@@ -2413,9 +2591,15 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
- type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ String expected_type_name;
+ if (err.argument < function_info.arguments.size()) {
+ expected_type_name = type_from_property(function_info.arguments[err.argument], true).to_string();
+ } else {
+ expected_type_name = Variant::get_type_name((Variant::Type)err.expected);
+ }
+
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*", function_name, err.argument + 1,
+ expected_type_name, p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
@@ -2490,6 +2674,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else {
reduce_expression(subscript->base);
base_type = subscript->base->get_datatype();
+ is_self = subscript->base->type == GDScriptParser::Node::SELF;
}
} else {
// Invalid call. Error already sent in parser.
@@ -2536,12 +2721,34 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
mark_lambda_use_self();
}
+ if (!p_is_root && !p_is_await && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
+ push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call);
+ }
+
+#ifdef DEBUG_ENABLED
+ if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) {
+ parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
+ }
+
+ if (is_static && !base_type.is_meta_type && !(is_self && parser->current_function != nullptr && parser->current_function->is_static)) {
+ String caller_type = String(base_type.native_type);
+
+ if (caller_type.is_empty()) {
+ caller_type = base_type.to_string();
+ }
+
+ parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
+ }
+#endif // DEBUG_ENABLED
+
call_type = return_type;
} else {
bool found = false;
- // Check if the name exists as something else.
- if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
+ // Enums do not have functions other than the built-in dictionary ones.
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
+ push_error(vformat(R"*(Enums only have Dictionary built-in methods. Function "%s()" does not exist for enum "%s".)*", p_call->function_name, base_type.enum_type), p_call->callee);
+ } else if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) { // Check if the name exists as something else.
GDScriptParser::IdentifierNode *callee_id;
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
callee_id = static_cast<GDScriptParser::IdentifierNode *>(p_call->callee);
@@ -2571,7 +2778,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
- push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call);
+ push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
}
}
@@ -2585,33 +2792,61 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
reduce_expression(p_cast->operand);
- GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
+ GDScriptParser::DataType cast_type = type_from_metatype(resolve_datatype(p_cast->cast_type));
if (!cast_type.is_set()) {
mark_node_unsafe(p_cast);
return;
}
- cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (!op_type.is_variant()) {
bool valid = false;
+ bool more_informative_error = false;
if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Enum types are compatible between each other, so it's a safe cast.
- valid = true;
+ // Enum casts are compatible when value from operand exists in target enum
+ if (p_cast->operand->is_constant && p_cast->operand->reduced) {
+ if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
+ valid = true;
+ } else {
+ valid = false;
+ more_informative_error = true;
+ push_error(vformat(R"(Invalid cast. Enum "%s" does not have value corresponding to "%s.%s" (%d).)",
+ cast_type.to_string(), op_type.enum_type,
+ enum_get_value_name(op_type, p_cast->operand->reduced_value), // Can never be null
+ p_cast->operand->reduced_value.operator uint64_t()),
+ p_cast->cast_type);
+ }
+ } else {
+ // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
+ mark_node_unsafe(p_cast);
+ valid = true;
+ }
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Convertint int to enum is always valid.
- valid = true;
+ // Int assignment to enum not valid when exact int assigned is known but is not an enum value
+ if (p_cast->operand->is_constant && p_cast->operand->reduced) {
+ if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
+ valid = true;
+ } else {
+ valid = false;
+ more_informative_error = true;
+ push_error(vformat(R"(Invalid cast. Enum "%s" does not have enum value %d.)", cast_type.to_string(), p_cast->operand->reduced_value.operator uint64_t()), p_cast->cast_type);
+ }
+ } else {
+ // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
+ mark_node_unsafe(p_cast);
+ valid = true;
+ }
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
}
- if (!valid) {
+ if (!valid && !more_informative_error) {
push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type);
}
}
@@ -2629,7 +2864,7 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements;
+ HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, StringLikeVariantComparator> elements;
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
@@ -2676,34 +2911,48 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType type;
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- if (ref.is_null()) {
- push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
- return type;
- }
+ String path = ScriptServer::get_global_class_path(p_class_name);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- if (err) {
- push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
- return type;
+ Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+
+ return ref->get_parser()->head->get_datatype();
+ } else {
+ return make_script_meta_type(ResourceLoader::load(path, "Script"));
}
+}
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::CLASS;
- type.builtin_type = Variant::OBJECT;
- type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
- type.class_type = ref->get_parser()->head;
- type.script_path = ref->get_parser()->script_path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
+void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) {
+ ERR_FAIL_NULL(p_identifier);
+
+ p_identifier->set_datatype(p_identifier_datatype);
+ Error err = OK;
+ GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr();
+ ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path));
+ scr = scr->find_class(p_identifier_datatype.class_type->fqcn);
+ p_identifier->reduced_value = scr;
+ p_identifier->is_constant = true;
}
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
+ if (!p_identifier->get_datatype().has_no_type()) {
+ return;
+ }
+
GDScriptParser::DataType base;
if (p_base == nullptr) {
base = type_from_metatype(parser->current_class->get_datatype());
@@ -2716,25 +2965,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (base.kind == GDScriptParser::DataType::ENUM) {
if (base.is_meta_type) {
if (base.enum_values.has(name)) {
+ p_identifier->set_datatype(type_from_metatype(base));
p_identifier->is_constant = true;
p_identifier->reduced_value = base.enum_values[name];
-
- GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::ENUM;
- result.is_constant = true;
- result.builtin_type = Variant::INT;
- result.native_type = base.native_type;
- result.enum_type = base.enum_type;
- p_identifier->set_datatype(result);
return;
- } else {
- // Consider as a Dictionary, so it can be anything.
- // This will be evaluated in the next if block.
- base.kind = GDScriptParser::DataType::BUILTIN;
- base.builtin_type = Variant::DICTIONARY;
- base.is_meta_type = false;
}
+
+ // Enum does not have this value, return.
+ return;
} else {
push_error(R"(Cannot get property from enum value.)", p_identifier);
return;
@@ -2788,93 +3026,94 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
GDScriptParser::ClassNode *base_class = base.class_type;
+ List<GDScriptParser::ClassNode *> script_classes;
+ bool is_base = true;
- // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
- while (base_class != nullptr) {
- if (base_class->identifier && base_class->identifier->name == name) {
- p_identifier->set_datatype(base_class->get_datatype());
+ if (base_class != nullptr) {
+ get_class_node_current_scope_classes(base_class, &script_classes);
+ }
+
+ for (GDScriptParser::ClassNode *script_class : script_classes) {
+ if (p_base == nullptr && script_class->identifier && script_class->identifier->name == name) {
+ reduce_identifier_from_base_set_class(p_identifier, script_class->get_datatype());
return;
}
- if (base_class->has_member(name)) {
- const GDScriptParser::ClassNode::Member &member = base_class->get_member(name);
- p_identifier->set_datatype(member.get_datatype());
+
+ if (script_class->has_member(name)) {
+ resolve_class_member(script_class, name, p_identifier);
+
+ GDScriptParser::ClassNode::Member member = script_class->get_member(name);
switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT:
- // For out-of-order resolution:
- reduce_expression(member.constant->initializer);
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.constant->initializer->reduced_value;
- p_identifier->set_datatype(member.constant->initializer->get_datatype());
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
p_identifier->constant_source = member.constant;
- break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE:
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- break;
- case GDScriptParser::ClassNode::Member::VARIABLE:
- p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
- p_identifier->variable_source = member.variable;
- member.variable->usages += 1;
- break;
- case GDScriptParser::ClassNode::Member::FUNCTION:
- resolve_function_signature(member.function);
- p_identifier->set_datatype(make_callable_type(member.function->info));
- break;
- case GDScriptParser::ClassNode::Member::CLASS:
- // For out-of-order resolution:
- resolve_class_interface(member.m_class);
- p_identifier->set_datatype(member.m_class->get_datatype());
- break;
- default:
- break; // Type already set.
- }
- return;
- }
- // Check outer constants.
- // TODO: Allow outer static functions.
- GDScriptParser::ClassNode *outer = base_class->outer;
- while (outer != nullptr) {
- if (outer->has_member(name)) {
- const GDScriptParser::ClassNode::Member &member = outer->get_member(name);
- switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT: {
- // TODO: Make sure loops won't cause problem. And make special error message for those.
- // For out-of-order resolution:
- reduce_expression(member.constant->initializer);
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.constant->initializer->reduced_value;
- return;
- } break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.m_enum->dictionary;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
+ return;
+ }
+
+ case GDScriptParser::ClassNode::Member::VARIABLE: {
+ if (is_base && !base.is_meta_type) {
p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.enum_value.value;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
+ p_identifier->variable_source = member.variable;
+ member.variable->usages += 1;
return;
- } break;
- case GDScriptParser::ClassNode::Member::ENUM: {
+ }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::SIGNAL: {
+ if (is_base && !base.is_meta_type) {
p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = false;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
return;
- } break;
- case GDScriptParser::ClassNode::Member::CLASS: {
- resolve_class_interface(member.m_class);
- p_identifier->set_datatype(member.m_class->get_datatype());
+ }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::FUNCTION: {
+ if (is_base && !base.is_meta_type) {
+ p_identifier->set_datatype(make_callable_type(member.function->info));
return;
- } break;
- default:
- break;
+ }
+ } break;
+
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
+ return;
+ }
+
+ default: {
+ // Do nothing
}
}
- outer = outer->outer;
}
- base_class = base_class->base_type.class_type;
+ if (is_base) {
+ is_base = script_class->base_type.class_type != nullptr;
+ if (!is_base && p_base != nullptr) {
+ break;
+ }
+ }
}
- // Check native members.
+ // Check native members. No need for native class recursion because Node exposes all Object's properties.
const StringName &native = base.native_type;
if (class_exists(native)) {
@@ -2901,35 +3140,39 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
if (ClassDB::has_enum(native, name)) {
- p_identifier->set_datatype(make_native_enum_type(native, name));
+ p_identifier->set_datatype(make_native_enum_type(name, native));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
bool valid = false;
+
int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid);
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
- p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
- return;
+
+ // Check whether this constant, which exists, belongs to an enum
+ StringName enum_name = ClassDB::get_integer_constant_enum(native, name);
+ if (enum_name != StringName()) {
+ p_identifier->set_datatype(make_native_enum_type(enum_name, native, false));
+ } else {
+ p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+ }
}
}
}
void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) {
- // TODO: This is opportunity to further infer types.
+ // TODO: This is an opportunity to further infer types.
- // Check if we are inside and enum. This allows enum values to access other elements of the same enum.
+ // Check if we are inside an enum. This allows enum values to access other elements of the same enum.
if (current_enum) {
for (int i = 0; i < current_enum->values.size(); i++) {
const GDScriptParser::EnumNode::Value &element = current_enum->values[i];
if (element.identifier->name == p_identifier->name) {
- GDScriptParser::DataType type;
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
- type.builtin_type = Variant::INT;
- type.is_constant = true;
+ StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM;
+ GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false);
if (element.parent_enum->identifier) {
type.enum_type = element.parent_enum->identifier->name;
}
@@ -2962,6 +3205,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
found_source = true;
break;
+ case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
mark_lambda_use_self();
break;
@@ -2997,18 +3241,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+ bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
+ bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ if ((source_is_variable || source_is_signal) && parser->current_function && parser->current_function->is_static) {
// Get the parent function above any lambda.
GDScriptParser::FunctionNode *parent_function = parser->current_function;
while (parent_function->source_lambda) {
parent_function = parent_function->source_lambda->parent_function;
}
- push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
}
if (!lambda_stack.is_empty()) {
- // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
- if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+ // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (source_is_variable || source_is_signal) {
mark_lambda_use_self();
return; // No need to capture.
}
@@ -3065,12 +3311,29 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::NATIVE;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
- Ref<GDScriptParserRef> parser = get_parser_for(autoload.path);
- if (parser.is_valid()) {
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
+ Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path);
+ if (singl_parser.is_valid()) {
+ Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err == OK) {
- result = type_from_metatype(parser->get_parser()->head->get_datatype());
+ result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
+ }
+ }
+ } else if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
+ if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name);
+ Node *node = Object::cast_to<Node>(constant);
+ if (node != nullptr) {
+ Ref<GDScript> scr = node->get_script();
+ if (scr.is_valid()) {
+ Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_script_path());
+ if (singl_parser.is_valid()) {
+ Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ if (err == OK) {
+ result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
+ }
+ }
+ }
}
}
}
@@ -3080,17 +3343,8 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
- if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) {
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[name];
- Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- p_identifier->set_datatype(type_from_variant(constant, p_identifier));
- p_identifier->is_constant = true;
- p_identifier->reduced_value = constant;
- return;
- }
-
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
- Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
+ if (GDScriptLanguage::get_singleton()->has_any_global_constant(name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_any_global_constant(name);
p_identifier->set_datatype(type_from_variant(constant, p_identifier));
p_identifier->is_constant = true;
p_identifier->reduced_value = constant;
@@ -3121,16 +3375,10 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
return;
}
- GDScriptParser::FunctionNode *previous_function = parser->current_function;
- parser->current_function = p_lambda->function;
-
lambda_stack.push_back(p_lambda);
-
- for (int i = 0; i < p_lambda->function->parameters.size(); i++) {
- resolve_parameter(p_lambda->function->parameters[i]);
- }
-
- resolve_suite(p_lambda->function->body);
+ resolve_function_signature(p_lambda->function, p_lambda, true);
+ resolve_function_body(p_lambda->function, true);
+ lambda_stack.pop_back();
int captures_amount = p_lambda->captures.size();
if (captures_amount > 0) {
@@ -3155,9 +3403,6 @@ void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
p_lambda->function->parameters_indices[capture->name] = i;
}
}
-
- lambda_stack.pop_back();
- parser->current_function = previous_function;
}
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
@@ -3185,16 +3430,41 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
p_preload->resolved_path = p_preload->path->reduced_value;
// TODO: Save this as script dependency.
if (p_preload->resolved_path.is_relative_path()) {
- p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path);
+ p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path);
}
p_preload->resolved_path = p_preload->resolved_path.simplify_path();
- if (!FileAccess::exists(p_preload->resolved_path)) {
- push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ if (!ResourceLoader::exists(p_preload->resolved_path)) {
+ Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
+
+ if (file_check->file_exists(p_preload->resolved_path)) {
+ push_error(vformat(R"(Preload file "%s" has no resource loaders (unrecognized file extension).)", p_preload->resolved_path), p_preload->path);
+ } else {
+ push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ }
} else {
// TODO: Don't load if validating: use completion cache.
- p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
- if (p_preload->resource.is_null()) {
- push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+
+ // Must load GDScript and PackedScenes separately to permit cyclic references
+ // as ResourceLoader::load() detect and reject those.
+ if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "GDScript") {
+ Error err = OK;
+ Ref<GDScript> res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path);
+ p_preload->resource = res;
+ if (err != OK) {
+ push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ } else if (ResourceLoader::get_resource_type(p_preload->resolved_path) == "PackedScene") {
+ Error err = OK;
+ Ref<PackedScene> res = GDScriptCache::get_packed_scene(p_preload->resolved_path, err, parser->script_path);
+ p_preload->resource = res;
+ if (err != OK) {
+ push_error(vformat(R"(Could not preload resource scene "%s".)", p_preload->resolved_path), p_preload->path);
+ }
+ } else {
+ p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
+ if (p_preload->resource.is_null()) {
+ push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+ }
}
}
}
@@ -3220,9 +3490,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_expression(p_subscript->base);
if (p_subscript->base->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false);
} else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false);
}
}
@@ -3232,43 +3502,44 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
if (p_subscript->attribute == nullptr) {
return;
}
- if (p_subscript->base->is_constant) {
+
+ GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
+ bool valid = false;
+ // If the base is a metatype, use the analyzer instead.
+ if (p_subscript->base->is_constant && !base_type.is_meta_type) {
// Just try to get it.
- bool valid = false;
Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
- if (!valid) {
- push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
- } else {
+ if (valid) {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
+ } else if (base_type.is_variant() || !base_type.is_hard_type()) {
+ valid = true;
result_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_subscript);
} else {
- GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
-
- if (base_type.is_variant() || !base_type.is_hard_type()) {
- result_type.kind = GDScriptParser::DataType::VARIANT;
- mark_node_unsafe(p_subscript);
- } else {
- reduce_identifier_from_base(p_subscript->attribute, &base_type);
- GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
- if (attr_type.is_set()) {
- result_type = attr_type;
- p_subscript->is_constant = p_subscript->attribute->is_constant;
- p_subscript->reduced_value = p_subscript->attribute->reduced_value;
- } else {
- if (base_type.kind == GDScriptParser::DataType::BUILTIN) {
- push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, base_type.to_string()), p_subscript->attribute);
+ reduce_identifier_from_base(p_subscript->attribute, &base_type);
+ GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
+ if (attr_type.is_set()) {
+ valid = true;
+ result_type = attr_type;
+ p_subscript->is_constant = p_subscript->attribute->is_constant;
+ p_subscript->reduced_value = p_subscript->attribute->reduced_value;
+ } else if (!base_type.is_meta_type || !base_type.is_constant) {
+ valid = base_type.kind != GDScriptParser::DataType::BUILTIN;
#ifdef DEBUG_ENABLED
- } else {
- parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string());
-#endif
- }
- result_type.kind = GDScriptParser::DataType::VARIANT;
+ if (valid) {
+ parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string());
}
+#endif
+ result_type.kind = GDScriptParser::DataType::VARIANT;
}
}
+ if (!valid) {
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ }
} else {
if (p_subscript->index == nullptr) {
return;
@@ -3281,12 +3552,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
Variant value = p_subscript->base->reduced_value.get(p_subscript->index->reduced_value, &valid);
if (!valid) {
push_error(vformat(R"(Cannot get index "%s" from "%s".)", p_subscript->index->reduced_value, p_subscript->base->reduced_value), p_subscript->index);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
- result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
GDScriptParser::DataType index_type = p_subscript->index->get_datatype();
@@ -3321,7 +3592,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::QUATERNION:
case Variant::AABB:
case Variant::OBJECT:
- error = index_type.builtin_type != Variant::STRING;
+ error = index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME;
break;
// Expect String or number.
case Variant::BASIS:
@@ -3335,11 +3606,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
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;
+ index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME;
break;
// Expect String or int.
case Variant::COLOR:
- error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
+ error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME;
break;
// Don't support indexing, but we will check it later.
case Variant::RID:
@@ -3523,39 +3794,40 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
return;
}
+ GDScriptParser::DataType operand_type = p_unary_op->operand->get_datatype();
+
if (p_unary_op->operand->is_constant) {
p_unary_op->is_constant = true;
p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant());
result = type_from_variant(p_unary_op->reduced_value, p_unary_op);
- } else if (p_unary_op->operand->get_datatype().is_variant()) {
+ }
+
+ if (operand_type.is_variant()) {
result.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_unary_op);
} else {
bool valid = false;
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), valid, p_unary_op);
+ result = get_operation_type(p_unary_op->variant_op, operand_type, valid, p_unary_op);
if (!valid) {
- push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
+ push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", operand_type.to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op);
}
}
p_unary_op->set_datatype(result);
}
-void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
- bool all_is_constant = true;
-
+void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) {
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
if (element->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const);
} else if (element->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const);
}
- all_is_constant = all_is_constant && element->is_constant;
- if (!all_is_constant) {
+ if (!element->is_constant) {
return;
}
}
@@ -3565,24 +3837,24 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
for (int i = 0; i < p_array->elements.size(); i++) {
array[i] = p_array->elements[i]->reduced_value;
}
+ if (p_is_const) {
+ array.set_read_only(true);
+ }
p_array->is_constant = true;
p_array->reduced_value = array;
}
-void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- bool all_is_constant = true;
-
+void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) {
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
if (element.value->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value));
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const);
} else if (element.value->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value));
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const);
}
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
- if (!all_is_constant) {
+ if (!element.key->is_constant || !element.value->is_constant) {
return;
}
}
@@ -3592,6 +3864,9 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
dict[element.key->reduced_value] = element.value->reduced_value;
}
+ if (p_is_const) {
+ dict.set_read_only(true);
+ }
p_dictionary->is_constant = true;
p_dictionary->reduced_value = dict;
}
@@ -3604,6 +3879,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type.
if (p_value.get_type() == Variant::OBJECT) {
+ // Object is treated as a native type, not a builtin type.
+ result.kind = GDScriptParser::DataType::NATIVE;
+
Object *obj = p_value;
if (!obj) {
return GDScriptParser::DataType();
@@ -3618,50 +3896,43 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
scr = obj->get_script();
}
if (scr.is_valid()) {
- if (scr->is_valid()) {
- result.script_type = scr;
- result.script_path = scr->get_path();
- Ref<GDScript> gds = scr;
- if (gds.is_valid()) {
- result.kind = GDScriptParser::DataType::CLASS;
- // This might be an inner class, so we want to get the parser for the root.
- // But still get the inner class from that tree.
- GDScript *current = gds.ptr();
- List<StringName> class_chain;
- while (current->_owner) {
- // Push to front so it's in reverse.
- class_chain.push_front(current->name);
- current = current->_owner;
- }
-
- Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
- if (ref.is_null()) {
- push_error("Could not find script in path.", p_source);
- GDScriptParser::DataType error_type;
- error_type.kind = GDScriptParser::DataType::VARIANT;
- return error_type;
- }
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
-
- GDScriptParser::ClassNode *found = ref->get_parser()->head;
-
- // It should be okay to assume this exists, since we have a complete script already.
- for (const StringName &E : class_chain) {
- found = found->get_member(E).m_class;
+ Ref<GDScript> gds = scr;
+ if (gds.is_valid()) {
+ // This might be an inner class, so we want to get the parser for the root.
+ // But still get the inner class from that tree.
+ String script_path = gds->get_script_path();
+ Ref<GDScriptParserRef> ref = get_parser_for(script_path);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ GDScriptParser::DataType error_type;
+ error_type.kind = GDScriptParser::DataType::VARIANT;
+ return error_type;
+ }
+ Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+ GDScriptParser::ClassNode *found = nullptr;
+ if (err == OK) {
+ found = ref->get_parser()->find_class(gds->fully_qualified_name);
+ if (found != nullptr) {
+ err = resolve_class_inheritance(found, p_source);
}
-
- result.class_type = found;
- result.script_path = ref->get_parser()->script_path;
- } else {
- result.kind = GDScriptParser::DataType::SCRIPT;
}
- result.native_type = scr->get_instance_base_type();
+ if (err || found == nullptr) {
+ push_error(vformat(R"(Could not resolve script "%s".)", script_path), p_source);
+ GDScriptParser::DataType error_type;
+ error_type.kind = GDScriptParser::DataType::VARIANT;
+ return error_type;
+ }
+
+ result.kind = GDScriptParser::DataType::CLASS;
+ result.native_type = found->get_datatype().native_type;
+ result.class_type = found;
+ result.script_path = ref->get_parser()->script_path;
} else {
- push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
- result.kind = GDScriptParser::DataType::VARIANT;
- result.type_source = GDScriptParser::DataType::UNDETECTED;
- result.is_meta_type = false;
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ result.native_type = scr->get_instance_base_type();
+ result.script_path = scr->get_path();
}
+ result.script_type = scr;
} else {
result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) {
@@ -3673,20 +3944,21 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
return result;
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) const {
+GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptParser::DataType &p_meta_type) {
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
- result.is_constant = false;
if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
result.builtin_type = Variant::INT;
+ } else {
+ result.is_constant = false;
}
return result;
}
-GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property) const {
+GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo &p_property, bool p_is_arg) const {
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- if (p_property.type == Variant::NIL && (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
+ if (p_property.type == Variant::NIL && (p_is_arg || (p_property.usage & PROPERTY_USAGE_NIL_IS_VARIANT))) {
// Variant
result.kind = GDScriptParser::DataType::VARIANT;
return result;
@@ -3736,11 +4008,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_default_arg_count = 0;
StringName function_name = p_function;
+ bool was_enum = false;
if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
+ was_enum = true;
if (p_base_type.is_meta_type) {
// Enum type can be treated as a dictionary value.
p_base_type.kind = GDScriptParser::DataType::BUILTIN;
- p_base_type.builtin_type = Variant::DICTIONARY;
p_base_type.is_meta_type = false;
} else {
push_error("Cannot call function on enum value.", p_source);
@@ -3763,6 +4036,10 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
if (E.name == p_function) {
function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name);
+ // Cannot use non-const methods on enums.
+ if (!r_static && was_enum && !(E.flags & METHOD_FLAG_CONST)) {
+ push_error(vformat(R"*(Cannot call non-const Dictionary function "%s()" on enum "%s".)*", p_function, p_base_type.enum_type), p_source);
+ }
return true;
}
}
@@ -3770,6 +4047,25 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
return false;
}
+ StringName base_native = p_base_type.native_type;
+ if (base_native != StringName()) {
+ // Empty native class might happen in some Script implementations.
+ // Just ignore it.
+ if (!class_exists(base_native)) {
+ push_error(vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native), p_source);
+ return false;
+ } else if (p_is_constructor && !ClassDB::can_instantiate(base_native)) {
+ if (p_base_type.kind == GDScriptParser::DataType::CLASS) {
+ push_error(vformat(R"(Class "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.class_type->fqcn.get_file(), base_native), p_source);
+ } else if (p_base_type.kind == GDScriptParser::DataType::SCRIPT) {
+ push_error(vformat(R"(Script "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.script_path.get_file(), base_native), p_source);
+ } else {
+ push_error(vformat(R"(Native class "%s" cannot be constructed as it is abstract.)", base_native), p_source);
+ }
+ return false;
+ }
+ }
+
if (p_is_constructor) {
function_name = "_init";
r_static = true;
@@ -3785,8 +4081,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
push_error(vformat(R"(Member "%s" is not a function.)", function_name), p_source);
return false;
}
+
+ resolve_class_member(base_class, function_name, p_source);
found_function = base_class->get_member(function_name).function;
}
+
+ resolve_class_inheritance(base_class, p_source);
base_class = base_class->base_type.class_type;
}
@@ -3794,11 +4094,11 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_static = p_is_constructor || found_function->is_static;
for (int i = 0; i < found_function->parameters.size(); i++) {
r_par_types.push_back(found_function->parameters[i]->get_datatype());
- if (found_function->parameters[i]->default_value != nullptr) {
+ if (found_function->parameters[i]->initializer != nullptr) {
r_default_arg_count++;
}
}
- r_return_type = found_function->get_datatype();
+ r_return_type = p_is_constructor ? p_base_type : found_function->get_datatype();
r_return_type.is_meta_type = false;
r_return_type.is_coroutine = found_function->is_coroutine;
@@ -3807,7 +4107,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
Ref<Script> base_script = p_base_type.script_type;
- while (base_script.is_valid() && base_script->is_valid()) {
+ while (base_script.is_valid() && base_script->has_method(function_name)) {
MethodInfo info = base_script->get_method_info(function_name);
if (!(info == MethodInfo())) {
@@ -3826,17 +4126,6 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
}
}
- StringName base_native = p_base_type.native_type;
-#ifdef DEBUG_ENABLED
- if (base_native != StringName()) {
- // Empty native class might happen in some Script implementations.
- // Just ignore it.
- if (!class_exists(base_native)) {
- ERR_FAIL_V_MSG(false, vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native));
- }
- }
-#endif
-
if (p_is_constructor) {
// Native types always have a default constructor.
r_return_type = p_base_type;
@@ -3864,7 +4153,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
for (const PropertyInfo &E : p_info.arguments) {
- r_par_types.push_back(type_from_property(E));
+ r_par_types.push_back(type_from_property(E, true));
}
return true;
}
@@ -3873,7 +4162,7 @@ bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScr
List<GDScriptParser::DataType> arg_types;
for (const PropertyInfo &E : p_method.arguments) {
- arg_types.push_back(type_from_property(E));
+ arg_types.push_back(type_from_property(E, true));
}
return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
@@ -3906,7 +4195,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
mark_node_unsafe(p_call);
if (!is_type_compatible(arg_type, par_type)) {
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*",
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
p_call->arguments[i]);
valid = false;
@@ -3955,12 +4244,10 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
base_class = base_class->base_type.class_type;
}
- StringName base_native = base.native_type;
-
- ERR_FAIL_COND_V_MSG(!class_exists(base_native), false, "Non-existent native base class.");
-
- StringName parent = base_native;
+ StringName parent = base.native_type;
while (parent != StringName()) {
+ ERR_FAIL_COND_V_MSG(!class_exists(parent), false, "Non-existent native base class.");
+
if (ClassDB::has_method(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
return true;
@@ -3993,9 +4280,6 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- GDScriptParser::DataType result;
- result.kind = GDScriptParser::DataType::VARIANT;
-
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
@@ -4015,28 +4299,18 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
-
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
bool validated = op_eval != nullptr;
- if (hard_operation && !validated) {
- r_valid = false;
- return result;
- } else if (hard_operation && validated) {
+ GDScriptParser::DataType result;
+ if (validated) {
r_valid = true;
- result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ result.type_source = hard_operation ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
- } else if (!hard_operation && !validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::UNDETECTED;
+ } else {
+ r_valid = !hard_operation;
result.kind = GDScriptParser::DataType::VARIANT;
- result.builtin_type = Variant::NIL;
- } else if (!hard_operation && validated) {
- r_valid = true;
- result.type_source = GDScriptParser::DataType::INFERRED;
- result.kind = GDScriptParser::DataType::BUILTIN;
- result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
}
return result;
@@ -4075,7 +4349,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
// Variant array can't be appended to typed array.
valid = false;
} else {
- valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), false);
+ valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), p_allow_implicit_conversion);
}
}
}
@@ -4150,6 +4424,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -4186,6 +4461,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -4200,12 +4476,56 @@ void GDScriptAnalyzer::push_error(const String &p_message, const GDScriptParser:
void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#ifdef DEBUG_ENABLED
+ if (p_node == nullptr) {
+ return;
+ }
+
for (int i = p_node->start_line; i <= p_node->end_line; i++) {
parser->unsafe_lines.insert(i);
}
#endif
}
+void GDScriptAnalyzer::downgrade_node_type_source(GDScriptParser::Node *p_node) {
+ GDScriptParser::IdentifierNode *identifier = nullptr;
+ if (p_node->type == GDScriptParser::Node::IDENTIFIER) {
+ identifier = static_cast<GDScriptParser::IdentifierNode *>(p_node);
+ } else if (p_node->type == GDScriptParser::Node::SUBSCRIPT) {
+ GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_node);
+ if (subscript->is_attribute) {
+ identifier = subscript->attribute;
+ }
+ }
+ if (identifier == nullptr) {
+ return;
+ }
+
+ GDScriptParser::Node *source = nullptr;
+ switch (identifier->source) {
+ case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
+ source = identifier->parameter_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
+ source = identifier->variable_source;
+ } break;
+ case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
+ source = identifier->bind_source;
+ } break;
+ default:
+ break;
+ }
+ if (source == nullptr) {
+ return;
+ }
+
+ GDScriptParser::DataType datatype;
+ datatype.kind = GDScriptParser::DataType::VARIANT;
+ source->set_datatype(datatype);
+}
+
void GDScriptAnalyzer::mark_lambda_use_self() {
for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
lambda->use_self = true;
@@ -4232,39 +4552,46 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
}
Error GDScriptAnalyzer::resolve_inheritance() {
- return resolve_inheritance(parser->head);
+ return resolve_class_inheritance(parser->head, true);
}
Error GDScriptAnalyzer::resolve_interface() {
- resolve_class_interface(parser->head);
+ resolve_class_interface(parser->head, true);
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_body() {
- resolve_class_body(parser->head);
+ resolve_class_body(parser->head, true);
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
-Error GDScriptAnalyzer::resolve_program() {
- resolve_class_interface(parser->head);
- resolve_class_body(parser->head);
-
+Error GDScriptAnalyzer::resolve_dependencies() {
for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) {
if (K.value.is_null()) {
return ERR_PARSE_ERROR;
}
- K.value->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ K.value->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
}
+
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::analyze() {
parser->errors.clear();
- Error err = resolve_inheritance(parser->head);
+ Error err = OK;
+
+ err = resolve_inheritance();
if (err) {
return err;
}
- return resolve_program();
+
+ resolve_interface();
+ resolve_body();
+ if (!parser->errors.is_empty()) {
+ return ERR_PARSE_ERROR;
+ }
+
+ return resolve_dependencies();
}
GDScriptAnalyzer::GDScriptAnalyzer(GDScriptParser *p_parser) {
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 3966b81b6e..b22d47982f 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_analyzer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_analyzer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_ANALYZER_H
#define GDSCRIPT_ANALYZER_H
@@ -45,36 +45,41 @@ class GDScriptAnalyzer {
List<GDScriptParser::LambdaNode *> lambda_stack;
// Tests for detecting invalid overloading of script members
- static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node);
+ static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node, const GDScriptParser::Node *p_member);
static _FORCE_INLINE_ bool has_member_name_conflict_in_native_type(const StringName &p_name, const StringName &p_native_type_string);
Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string);
Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node);
- Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
+ void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list);
+
+ Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive);
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
- // This traverses the tree to resolve all TypeNodes.
- Error resolve_program();
-
void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
- void resolve_class_interface(GDScriptParser::ClassNode *p_class);
- void resolve_class_body(GDScriptParser::ClassNode *p_class);
- void resolve_function_signature(GDScriptParser::FunctionNode *p_function);
- void resolve_function_body(GDScriptParser::FunctionNode *p_function);
- void resolve_node(GDScriptParser::Node *p_node);
+ void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive);
+ void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive);
+ void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr, bool p_is_lambda = false);
+ void resolve_function_body(GDScriptParser::FunctionNode *p_function, bool p_is_lambda = false);
+ void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
+ void resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind);
+ void resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local);
+ void resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local);
+ void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
void resolve_if(GDScriptParser::IfNode *p_if);
void resolve_for(GDScriptParser::ForNode *p_for);
void resolve_while(GDScriptParser::WhileNode *p_while);
- void resolve_variable(GDScriptParser::VariableNode *p_variable);
- void resolve_constant(GDScriptParser::ConstantNode *p_constant);
void resolve_assert(GDScriptParser::AssertNode *p_assert);
void resolve_match(GDScriptParser::MatchNode *p_match);
void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test);
void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test);
- void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
void resolve_return(GDScriptParser::ReturnNode *p_return);
// Reduction functions.
@@ -97,13 +102,13 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
- void const_fold_array(GDScriptParser::ArrayNode *p_array);
- void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+ void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const);
+ void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const);
// Helpers.
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
- GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
- GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
+ static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type);
+ GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const;
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
@@ -113,11 +118,13 @@ class GDScriptAnalyzer {
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
- void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
+ void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
+ void downgrade_node_type_source(GDScriptParser::Node *p_node);
void mark_lambda_use_self();
bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
+ static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
@@ -126,6 +133,7 @@ public:
Error resolve_inheritance();
Error resolve_interface();
Error resolve_body();
+ Error resolve_dependencies();
Error analyze();
GDScriptAnalyzer(GDScriptParser *p_parser);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index fa158591fd..6c80fb7665 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_byte_codegen.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_byte_codegen.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_byte_codegen.h"
@@ -164,7 +164,7 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
function->name = p_function_name;
function->_script = p_script;
- function->source = p_script->get_path();
+ function->source = p_script->get_script_path();
#ifdef DEBUG_ENABLED
function->func_cname = (String(function->source) + " - " + String(p_function_name)).utf8();
@@ -183,7 +183,7 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
ERR_PRINT("Non-zero temporary variables at end of function: " + itos(used_temporaries.size()));
}
#endif
- append(GDScriptFunction::OPCODE_END, 0);
+ append_opcode(GDScriptFunction::OPCODE_END);
for (int i = 0; i < temporaries.size(); i++) {
int stack_index = i + max_locals + RESERVED_STACK;
@@ -424,115 +424,115 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Variant::Type p_new_type) {
switch (p_new_type) {
case Variant::BOOL:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_BOOL, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_BOOL);
break;
case Variant::INT:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_INT, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_INT);
break;
case Variant::FLOAT:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_FLOAT, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_FLOAT);
break;
case Variant::STRING:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING);
break;
case Variant::VECTOR2:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2);
break;
case Variant::VECTOR2I:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2I, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2I);
break;
case Variant::RECT2:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2);
break;
case Variant::RECT2I:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2I, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2I);
break;
case Variant::VECTOR3:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3);
break;
case Variant::VECTOR3I:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I);
break;
case Variant::TRANSFORM2D:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM2D, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM2D);
break;
case Variant::VECTOR4:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3);
break;
case Variant::VECTOR4I:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I);
break;
case Variant::PLANE:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE);
break;
case Variant::QUATERNION:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_QUATERNION, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_QUATERNION);
break;
case Variant::AABB:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_AABB, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_AABB);
break;
case Variant::BASIS:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS);
break;
case Variant::TRANSFORM3D:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM3D);
break;
case Variant::PROJECTION:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PROJECTION, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PROJECTION);
break;
case Variant::COLOR:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR);
break;
case Variant::STRING_NAME:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING_NAME, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING_NAME);
break;
case Variant::NODE_PATH:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_NODE_PATH, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_NODE_PATH);
break;
case Variant::RID:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_RID, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_RID);
break;
case Variant::OBJECT:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_OBJECT, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_OBJECT);
break;
case Variant::CALLABLE:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_CALLABLE, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_CALLABLE);
break;
case Variant::SIGNAL:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_SIGNAL, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_SIGNAL);
break;
case Variant::DICTIONARY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_DICTIONARY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_DICTIONARY);
break;
case Variant::ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_ARRAY);
break;
case Variant::PACKED_BYTE_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY);
break;
case Variant::PACKED_INT32_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY);
break;
case Variant::PACKED_INT64_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY);
break;
case Variant::PACKED_FLOAT32_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY);
break;
case Variant::PACKED_FLOAT64_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY);
break;
case Variant::PACKED_STRING_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY);
break;
case Variant::PACKED_VECTOR2_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY);
break;
case Variant::PACKED_VECTOR3_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY);
break;
case Variant::PACKED_COLOR_ARRAY:
- append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, 1);
+ append_opcode(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY);
break;
case Variant::NIL:
case Variant::VARIANT_MAX:
@@ -546,7 +546,7 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
// Gather specific operator.
Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, Variant::NIL);
- append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_OPERATOR_VALIDATED);
append(p_left_operand);
append(Address());
append(p_target);
@@ -555,7 +555,7 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va
}
// No specific types, perform variant evaluation.
- append(GDScriptFunction::OPCODE_OPERATOR, 3);
+ append_opcode(GDScriptFunction::OPCODE_OPERATOR);
append(p_left_operand);
append(Address());
append(p_target);
@@ -575,7 +575,7 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
// Gather specific operator.
Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
- append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_OPERATOR_VALIDATED);
append(p_left_operand);
append(p_right_operand);
append(p_target);
@@ -584,7 +584,7 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
}
// No specific types, perform variant evaluation.
- append(GDScriptFunction::OPCODE_OPERATOR, 3);
+ append_opcode(GDScriptFunction::OPCODE_OPERATOR);
append(p_left_operand);
append(p_right_operand);
append(p_target);
@@ -592,28 +592,28 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
- append(GDScriptFunction::OPCODE_EXTENDS_TEST, 3);
+ append_opcode(GDScriptFunction::OPCODE_EXTENDS_TEST);
append(p_source);
append(p_type);
append(p_target);
}
void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
- append(GDScriptFunction::OPCODE_IS_BUILTIN, 2);
+ append_opcode(GDScriptFunction::OPCODE_IS_BUILTIN);
append(p_source);
append(p_target);
append(p_type);
}
void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_NOT);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_NOT);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -621,29 +621,29 @@ void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_o
void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
// If here means both operands are true.
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE);
append(p_target);
// Jump away from the fail condition.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(opcodes.size() + 3);
// Here it means one of operands is false.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_FALSE);
append(p_target);
}
void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -651,17 +651,17 @@ void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_op
void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
// If here means both operands are false.
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_FALSE);
append(p_target);
// Jump away from the success condition.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(opcodes.size() + 3);
// Here it means one of operands is true.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE);
append(p_target);
}
@@ -670,18 +670,18 @@ void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
}
void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_NOT);
append(p_condition);
ternary_jump_fail_pos.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN);
append(ternary_result.back()->get());
append(p_expr);
// Jump away from the false path.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
ternary_jump_skip_pos.push_back(opcodes.size());
append(0);
// Fail must jump here.
@@ -690,7 +690,7 @@ void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
}
void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN);
append(ternary_result.back()->get());
append(p_expr);
}
@@ -707,7 +707,7 @@ void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address
IS_BUILTIN_TYPE(p_source, Variant::get_indexed_element_type(p_target.type.builtin_type))) {
// Use indexed setter instead.
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
- append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED);
append(p_target);
append(p_index);
append(p_source);
@@ -715,7 +715,7 @@ void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address
return;
} else if (Variant::get_member_validated_keyed_setter(p_target.type.builtin_type)) {
Variant::ValidatedKeyedSetter setter = Variant::get_member_validated_keyed_setter(p_target.type.builtin_type);
- append(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED);
append(p_target);
append(p_index);
append(p_source);
@@ -724,7 +724,7 @@ void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address
}
}
- append(GDScriptFunction::OPCODE_SET_KEYED, 3);
+ append_opcode(GDScriptFunction::OPCODE_SET_KEYED);
append(p_target);
append(p_index);
append(p_source);
@@ -735,7 +735,7 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address
if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_getter(p_source.type.builtin_type)) {
// Use indexed getter instead.
Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(p_source.type.builtin_type);
- append(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED);
append(p_source);
append(p_index);
append(p_target);
@@ -743,7 +743,7 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address
return;
} else if (Variant::get_member_validated_keyed_getter(p_source.type.builtin_type)) {
Variant::ValidatedKeyedGetter getter = Variant::get_member_validated_keyed_getter(p_source.type.builtin_type);
- append(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED, 3);
+ append_opcode(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED);
append(p_source);
append(p_index);
append(p_target);
@@ -751,7 +751,7 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address
return;
}
}
- append(GDScriptFunction::OPCODE_GET_KEYED, 3);
+ append_opcode(GDScriptFunction::OPCODE_GET_KEYED);
append(p_source);
append(p_index);
append(p_target);
@@ -761,13 +761,13 @@ void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const S
if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name) &&
IS_BUILTIN_TYPE(p_source, Variant::get_member_type(p_target.type.builtin_type, p_name))) {
Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
- append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
+ append_opcode(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED);
append(p_target);
append(p_source);
append(setter);
return;
}
- append(GDScriptFunction::OPCODE_SET_NAMED, 2);
+ append_opcode(GDScriptFunction::OPCODE_SET_NAMED);
append(p_target);
append(p_source);
append(p_name);
@@ -776,26 +776,26 @@ void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const S
void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
if (HAS_BUILTIN_TYPE(p_source) && Variant::get_member_validated_getter(p_source.type.builtin_type, p_name)) {
Variant::ValidatedGetter getter = Variant::get_member_validated_getter(p_source.type.builtin_type, p_name);
- append(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED, 2);
+ append_opcode(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED);
append(p_source);
append(p_target);
append(getter);
return;
}
- append(GDScriptFunction::OPCODE_GET_NAMED, 2);
+ append_opcode(GDScriptFunction::OPCODE_GET_NAMED);
append(p_source);
append(p_target);
append(p_name);
}
void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_SET_MEMBER, 1);
+ append_opcode(GDScriptFunction::OPCODE_SET_MEMBER);
append(p_value);
append(p_name);
}
void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_GET_MEMBER, 1);
+ append_opcode(GDScriptFunction::OPCODE_GET_MEMBER);
append(p_target);
append(p_name);
}
@@ -804,11 +804,11 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
} else {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
append(p_target);
append(p_source);
append(p_target.type.builtin_type);
@@ -818,7 +818,7 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
append(p_target);
append(p_source);
append(class_idx);
@@ -828,7 +828,7 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
Variant script = p_target.type.script_type;
int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
append(p_target);
append(p_source);
append(idx);
@@ -837,7 +837,7 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
ERR_PRINT("Compiler bug: unresolved assign.");
// Shouldn't get here, but fail-safe to a regular assignment
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN);
append(p_target);
append(p_source);
}
@@ -846,45 +846,49 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY);
append(p_target);
append(p_source);
} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
// Need conversion.
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
append(p_target);
append(p_source);
append(p_target.type.builtin_type);
} else {
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN);
append(p_target);
append(p_source);
}
}
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_TRUE);
append(p_target);
}
void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN_FALSE);
append(p_target);
}
-void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
- write_assign(p_dst, p_src);
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) {
+ if (p_use_conversion) {
+ write_assign_with_conversion(p_dst, p_src);
+ } else {
+ write_assign(p_dst, p_src);
+ }
function->default_arguments.push_back(opcodes.size());
}
void GDScriptByteCodeGenerator::write_store_global(const Address &p_dst, int p_global_index) {
- append(GDScriptFunction::OPCODE_STORE_GLOBAL, 1);
+ append_opcode(GDScriptFunction::OPCODE_STORE_GLOBAL);
append(p_dst);
append(p_global_index);
}
void GDScriptByteCodeGenerator::write_store_named_global(const Address &p_dst, const StringName &p_global) {
- append(GDScriptFunction::OPCODE_STORE_NAMED_GLOBAL, 1);
+ append_opcode(GDScriptFunction::OPCODE_STORE_NAMED_GLOBAL);
append(p_dst);
append(p_global);
}
@@ -894,20 +898,20 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN, 2);
+ append_opcode(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
index = p_type.builtin_type;
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
- append(GDScriptFunction::OPCODE_CAST_TO_NATIVE, 3);
+ append_opcode(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
index = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_type.script_type;
int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT, 3);
+ append_opcode(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
index = idx;
} break;
default: {
@@ -920,44 +924,60 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(index);
}
+GDScriptCodeGenerator::Address GDScriptByteCodeGenerator::get_call_target(const GDScriptCodeGenerator::Address &p_target, Variant::Type p_type) {
+ if (p_target.mode == Address::NIL) {
+ GDScriptDataType type;
+ if (p_type != Variant::NIL) {
+ type.has_type = true;
+ type.kind = GDScriptDataType::BUILTIN;
+ type.builtin_type = p_type;
+ }
+ uint32_t addr = add_temporary(type);
+ pop_temporary();
+ return Address(Address::TEMPORARY, addr, type);
+ } else {
+ return p_target;
+ }
+}
+
void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
+ append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function);
}
@@ -979,25 +999,31 @@ void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, cons
}
if (is_validated) {
- append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
+ Variant::Type result_type = Variant::has_utility_function_return_value(p_function) ? Variant::get_utility_function_return_type(p_function) : Variant::NIL;
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
+ }
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_utility_function(p_function));
} else {
- append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function);
}
}
-void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments) {
bool is_validated = false;
// Check if all types are correct.
@@ -1017,77 +1043,45 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target,
if (!is_validated) {
// Perform regular call.
- write_call(p_target, p_base, p_method, p_arguments);
+ if (p_is_static) {
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(get_call_target(p_target));
+ append(p_type);
+ append(p_method);
+ append(p_arguments.size());
+ } else {
+ write_call(p_target, p_base, p_method, p_arguments);
+ }
return;
}
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Address target = get_call_target(p_target, result_type);
+ Variant::Type temp_type = temporaries[target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(target, result_type);
}
- append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(target);
append(p_arguments.size());
append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
- bool is_validated = false;
-
- // Check if all types are correct.
- if (Variant::is_builtin_method_vararg(p_type, p_method)) {
- is_validated = true; // Vararg works fine with any argument, since they can be any type.
- } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
- bool all_types_exact = true;
- for (int i = 0; i < p_arguments.size(); i++) {
- if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
- all_types_exact = false;
- break;
- }
- }
-
- is_validated = all_types_exact;
- }
-
- if (!is_validated) {
- // Perform regular call.
- append(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(p_target);
- append(p_type);
- append(p_method);
- append(p_arguments.size());
- return;
- }
-
- if (p_target.mode == Address::TEMPORARY) {
- Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
- Variant::Type temp_type = temporaries[p_target.address].type;
- if (result_type != temp_type) {
- write_type_adjust(p_target, result_type);
- }
- }
-
- append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, p_base, p_type, p_method, false, p_arguments);
+}
- for (int i = 0; i < p_arguments.size(); i++) {
- append(p_arguments[i]);
- }
- append(Address()); // No base since it's static.
- append(p_target);
- append(p_arguments.size());
- append(Variant::get_validated_builtin_method(p_type, p_method));
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ write_call_builtin_type(p_target, Address(), p_type, p_method, true, p_arguments);
}
void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) {
@@ -1097,11 +1091,11 @@ void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target
if (!is_validated) {
// Perform regular call.
- append(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_NATIVE_STATIC, p_arguments.size() + 1);
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(method);
append(p_arguments.size());
return;
@@ -1109,20 +1103,20 @@ void GDScriptByteCodeGenerator::write_call_native_static(const Address &p_target
}
void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_method);
}
void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
-#define CASE_TYPE(m_type) \
- case Variant::m_type: \
- append(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
+#define CASE_TYPE(m_type) \
+ case Variant::m_type: \
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
break
bool is_ptrcall = true;
@@ -1166,19 +1160,19 @@ void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, cons
CASE_TYPE(PACKED_VECTOR3_ARRAY);
CASE_TYPE(PACKED_COLOR_ARRAY);
default:
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
is_ptrcall = false;
break;
}
} else {
- append(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
}
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_method);
if (is_ptrcall) {
@@ -1189,45 +1183,45 @@ void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, cons
}
void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
+ append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(GDScriptFunction::ADDR_SELF);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
+ append_opcode_and_argcount(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_base);
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_function_name);
}
void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) {
- append(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
+ append_opcode_and_argcount(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
for (int i = 0; i < p_captures.size(); i++) {
append(p_captures[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_captures.size());
append(p_function);
}
@@ -1262,41 +1256,41 @@ void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant
}
}
if (valid_constructor >= 0) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(Variant::get_validated_constructor(p_type, valid_constructor));
return;
}
}
- append(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
append(p_type);
}
void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size());
}
void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_ARRAY, 2 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_ARRAY, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
if (p_element_type.script_type) {
Variant script_type = Ref<Script>(p_element_type.script_type);
int addr = get_constant_pos(script_type);
@@ -1311,30 +1305,30 @@ void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_tar
}
void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
+ append_opcode_and_argcount(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
- append(p_target);
+ append(get_call_target(p_target));
append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
}
void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
- append(GDScriptFunction::OPCODE_AWAIT, 1);
+ append_opcode(GDScriptFunction::OPCODE_AWAIT);
append(p_operand);
- append(GDScriptFunction::OPCODE_AWAIT_RESUME, 1);
+ append_opcode(GDScriptFunction::OPCODE_AWAIT_RESUME);
append(p_target);
}
void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_NOT);
append(p_condition);
if_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
}
void GDScriptByteCodeGenerator::write_else() {
- append(GDScriptFunction::OPCODE_JUMP, 0); // Jump from true if block;
+ append_opcode(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
int else_jmp_addr = opcodes.size();
append(0); // Jump destination, will be patched.
@@ -1349,7 +1343,7 @@ void GDScriptByteCodeGenerator::write_endif() {
}
void GDScriptByteCodeGenerator::write_jump_if_shared(const Address &p_value) {
- append(GDScriptFunction::OPCODE_JUMP_IF_SHARED, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_SHARED);
append(p_value);
if_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
@@ -1373,7 +1367,7 @@ void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable,
const Address &container = for_container_variables.back()->get();
// Assign container.
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSIGN);
append(container);
append(p_list);
@@ -1475,19 +1469,19 @@ void GDScriptByteCodeGenerator::write_for() {
}
// Begin loop.
- append(begin_opcode, 3);
+ append_opcode(begin_opcode);
append(counter);
append(container);
append(iterator);
for_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(opcodes.size() + 6); // Skip over 'continue' code.
// Next iteration.
int continue_addr = opcodes.size();
continue_addrs.push_back(continue_addr);
- append(iterate_opcode, 3);
+ append_opcode(iterate_opcode);
append(counter);
append(container);
append(iterator);
@@ -1497,7 +1491,7 @@ void GDScriptByteCodeGenerator::write_for() {
void GDScriptByteCodeGenerator::write_endfor() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -1526,7 +1520,7 @@ void GDScriptByteCodeGenerator::start_while_condition() {
void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
// Condition check.
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
+ append_opcode(GDScriptFunction::OPCODE_JUMP_IF_NOT);
append(p_condition);
while_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
@@ -1534,7 +1528,7 @@ void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
void GDScriptByteCodeGenerator::write_endwhile() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -1572,28 +1566,28 @@ void GDScriptByteCodeGenerator::end_match() {
}
void GDScriptByteCodeGenerator::write_break() {
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
current_breaks_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_continue() {
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
append(continue_addrs.back()->get());
}
void GDScriptByteCodeGenerator::write_continue_match() {
- append(GDScriptFunction::OPCODE_JUMP, 0);
+ append_opcode(GDScriptFunction::OPCODE_JUMP);
match_continues_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_breakpoint() {
- append(GDScriptFunction::OPCODE_BREAKPOINT, 0);
+ append_opcode(GDScriptFunction::OPCODE_BREAKPOINT);
}
void GDScriptByteCodeGenerator::write_newline(int p_line) {
- append(GDScriptFunction::OPCODE_LINE, 0);
+ append_opcode(GDScriptFunction::OPCODE_LINE);
append(p_line);
current_line = p_line;
}
@@ -1611,23 +1605,23 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
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);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(script_idx);
append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
append(element_type.native_type);
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) {
// Add conversion.
- append(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN, 1);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
append(p_return_value);
append(function->return_type.builtin_type);
} else {
// Just assign.
- append(GDScriptFunction::OPCODE_RETURN, 1);
+ append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
}
} else {
- append(GDScriptFunction::OPCODE_RETURN, 1);
+ append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
}
} else {
@@ -1640,19 +1634,19 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
int script_idx = get_constant_pos(script);
script_idx |= (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY, 2);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
append(script_idx);
append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
append(element_type.native_type);
} else {
- append(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN, 1);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN);
append(p_return_value);
append(function->return_type.builtin_type);
}
} break;
case GDScriptDataType::NATIVE: {
- append(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE, 2);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE);
append(p_return_value);
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
@@ -1664,7 +1658,7 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
Variant script = function->return_type.script_type;
int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT, 2);
+ append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT);
append(p_return_value);
append(script_idx);
} break;
@@ -1672,7 +1666,7 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
ERR_PRINT("Compiler bug: unresolved return.");
// Shouldn't get here, but fail-safe to a regular return;
- append(GDScriptFunction::OPCODE_RETURN, 1);
+ append_opcode(GDScriptFunction::OPCODE_RETURN);
append(p_return_value);
} break;
}
@@ -1680,7 +1674,7 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
}
void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
- append(GDScriptFunction::OPCODE_ASSERT, 2);
+ append_opcode(GDScriptFunction::OPCODE_ASSERT);
append(p_test);
append(p_message);
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 7dd51845df..171c505116 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_byte_codegen.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_byte_codegen.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_BYTE_CODEGEN_H
#define GDSCRIPT_BYTE_CODEGEN_H
@@ -309,6 +309,8 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
}
+ Address get_call_target(const Address &p_target, Variant::Type p_type = Variant::NIL);
+
int address_of(const Address &p_address) {
switch (p_address.mode) {
case Address::SELF:
@@ -331,8 +333,13 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
return -1; // Unreachable.
}
- void append(GDScriptFunction::Opcode p_code, int p_argument_count) {
- opcodes.push_back((p_code & GDScriptFunction::INSTR_MASK) | (p_argument_count << GDScriptFunction::INSTR_BITS));
+ void append_opcode(GDScriptFunction::Opcode p_code) {
+ opcodes.push_back(p_code);
+ }
+
+ void append_opcode_and_argcount(GDScriptFunction::Opcode p_code, int p_argument_count) {
+ opcodes.push_back(p_code);
+ opcodes.push_back(p_argument_count);
instr_args_max = MAX(instr_args_max, p_argument_count);
}
@@ -453,7 +460,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
- virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
+ virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src, bool p_use_conversion) override;
virtual void write_store_global(const Address &p_dst, int p_global_index) override;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) override;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
@@ -462,6 +469,7 @@ public:
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
+ void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, bool p_is_static, const Vector<Address> &p_arguments);
virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_native_static(const Address &p_target, const StringName &p_class, const StringName &p_method, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 48d5fbc569..a009e8e0a8 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_cache.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_cache.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_cache.h"
@@ -34,7 +34,9 @@
#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
+#include "gdscript_compiler.h"
#include "gdscript_parser.h"
+#include "scene/resources/packed_scene.h"
bool GDScriptParserRef::is_valid() const {
return parser != nullptr;
@@ -48,6 +50,13 @@ GDScriptParser *GDScriptParserRef::get_parser() const {
return parser;
}
+GDScriptAnalyzer *GDScriptParserRef::get_analyzer() {
+ if (analyzer == nullptr) {
+ analyzer = memnew(GDScriptAnalyzer(parser));
+ }
+ return analyzer;
+}
+
Error GDScriptParserRef::raise_status(Status p_new_status) {
ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
@@ -62,23 +71,22 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
result = parser->parse(GDScriptCache::get_source_code(path), path, false);
break;
case PARSED: {
- analyzer = memnew(GDScriptAnalyzer(parser));
status = INHERITANCE_SOLVED;
- Error inheritance_result = analyzer->resolve_inheritance();
+ Error inheritance_result = get_analyzer()->resolve_inheritance();
if (result == OK) {
result = inheritance_result;
}
} break;
case INHERITANCE_SOLVED: {
status = INTERFACE_SOLVED;
- Error interface_result = analyzer->resolve_interface();
+ Error interface_result = get_analyzer()->resolve_interface();
if (result == OK) {
result = interface_result;
}
} break;
case INTERFACE_SOLVED: {
status = FULLY_SOLVED;
- Error body_result = analyzer->resolve_body();
+ Error body_result = get_analyzer()->resolve_body();
if (result == OK) {
result = body_result;
}
@@ -95,27 +103,96 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
return result;
}
-GDScriptParserRef::~GDScriptParserRef() {
+void GDScriptParserRef::clear() {
+ if (cleared) {
+ return;
+ }
+ cleared = true;
+
if (parser != nullptr) {
memdelete(parser);
}
+
if (analyzer != nullptr) {
memdelete(analyzer);
}
- MutexLock lock(GDScriptCache::singleton->lock);
+}
+
+GDScriptParserRef::~GDScriptParserRef() {
+ clear();
+
+ MutexLock lock(GDScriptCache::singleton->mutex);
GDScriptCache::singleton->parser_map.erase(path);
}
GDScriptCache *GDScriptCache::singleton = nullptr;
+void GDScriptCache::move_script(const String &p_from, const String &p_to) {
+ if (singleton == nullptr || p_from == p_to) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->cleared) {
+ return;
+ }
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (E.value.has(p_from)) {
+ E.value.insert(p_to);
+ E.value.erase(p_from);
+ }
+ }
+
+ if (singleton->parser_map.has(p_from) && !p_from.is_empty()) {
+ singleton->parser_map[p_to] = singleton->parser_map[p_from];
+ }
+ singleton->parser_map.erase(p_from);
+
+ if (singleton->shallow_gdscript_cache.has(p_from) && !p_from.is_empty()) {
+ singleton->shallow_gdscript_cache[p_to] = singleton->shallow_gdscript_cache[p_from];
+ }
+ singleton->shallow_gdscript_cache.erase(p_from);
+
+ if (singleton->full_gdscript_cache.has(p_from) && !p_from.is_empty()) {
+ singleton->full_gdscript_cache[p_to] = singleton->full_gdscript_cache[p_from];
+ }
+ singleton->full_gdscript_cache.erase(p_from);
+}
+
void GDScriptCache::remove_script(const String &p_path) {
- MutexLock lock(singleton->lock);
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->cleared) {
+ return;
+ }
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (!E.value.has(p_path)) {
+ continue;
+ }
+ E.value.erase(p_path);
+ }
+
+ GDScriptCache::clear_unreferenced_packed_scenes();
+
+ if (singleton->parser_map.has(p_path)) {
+ singleton->parser_map[p_path]->clear();
+ singleton->parser_map.erase(p_path);
+ }
+
+ singleton->dependencies.erase(p_path);
singleton->shallow_gdscript_cache.erase(p_path);
singleton->full_gdscript_cache.erase(p_path);
}
Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) {
- MutexLock lock(singleton->lock);
+ MutexLock lock(singleton->mutex);
Ref<GDScriptParserRef> ref;
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
@@ -146,9 +223,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
Vector<uint8_t> source_file;
Error err;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (err) {
- ERR_FAIL_COND_V(err, "");
- }
+ ERR_FAIL_COND_V(err, "");
uint64_t len = f->get_length();
source_file.resize(len + 1);
@@ -163,8 +238,8 @@ String GDScriptCache::get_source_code(const String &p_path) {
return source;
}
-Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) {
- MutexLock lock(singleton->lock);
+Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_error, const String &p_owner) {
+ MutexLock lock(singleton->mutex);
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
@@ -178,49 +253,75 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
Ref<GDScript> script;
script.instantiate();
script->set_path(p_path, true);
- script->set_script_path(p_path);
script->load_source_code(p_path);
- singleton->shallow_gdscript_cache[p_path] = script.ptr();
+ Ref<GDScriptParserRef> parser_ref = get_parser(p_path, GDScriptParserRef::PARSED, r_error);
+ if (r_error == OK) {
+ GDScriptCompiler::make_scripts(script.ptr(), parser_ref->get_parser()->get_tree(), true);
+ }
+
+ singleton->shallow_gdscript_cache[p_path] = script;
return script;
}
-Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner) {
- MutexLock lock(singleton->lock);
+Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner, bool p_update_from_disk) {
+ MutexLock lock(singleton->mutex);
if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
+ Ref<GDScript> script;
r_error = OK;
if (singleton->full_gdscript_cache.has(p_path)) {
- return singleton->full_gdscript_cache[p_path];
+ script = singleton->full_gdscript_cache[p_path];
+ if (!p_update_from_disk) {
+ return script;
+ }
}
- Ref<GDScript> script = get_shallow_script(p_path);
- ERR_FAIL_COND_V(script.is_null(), Ref<GDScript>());
-
- r_error = script->load_source_code(p_path);
+ if (script.is_null()) {
+ script = get_shallow_script(p_path, r_error);
+ if (r_error) {
+ return script;
+ }
+ }
- if (r_error) {
- return script;
+ if (p_update_from_disk) {
+ r_error = script->load_source_code(p_path);
}
- r_error = script->reload();
if (r_error) {
return script;
}
- singleton->full_gdscript_cache[p_path] = script.ptr();
+ singleton->full_gdscript_cache[p_path] = script;
singleton->shallow_gdscript_cache.erase(p_path);
+ script->reload(true);
return script;
}
+Ref<GDScript> GDScriptCache::get_cached_script(const String &p_path) {
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->full_gdscript_cache.has(p_path)) {
+ return singleton->full_gdscript_cache[p_path];
+ }
+
+ if (singleton->shallow_gdscript_cache.has(p_path)) {
+ return singleton->shallow_gdscript_cache[p_path];
+ }
+
+ return Ref<GDScript>();
+}
+
Error GDScriptCache::finish_compiling(const String &p_owner) {
+ MutexLock lock(singleton->mutex);
+
// Mark this as compiled.
- Ref<GDScript> script = get_shallow_script(p_owner);
- singleton->full_gdscript_cache[p_owner] = script.ptr();
+ Ref<GDScript> script = get_cached_script(p_owner);
+ singleton->full_gdscript_cache[p_owner] = script;
singleton->shallow_gdscript_cache.erase(p_owner);
HashSet<String> depends = singleton->dependencies[p_owner];
@@ -241,13 +342,98 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
return err;
}
+Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_error, const String &p_owner) {
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->packed_scene_cache.has(p_path)) {
+ singleton->packed_scene_dependencies[p_path].insert(p_owner);
+ return singleton->packed_scene_cache[p_path];
+ }
+
+ Ref<PackedScene> scene = ResourceCache::get_ref(p_path);
+ if (scene.is_valid()) {
+ singleton->packed_scene_cache[p_path] = scene;
+ singleton->packed_scene_dependencies[p_path].insert(p_owner);
+ return scene;
+ }
+ scene.instantiate();
+
+ r_error = OK;
+ if (p_path.is_empty()) {
+ r_error = ERR_FILE_BAD_PATH;
+ return scene;
+ }
+
+ scene->set_path(p_path);
+ singleton->packed_scene_cache[p_path] = scene;
+ singleton->packed_scene_dependencies[p_path].insert(p_owner);
+
+ scene->reload_from_file();
+ return scene;
+}
+
+void GDScriptCache::clear_unreferenced_packed_scenes() {
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->cleared) {
+ return;
+ }
+
+ for (KeyValue<String, HashSet<String>> &E : singleton->packed_scene_dependencies) {
+ if (E.value.size() > 0 || !ResourceLoader::is_imported(E.key)) {
+ continue;
+ }
+
+ singleton->packed_scene_dependencies.erase(E.key);
+ singleton->packed_scene_cache.erase(E.key);
+ }
+}
+
+void GDScriptCache::clear() {
+ if (singleton == nullptr) {
+ return;
+ }
+
+ MutexLock lock(singleton->mutex);
+
+ if (singleton->cleared) {
+ return;
+ }
+ singleton->cleared = true;
+
+ RBSet<Ref<GDScriptParserRef>> parser_map_refs;
+ for (KeyValue<String, GDScriptParserRef *> &E : singleton->parser_map) {
+ parser_map_refs.insert(E.value);
+ }
+
+ for (Ref<GDScriptParserRef> &E : parser_map_refs) {
+ if (E.is_valid())
+ E->clear();
+ }
+
+ singleton->packed_scene_dependencies.clear();
+ singleton->packed_scene_cache.clear();
+
+ parser_map_refs.clear();
+ singleton->parser_map.clear();
+ singleton->shallow_gdscript_cache.clear();
+ singleton->full_gdscript_cache.clear();
+
+ singleton->packed_scene_cache.clear();
+ singleton->packed_scene_dependencies.clear();
+}
+
GDScriptCache::GDScriptCache() {
singleton = this;
}
GDScriptCache::~GDScriptCache() {
- parser_map.clear();
- shallow_gdscript_cache.clear();
- full_gdscript_cache.clear();
+ if (!cleared) {
+ clear();
+ }
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index b971bdd984..c7f40f6e82 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_cache.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_cache.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
@@ -36,6 +36,7 @@
#include "core/templates/hash_map.h"
#include "core/templates/hash_set.h"
#include "gdscript.h"
+#include "scene/resources/packed_scene.h"
class GDScriptAnalyzer;
class GDScriptParser;
@@ -56,6 +57,7 @@ private:
Status status = EMPTY;
Error result = OK;
String path;
+ bool cleared = false;
friend class GDScriptCache;
@@ -63,7 +65,9 @@ public:
bool is_valid() const;
Status get_status() const;
GDScriptParser *get_parser() const;
+ GDScriptAnalyzer *get_analyzer();
Error raise_status(Status p_new_status);
+ void clear();
GDScriptParserRef() {}
~GDScriptParserRef();
@@ -72,25 +76,37 @@ public:
class GDScriptCache {
// String key is full path.
HashMap<String, GDScriptParserRef *> parser_map;
- HashMap<String, GDScript *> shallow_gdscript_cache;
- HashMap<String, GDScript *> full_gdscript_cache;
+ HashMap<String, Ref<GDScript>> shallow_gdscript_cache;
+ HashMap<String, Ref<GDScript>> full_gdscript_cache;
HashMap<String, HashSet<String>> dependencies;
+ HashMap<String, Ref<PackedScene>> packed_scene_cache;
+ HashMap<String, HashSet<String>> packed_scene_dependencies;
friend class GDScript;
friend class GDScriptParserRef;
+ friend class GDScriptInstance;
static GDScriptCache *singleton;
- Mutex lock;
- static void remove_script(const String &p_path);
+ bool cleared = false;
+
+ Mutex mutex;
public:
+ static void move_script(const String &p_from, const String &p_to);
+ static void remove_script(const String &p_path);
static Ref<GDScriptParserRef> get_parser(const String &p_path, GDScriptParserRef::Status status, Error &r_error, const String &p_owner = String());
static String get_source_code(const String &p_path);
- static Ref<GDScript> get_shallow_script(const String &p_path, const String &p_owner = String());
- static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String());
+ static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String());
+ static Ref<GDScript> get_full_script(const String &p_path, Error &r_error, const String &p_owner = String(), bool p_update_from_disk = false);
+ static Ref<GDScript> get_cached_script(const String &p_path);
static Error finish_compiling(const String &p_owner);
+ static Ref<PackedScene> get_packed_scene(const String &p_path, Error &r_error, const String &p_owner = "");
+ static void clear_unreferenced_packed_scenes();
+
+ static void clear();
+
GDScriptCache();
~GDScriptCache();
};
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 5972481c3a..e885938eba 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_codegen.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_codegen.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_CODEGEN_H
#define GDSCRIPT_CODEGEN_H
@@ -59,7 +59,7 @@ public:
type = p_type;
}
Address(AddressMode p_mode, uint32_t p_address, const GDScriptDataType &p_type = GDScriptDataType()) {
- mode = p_mode,
+ mode = p_mode;
address = p_address;
type = p_type;
}
@@ -113,7 +113,7 @@ public:
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
- virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
+ virtual void write_assign_default_parameter(const Address &dst, const Address &src, bool p_use_conversion) = 0;
virtual void write_store_global(const Address &p_dst, int p_global_index) = 0;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) = 0;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 00e8223b9a..d63a1b4536 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_compiler.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_compiler.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_compiler.h"
@@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) {
+ if (_is_local_or_parameter(codegen, p_name)) {
return false; //shadowed
}
@@ -65,6 +65,10 @@ bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringNa
return ClassDB::has_property(nc->get_name(), p_name);
}
+bool GDScriptCompiler::_is_local_or_parameter(CodeGen &codegen, const StringName &p_name) {
+ return codegen.parameters.has(p_name) || codegen.locals.has(p_name);
+}
+
void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::Node *p_node) {
if (!error.is_empty()) {
return;
@@ -80,7 +84,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N
}
}
-GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) const {
+GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) {
if (!p_datatype.is_set() || !p_datatype.is_hard_type()) {
return GDScriptDataType();
}
@@ -103,74 +107,44 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type_ref = Ref<Script>(p_datatype.script_type);
+ result.builtin_type = p_datatype.builtin_type;
+ result.script_type_ref = p_datatype.script_type;
result.script_type = result.script_type_ref.ptr();
- result.native_type = result.script_type->get_instance_base_type();
+ result.native_type = p_datatype.native_type;
} break;
case GDScriptParser::DataType::CLASS: {
- // Locate class by constructing the path to it and following that path.
- GDScriptParser::ClassNode *class_type = p_datatype.class_type;
- if (class_type) {
- 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;
- while (class_type->outer) {
- names.push_back(class_type->identifier->name);
- class_type = class_type->outer;
- }
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.builtin_type = p_datatype.builtin_type;
+ result.native_type = p_datatype.native_type;
- Ref<GDScript> script = Ref<GDScript>(main_script);
- while (names.back()) {
- if (!script->subclasses.has(names.back()->get())) {
- ERR_PRINT("Parser bug: Cannot locate datatype class.");
- result.has_type = false;
- return GDScriptDataType();
- }
- script = script->subclasses[names.back()->get()];
- names.pop_back();
- }
- result.script_type = script.ptr();
- result.native_type = script->get_instance_base_type();
- } else {
- // 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);
- }
+ bool is_local_class = parser->has_class(p_datatype.class_type);
+
+ Ref<GDScript> script;
+ if (is_local_class) {
+ script = Ref<GDScript>(main_script);
+ } else {
+ Error err = OK;
+ script = GDScriptCache::get_shallow_script(p_datatype.script_path, err, p_owner->path);
+ if (err) {
+ _set_error(vformat(R"(Could not find script "%s": %s)", p_datatype.script_path, error_names[err]), nullptr);
+ }
+ }
+
+ if (script.is_valid()) {
+ script = Ref<GDScript>(script->find_class(p_datatype.class_type->fqcn));
+ }
- result.script_type = result.script_type_ref.ptr();
- result.native_type = p_datatype.native_type;
+ if (script.is_null()) {
+ _set_error(vformat(R"(Could not find class "%s" in "%s".)", p_datatype.class_type->fqcn, p_datatype.script_path), nullptr);
+ return GDScriptDataType();
+ } else {
+ // Only hold a strong reference if the owner of the element qualified with this type is not local, to avoid cyclic references (leaks).
+ // TODO: Might lead to use after free if script_type is a subclass and is used after its parent is freed.
+ if (!is_local_class) {
+ result.script_type_ref = script;
}
+ result.script_type = script.ptr();
+ result.native_type = p_datatype.native_type;
}
} break;
case GDScriptParser::DataType::ENUM:
@@ -182,6 +156,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.builtin_type = Variant::INT;
}
break;
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
return GDScriptDataType();
@@ -189,13 +164,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
if (p_datatype.has_container_element_type()) {
- result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type()));
- }
-
- // Only hold strong reference to the script if it's not the owner of the
- // element qualified with this type, to avoid cyclic references (leaks).
- if (result.script_type && result.script_type == p_owner) {
- result.script_type_ref = Ref<Script>();
+ result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type(), p_owner));
}
return result;
@@ -244,7 +213,7 @@ static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScr
}
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
- if (p_expression->is_constant) {
+ if (p_expression->is_constant && !(p_expression->get_datatype().is_meta_type && p_expression->get_datatype().kind == GDScriptParser::DataType::CLASS)) {
return codegen.add_constant(p_expression->reduced_value);
}
@@ -367,7 +336,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// This is so one autoload doesn't try to load another before it's compiled.
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()));
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
gen->write_store_global(global, idx);
return global;
@@ -390,11 +359,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (class_node->identifier && class_node->identifier->name == identifier) {
res = Ref<GDScript>(main_script);
} else {
- res = ResourceLoader::load(ScriptServer::get_global_class_path(identifier));
- if (res.is_null()) {
- _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
- r_error = ERR_COMPILATION_FAILED;
- return GDScriptCodeGenerator::Address();
+ String global_class_path = ScriptServer::get_global_class_path(identifier);
+ if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
+ Error err = OK;
+ res = GDScriptCache::get_full_script(global_class_path, err);
+ if (err != OK) {
+ _set_error("Can't load global class " + String(identifier), p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
+ } else {
+ res = ResourceLoader::load(global_class_path);
+ if (res.is_null()) {
+ _set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
+ r_error = ERR_COMPILATION_FAILED;
+ return GDScriptCodeGenerator::Address();
+ }
}
}
@@ -434,7 +414,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> values;
// Create the result temporary first since it's the last to be killed.
- GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype());
+ GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype(), codegen.script);
GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
@@ -510,35 +490,42 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
- GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
+ GDScriptParser::DataType og_cast_type = cn->get_datatype();
+ GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type, codegen.script);
- if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
- cast_type.kind = GDScriptDataType::BUILTIN;
- cast_type.builtin_type = Variant::INT;
- }
+ GDScriptCodeGenerator::Address result;
+ if (cast_type.has_type) {
+ if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
+ cast_type.kind = GDScriptDataType::BUILTIN;
+ cast_type.builtin_type = Variant::INT;
+ }
- // Create temporary for result first since it will be deleted last.
- GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
+ // Create temporary for result first since it will be deleted last.
+ result = codegen.add_temporary(cast_type);
- GDScriptCodeGenerator::Address source = _parse_expression(codegen, r_error, cn->operand);
+ GDScriptCodeGenerator::Address src = _parse_expression(codegen, r_error, cn->operand);
- gen->write_cast(result, source, cast_type);
+ gen->write_cast(result, src, cast_type);
- if (source.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (src.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ result = _parse_expression(codegen, r_error, cn->operand);
}
return result;
} break;
case GDScriptParser::Node::CALL: {
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
- GDScriptDataType type = _gdtype_from_datatype(call->get_datatype());
- GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
- GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
-
- GDScriptCodeGenerator::Address return_addr = p_root ? nil : result;
+ GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script);
+ GDScriptCodeGenerator::Address result;
+ if (p_root) {
+ result = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
+ } else {
+ result = codegen.add_temporary(type);
+ }
Vector<GDScriptCodeGenerator::Address> arguments;
for (int i = 0; i < call->arguments.size(); i++) {
@@ -589,13 +576,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (within_await) {
gen->write_call_async(result, self, call->function_name, arguments);
} else {
- gen->write_call(return_addr, self, call->function_name, arguments);
+ gen->write_call(result, self, call->function_name, arguments);
}
} else {
if (within_await) {
gen->write_call_self_async(result, call->function_name, arguments);
} else {
- gen->write_call_self(return_addr, call->function_name, arguments);
+ gen->write_call_self(result, call->function_name, arguments);
}
}
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
@@ -634,12 +621,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call_method_bind(result, base, method, arguments);
}
} else {
- gen->write_call(return_addr, base, call->function_name, arguments);
+ gen->write_call(result, base, call->function_name, arguments);
}
} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
} else {
- gen->write_call(return_addr, base, call->function_name, arguments);
+ gen->write_call(result, base, call->function_name, arguments);
}
if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -670,7 +657,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> args;
args.push_back(codegen.add_constant(NodePath(get_node->full_path)));
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype(), codegen.script));
MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
@@ -686,7 +673,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::AWAIT: {
const GDScriptParser::AwaitNode *await = static_cast<const GDScriptParser::AwaitNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
within_await = true;
GDScriptCodeGenerator::Address argument = _parse_expression(codegen, r_error, await->to_await);
within_await = false;
@@ -705,7 +692,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Indexing operator.
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) {
@@ -735,7 +722,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// 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->value.index, _gdtype_from_datatype(subscript->get_datatype()));
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype(), codegen.script));
}
}
@@ -773,7 +760,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::UNARY_OPERATOR: {
const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);
if (r_error) {
@@ -791,7 +778,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::BINARY_OPERATOR: {
const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype(), codegen.script));
switch (binary->operation) {
case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
@@ -867,7 +854,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::TERNARY_OPERATOR: {
// x IF a ELSE y operator with early out on failure.
const GDScriptParser::TernaryOpNode *ternary = static_cast<const GDScriptParser::TernaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(ternary->get_datatype(), codegen.script));
gen->write_start_ternary(result);
@@ -944,7 +931,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName var_name = identifier->name;
if (_is_class_member_property(codegen, var_name)) {
assign_class_member_property = var_name;
- } else if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ } else if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member_property = true;
member_property_setter_function = codegen.script->member_indices[var_name].setter;
member_property_has_setter = member_property_setter_function != StringName();
@@ -985,7 +972,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
break;
}
const GDScriptParser::SubscriptNode *subscript_elem = E->get();
- GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype()));
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript_elem->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address key;
StringName name;
@@ -1024,8 +1011,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Perform operator if any.
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
- GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype(), codegen.script));
if (subscript->is_attribute) {
gen->write_get_named(value, name, prev_base);
} else {
@@ -1130,8 +1117,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
if (has_operation) {
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
- GDScriptCodeGenerator::Address member = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
+ GDScriptCodeGenerator::Address member = codegen.add_temporary(_gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script));
gen->write_get_member(member, name);
gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);
gen->pop_temporary(); // Pop member temp.
@@ -1155,7 +1142,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool is_in_setter = false;
StringName setter_function;
StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member = true;
setter_function = codegen.script->member_indices[var_name].setter;
has_setter = setter_function != StringName();
@@ -1184,7 +1171,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
if (has_operation) {
// Perform operation.
- GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype()));
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary(_gdtype_from_datatype(assignment->get_datatype(), codegen.script));
GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);
gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value);
to_assign = op_result;
@@ -1196,7 +1183,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
to_assign = assigned_value;
}
- GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
+ GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype(), codegen.script);
if (has_setter && !is_in_setter) {
// Call setter.
@@ -1226,7 +1213,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::LAMBDA: {
const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype()));
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype(), codegen.script));
Vector<GDScriptCodeGenerator::Address> captures;
captures.resize(lambda->captures.size());
@@ -1276,9 +1263,30 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
equality_type.kind = GDScriptDataType::BUILTIN;
equality_type.builtin_type = Variant::BOOL;
+ GDScriptCodeGenerator::Address type_string_addr = codegen.add_constant(Variant::STRING);
+ GDScriptCodeGenerator::Address type_string_name_addr = codegen.add_constant(Variant::STRING_NAME);
+
// Check type equality.
GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+
+ // Check if StringName <-> String comparison is possible.
+ GDScriptCodeGenerator::Address type_comp_addr_1 = codegen.add_temporary(equality_type);
+ GDScriptCodeGenerator::Address type_comp_addr_2 = codegen.add_temporary(equality_type);
+
+ codegen.generator->write_binary_operator(type_comp_addr_1, Variant::OP_EQUAL, p_type_addr, type_string_addr);
+ codegen.generator->write_binary_operator(type_comp_addr_2, Variant::OP_EQUAL, literal_type_addr, type_string_name_addr);
+ codegen.generator->write_binary_operator(type_comp_addr_1, Variant::OP_AND, type_comp_addr_1, type_comp_addr_2);
+ codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_OR, type_equality_addr, type_comp_addr_1);
+
+ codegen.generator->write_binary_operator(type_comp_addr_1, Variant::OP_EQUAL, p_type_addr, type_string_name_addr);
+ codegen.generator->write_binary_operator(type_comp_addr_2, Variant::OP_EQUAL, literal_type_addr, type_string_addr);
+ codegen.generator->write_binary_operator(type_comp_addr_1, Variant::OP_AND, type_comp_addr_1, type_comp_addr_2);
+ codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_OR, type_equality_addr, type_comp_addr_1);
+
+ codegen.generator->pop_temporary(); // Remove type_comp_addr_2 from stack.
+ codegen.generator->pop_temporary(); // Remove type_comp_addr_1 from stack.
+
codegen.generator->write_and_left_operand(type_equality_addr);
// Get literal.
@@ -1645,12 +1653,12 @@ void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptPars
// Parameters are added directly from function and loop variables are declared explicitly.
continue;
}
- codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype()));
+ codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script));
}
}
Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals) {
- Error error = OK;
+ Error err = OK;
GDScriptCodeGenerator *gen = codegen.generator;
codegen.start_block();
@@ -1675,10 +1683,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
// Evaluate the match expression.
- GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype()));
- GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, error, match->test);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype(), codegen.script));
+ GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, err, match->test);
+ if (err) {
+ return err;
}
// Assign to local.
@@ -1723,9 +1731,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
// For each pattern in branch.
GDScriptCodeGenerator::Address pattern_result = codegen.add_temporary();
for (int k = 0; k < branch->patterns.size(); k++) {
- pattern_result = _parse_match_pattern(codegen, error, branch->patterns[k], value, type, pattern_result, k == 0, false);
- if (error != OK) {
- return error;
+ pattern_result = _parse_match_pattern(codegen, err, branch->patterns[k], value, type, pattern_result, k == 0, false);
+ if (err != OK) {
+ return err;
}
}
@@ -1736,9 +1744,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->pop_temporary();
// Parse the branch block.
- error = _parse_block(codegen, branch->block, false); // Don't add locals again.
- if (error) {
- return error;
+ err = _parse_block(codegen, branch->block, false); // Don't add locals again.
+ if (err) {
+ return err;
}
codegen.end_block(); // Get out of extra block.
@@ -1753,9 +1761,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
} break;
case GDScriptParser::Node::IF: {
const GDScriptParser::IfNode *if_n = static_cast<const GDScriptParser::IfNode *>(s);
- GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, if_n->condition);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, if_n->condition);
+ if (err) {
+ return err;
}
gen->write_if(condition);
@@ -1764,17 +1772,17 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.generator->pop_temporary();
}
- error = _parse_block(codegen, if_n->true_block);
- if (error) {
- return error;
+ err = _parse_block(codegen, if_n->true_block);
+ if (err) {
+ return err;
}
if (if_n->false_block) {
gen->write_else();
- error = _parse_block(codegen, if_n->false_block);
- if (error) {
- return error;
+ err = _parse_block(codegen, if_n->false_block);
+ if (err) {
+ return err;
}
}
@@ -1784,13 +1792,13 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::ForNode *for_n = static_cast<const GDScriptParser::ForNode *>(s);
codegen.start_block();
- GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
+ GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script));
- gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype()));
+ gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script));
- GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list);
+ if (err) {
+ return err;
}
gen->write_for_assignment(iterator, list);
@@ -1801,9 +1809,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_for();
- error = _parse_block(codegen, for_n->loop);
- if (error) {
- return error;
+ err = _parse_block(codegen, for_n->loop);
+ if (err) {
+ return err;
}
gen->write_endfor();
@@ -1815,9 +1823,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->start_while_condition();
- GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, while_n->condition);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, while_n->condition);
+ if (err) {
+ return err;
}
gen->write_while(condition);
@@ -1826,9 +1834,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.generator->pop_temporary();
}
- error = _parse_block(codegen, while_n->loop);
- if (error) {
- return error;
+ err = _parse_block(codegen, while_n->loop);
+ if (err) {
+ return err;
}
gen->write_endwhile();
@@ -1850,9 +1858,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
GDScriptCodeGenerator::Address return_value;
if (return_n->return_value != nullptr) {
- return_value = _parse_expression(codegen, error, return_n->return_value);
- if (error) {
- return error;
+ return_value = _parse_expression(codegen, err, return_n->return_value);
+ if (err) {
+ return err;
}
}
@@ -1865,17 +1873,17 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
#ifdef DEBUG_ENABLED
const GDScriptParser::AssertNode *as = static_cast<const GDScriptParser::AssertNode *>(s);
- GDScriptCodeGenerator::Address condition = _parse_expression(codegen, error, as->condition);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address condition = _parse_expression(codegen, err, as->condition);
+ if (err) {
+ return err;
}
GDScriptCodeGenerator::Address message;
if (as->message) {
- message = _parse_expression(codegen, error, as->message);
- if (error) {
- return error;
+ message = _parse_expression(codegen, err, as->message);
+ if (err) {
+ return err;
}
}
gen->write_assert(condition, message);
@@ -1897,20 +1905,21 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
// Should be already in stack when the block began.
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
- GDScriptParser::DataType local_type = lv->get_datatype();
+ GDScriptDataType local_type = _gdtype_from_datatype(lv->get_datatype(), codegen.script);
+ bool initialized = false;
if (lv->initializer != nullptr) {
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) {
+ if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) {
if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
} else {
codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
}
}
- GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer);
+ if (err) {
+ return err;
}
if (lv->use_conversion_assign) {
gen->write_assign_with_conversion(local, src_address);
@@ -1920,15 +1929,23 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
- } else if (lv->get_datatype().is_hard_type()) {
+ initialized = true;
+ } else if (local_type.has_type) {
// Initialize with default for type.
if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
- } else if (local_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
+ } else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
}
// The `else` branch is for objects, in such case we leave it as `null`.
}
+
+ // Assigns a null for the unassigned variables in loops.
+ if (!initialized && p_block->is_loop) {
+ codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>());
+ }
} break;
case GDScriptParser::Node::CONSTANT: {
// Local constants.
@@ -1946,9 +1963,9 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
default: {
// Expression.
if (s->is_expression()) {
- GDScriptCodeGenerator::Address expr = _parse_expression(codegen, error, static_cast<const GDScriptParser::ExpressionNode *>(s), true);
- if (error) {
- return error;
+ GDScriptCodeGenerator::Address expr = _parse_expression(codegen, err, static_cast<const GDScriptParser::ExpressionNode *>(s), true);
+ if (err) {
+ return err;
}
if (expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
@@ -2007,10 +2024,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
for (int i = 0; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);
- uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type);
+ uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->initializer != nullptr, par_type);
codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
- if (p_func->parameters[i]->default_value != nullptr) {
+ if (parameter->initializer != nullptr) {
optional_parameters++;
}
}
@@ -2033,17 +2050,17 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
continue;
}
- GDScriptParser::DataType field_type = field->get_datatype();
+ GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script);
- GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type);
if (field->initializer) {
// Emit proper line change.
codegen.generator->write_newline(field->initializer->start_line);
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type()) {
+ if (field_type.has_type && field_type.builtin_type == Variant::ARRAY) {
if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
} else {
codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
}
@@ -2062,13 +2079,13 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
- } else if (field->get_datatype().is_hard_type()) {
+ } else if (field_type.has_type) {
codegen.generator->write_newline(field->start_line);
// Initialize with default for type.
if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
- } else if (field_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ } else if (field_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
}
// The `else` branch is for objects, in such case we leave it as `null`.
@@ -2082,13 +2099,24 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
- GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value);
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->initializer);
if (r_error) {
memdelete(codegen.generator);
return nullptr;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
- codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
+
+ // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+ GDScriptDataType par_type = dst_addr.type;
+ if (par_type.has_type && par_type.builtin_type == Variant::ARRAY) {
+ if (par_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(dst_addr, par_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ } else {
+ codegen.generator->write_construct_array(dst_addr, Vector<GDScriptCodeGenerator::Address>());
+ }
+ }
+
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -2107,8 +2135,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
if (EngineDebugger::is_active()) {
String signature;
// Path.
- if (!p_script->get_path().is_empty()) {
- signature += p_script->get_path();
+ if (!p_script->get_script_path().is_empty()) {
+ signature += p_script->get_script_path();
}
// Location.
if (p_func) {
@@ -2180,7 +2208,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
}
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
- Error error = OK;
+ Error err = OK;
GDScriptParser::FunctionNode *function;
@@ -2190,28 +2218,25 @@ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptP
function = p_variable->getter;
}
- _parse_function(error, p_script, p_class, function);
+ _parse_function(err, p_script, p_class, function);
- return error;
+ return err;
}
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- parsing_classes.insert(p_script);
+Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ if (parsed_classes.has(p_script)) {
+ return OK;
+ }
- if (p_class->outer && p_class->outer->outer) {
- // Owner is not root
- if (!parsed_classes.has(p_script->_owner)) {
- if (parsing_classes.has(p_script->_owner)) {
- _set_error("Cyclic class reference for '" + String(p_class->identifier->name) + "'.", p_class);
- return ERR_PARSE_ERROR;
- }
- Error err = _parse_class_level(p_script->_owner, p_class->outer, p_keep_state);
- if (err) {
- return err;
- }
- }
+ if (parsing_classes.has(p_script)) {
+ String class_name = p_class->identifier ? String(p_class->identifier->name) : p_class->fqcn;
+ _set_error(vformat(R"(Cyclic class reference for "%s".)", class_name), p_class);
+ return ERR_PARSE_ERROR;
}
+ parsing_classes.insert(p_script);
+
+ p_script->clearing = true;
#ifdef TOOLS_ENABLED
p_script->doc_functions.clear();
p_script->doc_variables.clear();
@@ -2234,10 +2259,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->base = Ref<GDScript>();
p_script->_base = nullptr;
p_script->members.clear();
+
+ // This makes possible to clear script constants and member_functions without heap-use-after-free errors.
+ HashMap<StringName, Variant> constants;
+ for (const KeyValue<StringName, Variant> &E : p_script->constants) {
+ constants.insert(E.key, E.value);
+ }
p_script->constants.clear();
+ constants.clear();
+ HashMap<StringName, GDScriptFunction *> member_functions;
for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
+ member_functions.insert(E.key, E.value);
+ }
+ p_script->member_functions.clear();
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
memdelete(E.value);
}
+ member_functions.clear();
+
if (p_script->implicit_initializer) {
memdelete(p_script->implicit_initializer);
}
@@ -2252,8 +2291,9 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->implicit_initializer = nullptr;
p_script->implicit_ready = nullptr;
+ p_script->clearing = false;
+
p_script->tool = parser->is_tool();
- p_script->name = p_class->identifier ? p_class->identifier->name : "";
if (!p_script->name.is_empty()) {
if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) {
@@ -2262,53 +2302,50 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
}
- Ref<GDScriptNativeClass> native;
+ GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script);
- GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type);
// Inheritance
switch (base_type.kind) {
case GDScriptDataType::NATIVE: {
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
- native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
- ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
- p_script->native = native;
+ p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
+ ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG);
} break;
case GDScriptDataType::GDSCRIPT: {
Ref<GDScript> base = Ref<GDScript>(base_type.script_type);
- p_script->base = base;
- p_script->_base = base.ptr();
+ if (base.is_null()) {
+ return ERR_COMPILATION_FAILED;
+ }
- if (p_class->base_type.kind == GDScriptParser::DataType::CLASS && p_class->base_type.class_type != nullptr) {
- if (p_class->base_type.script_path == main_script->path) {
- if (!parsed_classes.has(p_script->_base)) {
- if (parsing_classes.has(p_script->_base)) {
- String class_name = p_class->identifier ? p_class->identifier->name : "<main>";
- _set_error("Cyclic class reference for '" + class_name + "'.", p_class);
- return ERR_PARSE_ERROR;
- }
- Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state);
- if (err) {
- return err;
- }
- }
- } else {
- Error err = OK;
- base = GDScriptCache::get_full_script(p_class->base_type.script_path, err, main_script->path);
- if (err) {
- return err;
- }
- if (base.is_null() || !base->is_valid()) {
- return ERR_COMPILATION_FAILED;
- }
+ if (main_script->has_class(base.ptr())) {
+ Error err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ return err;
}
+ } else if (!base->is_valid()) {
+ Error err = OK;
+ Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path);
+ if (err) {
+ _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
+ return err;
+ }
+ if (base_root.is_valid()) {
+ base = Ref<GDScript>(base_root->find_class(base->fully_qualified_name));
+ }
+ if (base.is_null()) {
+ _set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
+ return ERR_COMPILATION_FAILED;
+ }
+ ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG);
}
+ p_script->base = base;
+ p_script->_base = base.ptr();
p_script->member_indices = base->member_indices;
- native = base->native;
- p_script->native = native;
+ p_script->native = base->native;
} break;
default: {
- _set_error("Parser bug: invalid inheritance.", p_class);
+ _set_error("Parser bug: invalid inheritance.", nullptr);
return ERR_BUG;
} break;
}
@@ -2370,6 +2407,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
#ifdef TOOLS_ENABLED
if (variable->initializer != nullptr && variable->initializer->is_constant) {
p_script->member_default_values[name] = variable->initializer->reduced_value;
+ GDScriptCompiler::convert_to_initializer_type(p_script->member_default_values[name], variable);
} else {
p_script->member_default_values.erase(name);
}
@@ -2428,27 +2466,20 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
case GDScriptParser::ClassNode::Member::ENUM: {
const GDScriptParser::EnumNode *enum_n = member.m_enum;
+ StringName name = enum_n->identifier->name;
- // TODO: Make enums not be just a dictionary?
- Dictionary new_enum;
- for (int j = 0; j < enum_n->values.size(); j++) {
- int value = enum_n->values[j].value;
- // Needs to be string because Variant::get will convert to String.
- new_enum[String(enum_n->values[j].identifier->name)] = value;
- }
-
- p_script->constants.insert(enum_n->identifier->name, new_enum);
+ p_script->constants.insert(name, enum_n->dictionary);
#ifdef TOOLS_ENABLED
- p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
- p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc();
- p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name;
- p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description;
+ p_script->member_lines[name] = enum_n->start_line;
+ p_script->doc_enums[name] = DocData::EnumDoc();
+ p_script->doc_enums[name].name = name;
+ p_script->doc_enums[name].description = enum_n->doc_description;
for (int j = 0; j < enum_n->values.size(); j++) {
DocData::ConstantDoc const_doc;
const_doc.name = enum_n->values[j].identifier->name;
const_doc.value = Variant(enum_n->values[j].value).operator String();
const_doc.description = enum_n->values[j].doc_description;
- p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc);
+ p_script->doc_enums[name].values.push_back(const_doc);
}
#endif
} break;
@@ -2479,8 +2510,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
parsed_classes.insert(p_script);
parsing_classes.erase(p_script);
- //parse sub-classes
-
+ // Populate sub-classes.
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type != member.CLASS) {
@@ -2492,27 +2522,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
GDScript *subclass_ptr = subclass.ptr();
// Subclass might still be parsing, just skip it
- if (!parsed_classes.has(subclass_ptr) && !parsing_classes.has(subclass_ptr)) {
- Error err = _parse_class_level(subclass_ptr, inner_class, p_keep_state);
+ if (!parsing_classes.has(subclass_ptr)) {
+ Error err = _populate_class_members(subclass_ptr, inner_class, p_keep_state);
if (err) {
return err;
}
}
#ifdef TOOLS_ENABLED
-
p_script->member_lines[name] = inner_class->start_line;
#endif
-
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
}
return OK;
}
-Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- //parse methods
-
+Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ // Compile member functions, getters, and setters.
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type == member.FUNCTION) {
@@ -2616,17 +2643,36 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
StringName name = inner_class->identifier->name;
GDScript *subclass = p_script->subclasses[name].ptr();
- Error err = _parse_class_blocks(subclass, inner_class, p_keep_state);
+ Error err = _compile_class(subclass, inner_class, p_keep_state);
if (err) {
return err;
}
}
+ p_script->_init_rpc_methods_properties();
+
p_script->valid = true;
return OK;
}
-void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::convert_to_initializer_type(Variant &p_variant, const GDScriptParser::VariableNode *p_node) {
+ // Set p_variant to the value of p_node's initializer, with the type of p_node's variable.
+ GDScriptParser::DataType member_t = p_node->datatype;
+ GDScriptParser::DataType init_t = p_node->initializer->datatype;
+ if (member_t.is_hard_type() && init_t.is_hard_type() &&
+ member_t.kind == GDScriptParser::DataType::BUILTIN && init_t.kind == GDScriptParser::DataType::BUILTIN) {
+ if (Variant::can_convert_strict(init_t.builtin_type, member_t.builtin_type)) {
+ Variant *v = &p_node->initializer->reduced_value;
+ Callable::CallError ce;
+ Variant::construct(member_t.builtin_type, p_variant, const_cast<const Variant **>(&v), 1, ce);
+ }
+ }
+}
+
+void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+ p_script->fully_qualified_name = p_class->fqcn;
+ p_script->name = p_class->identifier ? p_class->identifier->name : "";
+
HashMap<StringName, Ref<GDScript>> old_subclasses;
if (p_keep_state) {
@@ -2643,24 +2689,22 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
StringName name = inner_class->identifier->name;
Ref<GDScript> subclass;
- String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
if (old_subclasses.has(name)) {
subclass = old_subclasses[name];
} else {
- Ref<GDScript> orphan_subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(fully_qualified_name);
- if (orphan_subclass.is_valid()) {
- subclass = orphan_subclass;
- } else {
- subclass.instantiate();
- }
+ subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(inner_class->fqcn);
+ }
+
+ if (subclass.is_null()) {
+ subclass.instantiate();
}
subclass->_owner = p_script;
- subclass->fully_qualified_name = fully_qualified_name;
+ subclass->path = p_script->path;
p_script->subclasses.insert(name, subclass);
- _make_scripts(subclass.ptr(), inner_class, false);
+ make_scripts(subclass.ptr(), inner_class, p_keep_state);
}
}
@@ -2674,26 +2718,26 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
source = p_script->get_path();
- // The best fully qualified name for a base level script is its file path
- p_script->fully_qualified_name = p_script->path;
-
// Create scripts for subclasses beforehand so they can be referenced
- _make_scripts(p_script, root, p_keep_state);
+ make_scripts(p_script, root, p_keep_state);
- p_script->_owner = nullptr;
- Error err = _parse_class_level(p_script, root, p_keep_state);
+ main_script->_owner = nullptr;
+ Error err = _populate_class_members(main_script, parser->get_tree(), p_keep_state);
if (err) {
return err;
}
- err = _parse_class_blocks(p_script, root, p_keep_state);
-
+ err = _compile_class(main_script, root, p_keep_state);
if (err) {
return err;
}
- return GDScriptCache::finish_compiling(p_script->get_path());
+#ifdef TOOLS_ENABLED
+ p_script->_update_doc();
+#endif
+
+ return GDScriptCache::finish_compiling(main_script->get_path());
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 4841884e2d..17c6cc8d2f 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_compiler.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_compiler.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
@@ -81,10 +81,10 @@ class GDScriptCompiler {
type.kind = GDScriptDataType::NATIVE;
type.native_type = obj->get_class_name();
- Ref<Script> script = obj->get_script();
- if (script.is_valid()) {
- type.script_type = script.ptr();
- Ref<GDScript> gdscript = script;
+ Ref<Script> scr = obj->get_script();
+ if (scr.is_valid()) {
+ type.script_type = scr.ptr();
+ Ref<GDScript> gdscript = scr;
if (gdscript.is_valid()) {
type.kind = GDScriptDataType::GDSCRIPT;
} else {
@@ -115,13 +115,14 @@ class GDScriptCompiler {
bool _is_class_member_property(CodeGen &codegen, const StringName &p_name);
bool _is_class_member_property(GDScript *owner, const StringName &p_name);
+ bool _is_local_or_parameter(CodeGen &codegen, const StringName &p_name);
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::BinaryOpNode *on, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
Error _create_binary_operator(CodeGen &codegen, const GDScriptParser::ExpressionNode *p_left_operand, const GDScriptParser::ExpressionNode *p_right_operand, Variant::Operator op, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
- GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner = nullptr) const;
+ GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner);
GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
@@ -130,9 +131,8 @@ class GDScriptCompiler {
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
- Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _populate_class_members(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line = 0;
int err_column = 0;
StringName source;
@@ -140,6 +140,8 @@ class GDScriptCompiler {
bool within_await = false;
public:
+ static void convert_to_initializer_type(Variant &p_variant, const GDScriptParser::VariableNode *p_node);
+ static void make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state = false);
String get_error() const;
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index b38c7c6699..4edabdcb40 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_disassembler.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_disassembler.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#ifdef DEBUG_ENABLED
@@ -104,10 +104,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += ": ";
// This makes the compiler complain if some opcode is unchecked in the switch.
- Opcode code = Opcode(_code_ptr[ip] & INSTR_MASK);
- int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS;
+ Opcode opcode = Opcode(_code_ptr[ip]);
- switch (code) {
+ switch (opcode) {
case OPCODE_OPERATOR: {
int operation = _code_ptr[ip + 4];
@@ -372,6 +371,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 4;
} break;
case OPCODE_CONSTRUCT: {
+ int instr_var_args = _code_ptr[++ip];
Variant::Type t = Variant::Type(_code_ptr[ip + 3 + instr_var_args]);
int argc = _code_ptr[ip + 1 + instr_var_args];
@@ -391,6 +391,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 3 + instr_var_args;
} break;
case OPCODE_CONSTRUCT_VALIDATED: {
+ int instr_var_args = _code_ptr[++ip];
int argc = _code_ptr[ip + 1 + instr_var_args];
text += "construct validated ";
@@ -409,6 +410,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 3 + instr_var_args;
} break;
case OPCODE_CONSTRUCT_ARRAY: {
+ int instr_var_args = _code_ptr[++ip];
int argc = _code_ptr[ip + 1 + instr_var_args];
text += " make_array ";
text += DADDR(1 + argc);
@@ -426,6 +428,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 3 + argc;
} break;
case OPCODE_CONSTRUCT_TYPED_ARRAY: {
+ int instr_var_args = _code_ptr[++ip];
int argc = _code_ptr[ip + 1 + instr_var_args];
Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2]);
@@ -460,6 +463,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 3 + argc;
} break;
case OPCODE_CONSTRUCT_DICTIONARY: {
+ int instr_var_args = _code_ptr[++ip];
int argc = _code_ptr[ip + 1 + instr_var_args];
text += "make_dict ";
text += DADDR(1 + argc * 2);
@@ -481,8 +485,10 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
case OPCODE_CALL:
case OPCODE_CALL_RETURN:
case OPCODE_CALL_ASYNC: {
- bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_RETURN;
- bool async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+ bool ret = (_code_ptr[ip]) == OPCODE_CALL_RETURN;
+ bool async = (_code_ptr[ip]) == OPCODE_CALL_ASYNC;
+
+ int instr_var_args = _code_ptr[++ip];
if (ret) {
text += "call-ret ";
@@ -513,7 +519,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
} break;
case OPCODE_CALL_METHOD_BIND:
case OPCODE_CALL_METHOD_BIND_RET: {
- bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+ bool ret = (_code_ptr[ip]) == OPCODE_CALL_METHOD_BIND_RET;
+ int instr_var_args = _code_ptr[++ip];
if (ret) {
text += "call-method_bind-ret ";
@@ -543,6 +550,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 5 + argc;
} break;
case OPCODE_CALL_BUILTIN_STATIC: {
+ int instr_var_args = _code_ptr[++ip];
Variant::Type type = (Variant::Type)_code_ptr[ip + 1 + instr_var_args];
int argc = _code_ptr[ip + 3 + instr_var_args];
@@ -565,6 +573,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 5 + argc;
} break;
case OPCODE_CALL_NATIVE_STATIC: {
+ int instr_var_args = _code_ptr[++ip];
MethodBind *method = _methods_ptr[_code_ptr[ip + 1 + instr_var_args]];
int argc = _code_ptr[ip + 2 + instr_var_args];
@@ -587,6 +596,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 4 + argc;
} break;
case OPCODE_CALL_PTRCALL_NO_RETURN: {
+ int instr_var_args = _code_ptr[++ip];
+
text += "call-ptrcall (no return) ";
MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
@@ -610,6 +621,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
#define DISASSEMBLE_PTRCALL(m_type) \
case OPCODE_CALL_PTRCALL_##m_type: { \
+ int instr_var_args = _code_ptr[++ip]; \
text += "call-ptrcall (return "; \
text += #m_type; \
text += ") "; \
@@ -667,6 +679,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY);
case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: {
+ int instr_var_args = _code_ptr[++ip];
int argc = _code_ptr[ip + 1 + instr_var_args];
text += "call-builtin-method validated ";
@@ -689,6 +702,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 5 + argc;
} break;
case OPCODE_CALL_UTILITY: {
+ int instr_var_args = _code_ptr[++ip];
+
text += "call-utility ";
int argc = _code_ptr[ip + 1 + instr_var_args];
@@ -708,6 +723,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 4 + argc;
} break;
case OPCODE_CALL_UTILITY_VALIDATED: {
+ int instr_var_args = _code_ptr[++ip];
+
text += "call-utility ";
int argc = _code_ptr[ip + 1 + instr_var_args];
@@ -727,6 +744,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 4 + argc;
} break;
case OPCODE_CALL_GDSCRIPT_UTILITY: {
+ int instr_var_args = _code_ptr[++ip];
+
text += "call-gscript-utility ";
int argc = _code_ptr[ip + 1 + instr_var_args];
@@ -746,6 +765,8 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 4 + argc;
} break;
case OPCODE_CALL_SELF_BASE: {
+ int instr_var_args = _code_ptr[++ip];
+
text += "call-self-base ";
int argc = _code_ptr[ip + 1 + instr_var_args];
@@ -777,6 +798,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 2;
} break;
case OPCODE_CREATE_LAMBDA: {
+ int instr_var_args = _code_ptr[++ip];
int captures_count = _code_ptr[ip + 1 + instr_var_args];
GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]];
@@ -796,6 +818,7 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 3 + captures_count;
} break;
case OPCODE_CREATE_SELF_LAMBDA: {
+ int instr_var_args = _code_ptr[++ip];
int captures_count = _code_ptr[ip + 1 + instr_var_args];
GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]];
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index d943974ce4..3fc0924b4c 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript.h"
@@ -61,8 +61,8 @@ bool GDScriptLanguage::is_using_templates() {
}
Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
- Ref<GDScript> script;
- script.instantiate();
+ Ref<GDScript> scr;
+ scr.instantiate();
String processed_template = p_template;
bool type_hints = false;
#ifdef TOOLS_ENABLED
@@ -80,10 +80,10 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri
}
processed_template = processed_template.replace("_BASE_", p_base_class_name)
- .replace("_CLASS_", p_class_name)
+ .replace("_CLASS_", p_class_name.to_pascal_case())
.replace("_TS_", _get_indentation());
- script->set_source_code(processed_template);
- return script;
+ scr->set_source_code(processed_template);
+ return scr;
}
Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) {
@@ -318,10 +318,10 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
return;
}
- Ref<GDScript> script = instance->get_script();
- ERR_FAIL_COND(script.is_null());
+ Ref<GDScript> scr = instance->get_script();
+ ERR_FAIL_COND(scr.is_null());
- const HashMap<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
+ const HashMap<StringName, GDScript::MemberInfo> &mi = scr->debug_get_member_indices();
for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {
p_members->push_back(E.key);
@@ -344,7 +344,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 HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();
- const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array();
+ const Variant *gl_array = GDScriptLanguage::get_singleton()->get_global_array();
List<Pair<String, Variant>> cinfo;
get_public_constants(&cinfo);
@@ -365,7 +365,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
continue;
}
- const Variant &var = globals[E.value];
+ const Variant &var = gl_array[E.value];
if (Object *obj = var) {
if (Object::cast_to<GDScriptNativeClass>(obj)) {
continue;
@@ -660,7 +660,13 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
}
static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) {
- String arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
+ String arghint;
+
+ if (p_function->get_datatype().builtin_type == Variant::NIL) {
+ arghint = "void " + p_function->identifier->name.operator String() + "(";
+ } else {
+ arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(";
+ }
for (int i = 0; i < p_function->parameters.size(); i++) {
if (i > 0) {
@@ -671,39 +677,43 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
arghint += String::chr(0xFFFF);
}
const GDScriptParser::ParameterNode *par = p_function->parameters[i];
- arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();
+ if (!par->get_datatype().is_hard_type()) {
+ arghint += par->identifier->name.operator String() + ": Variant";
+ } else {
+ arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();
+ }
- if (par->default_value) {
+ if (par->initializer) {
String def_val = "<unknown>";
- switch (par->default_value->type) {
+ switch (par->initializer->type) {
case GDScriptParser::Node::LITERAL: {
- const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value);
+ const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer);
def_val = literal->value.get_construct_string();
} break;
case GDScriptParser::Node::IDENTIFIER: {
- const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value);
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer);
def_val = id->name.operator String();
} break;
case GDScriptParser::Node::CALL: {
- const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->default_value);
+ const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);
if (call->is_constant && call->reduced) {
def_val = call->function_name.operator String() + call->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::ARRAY: {
- const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->default_value);
+ const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);
if (arr->is_constant && arr->reduced) {
def_val = arr->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::DICTIONARY: {
- const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->default_value);
+ const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);
if (dict->is_constant && dict->reduced) {
def_val = dict->reduced_value.operator String();
}
} break;
case GDScriptParser::Node::SUBSCRIPT: {
- const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
+ const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);
if (sub->is_constant) {
if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
def_val = sub->get_datatype().to_string();
@@ -753,10 +763,10 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider1.insert_text = slider1.display.quote(p_quote_style);
r_result.insert(slider1.display, slider1);
- ScriptLanguage::CodeCompletionOption slider2("or_lesser", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ ScriptLanguage::CodeCompletionOption slider2("or_less", 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);
+ ScriptLanguage::CodeCompletionOption slider3("hide_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider3.insert_text = slider3.display.quote(p_quote_style);
r_result.insert(slider3.display, slider3);
}
@@ -985,9 +995,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
GDScriptParser::DataType base_type = p_base.type;
- bool _static = base_type.is_meta_type;
- if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {
ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);
option.insert_text += "(";
r_result.insert(option.display, option);
@@ -996,7 +1005,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
while (!base_type.has_no_type()) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
- _find_identifiers_in_class(base_type.class_type, p_only_functions, _static, false, r_result, p_recursion_depth + 1);
+ _find_identifiers_in_class(base_type.class_type, p_only_functions, base_type.is_meta_type, false, r_result, p_recursion_depth + 1);
// This already finds all parent identifiers, so we are done.
base_type = GDScriptParser::DataType();
} break;
@@ -1004,7 +1013,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
if (!p_only_functions) {
- if (!_static) {
+ if (!base_type.is_meta_type) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const PropertyInfo &E : members) {
@@ -1080,7 +1089,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
- if (!_static || Engine::get_singleton()->has_singleton(type)) {
+ if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
for (const PropertyInfo &E : pinfo) {
@@ -1097,7 +1106,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
- bool only_static = _static && !Engine::get_singleton()->has_singleton(type);
+ bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type);
List<MethodInfo> methods;
ClassDB::get_method_list(type, &methods, false, true);
@@ -1119,6 +1128,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
return;
} break;
+ case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
Variant tmp;
@@ -1146,6 +1156,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
tmp.get_method_list(&methods);
for (const MethodInfo &E : methods) {
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) {
+ // Enum types are static and cannot change, therefore we skip non-const dictionary methods.
+ continue;
+ }
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);
if (E.arguments.size()) {
option.insert_text += "(";
@@ -1195,8 +1209,8 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
_find_built_in_variants(r_result);
static const char *_keywords[] = {
- "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super",
- "break", "continue", "pass", "return",
+ "true", "false", "PI", "TAU", "INF", "NAN", "null", "self", "super",
+ "break", "breakpoint", "continue", "pass", "return",
nullptr
};
@@ -1208,7 +1222,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}
static const char *_keywords_with_space[] = {
- "and", "in", "not", "or", "as", "class", "extends", "is", "func", "signal", "await",
+ "and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await",
"const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while",
nullptr
};
@@ -1261,6 +1275,14 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
}
r_result.insert(option.display, option);
}
+
+ // Global classes
+ List<StringName> global_classes;
+ ScriptServer::get_global_class_list(&global_classes);
+ for (const StringName &E : global_classes) {
+ ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
+ r_result.insert(option.display, option);
+ }
}
static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) {
@@ -1354,6 +1376,9 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
if (p_expression->is_constant) {
// Already has a value, so just use that.
r_type = _type_from_variant(p_expression->reduced_value);
+ if (p_expression->get_datatype().kind == GDScriptParser::DataType::ENUM) {
+ r_type.type = p_expression->get_datatype();
+ }
found = true;
} else {
switch (p_expression->type) {
@@ -1602,7 +1627,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
}
}
- if (!found) {
+ if (!found && base.value.get_type() != Variant::NIL) {
found = _guess_method_return_type_from_base(c, base, call->function_name, r_type);
}
}
@@ -1846,9 +1871,9 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
break;
case GDScriptParser::SuiteNode::Local::PARAMETER:
- if (local.parameter->default_value) {
- last_assign_line = local.parameter->default_value->end_line;
- last_assigned_expression = local.parameter->default_value;
+ if (local.parameter->initializer) {
+ last_assign_line = local.parameter->initializer->end_line;
+ last_assigned_expression = local.parameter->initializer;
}
is_function_parameter = true;
break;
@@ -1929,12 +1954,12 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
id_type = parameter->get_datatype();
}
- if (parameter->default_value) {
+ if (parameter->initializer) {
GDScriptParser::CompletionContext c = p_context;
c.current_function = parent_function;
c.current_class = base_type.class_type;
c.base = nullptr;
- if (_guess_expression_type(c, parameter->default_value, r_type)) {
+ if (_guess_expression_type(c, parameter->initializer, r_type)) {
return true;
}
}
@@ -2021,8 +2046,13 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
r_type.type.kind = GDScriptParser::DataType::NATIVE;
r_type.type.native_type = p_identifier;
r_type.type.is_constant = true;
- r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(p_identifier);
- r_type.value = Variant();
+ if (Engine::get_singleton()->has_singleton(p_identifier)) {
+ r_type.type.is_meta_type = false;
+ r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
+ } else {
+ r_type.type.is_meta_type = true;
+ r_type.value = Variant();
+ }
}
return false;
@@ -2257,6 +2287,11 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
if (base_type.class_type->has_function(p_method)) {
const GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;
if (!is_static || method->is_static) {
+ if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {
+ r_type.type = method->get_datatype();
+ return true;
+ }
+
int last_return_line = -1;
const GDScriptParser::ExpressionNode *last_returned_value = nullptr;
GDScriptParser::CompletionContext c = p_context;
@@ -2270,10 +2305,6 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
if (_guess_expression_type(c, last_returned_value, r_type)) {
return true;
}
- if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {
- r_type.type = method->get_datatype();
- return true;
- }
}
}
}
@@ -2496,9 +2527,72 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
+static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {
+ if (p_context.base == nullptr) {
+ return false;
+ }
+ const GDScriptParser::GetNodeNode *get_node = nullptr;
+
+ switch (p_subscript->base->type) {
+ case GDScriptParser::Node::GET_NODE: {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);
+ } break;
+
+ case GDScriptParser::Node::IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
+
+ switch (identifier_node->source) {
+ case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
+ if (p_context.current_class != nullptr) {
+ const StringName &member_name = identifier_node->name;
+ const GDScriptParser::ClassNode *current_class = p_context.current_class;
+
+ if (current_class->has_member(member_name)) {
+ const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
+
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
+ const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
+
+ if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ }
+ }
+ }
+ }
+ } break;
+ case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
+ if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
+ }
+ } break;
+ default: {
+ } break;
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ if (get_node != nullptr) {
+ const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
+ if (node != nullptr) {
+ if (r_base != nullptr) {
+ *r_base = node;
+ }
+ r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_base_type.kind = GDScriptParser::DataType::NATIVE;
+ r_base_type.native_type = node->get_class_name();
+ r_base_type.builtin_type = Variant::OBJECT;
+ return true;
+ }
+ }
+
+ return false;
+}
+
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"))) {
+ if (p_argidx == 0 && bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
}
@@ -2517,39 +2611,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptCompletionIdentifier connect_base;
- if (Variant::has_utility_function(call->function_name)) {
- MethodInfo info = Variant::get_utility_function_info(call->function_name);
- r_arghint = _make_arguments_hint(info, p_argidx);
- return;
- } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
- MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
- r_arghint = _make_arguments_hint(info, p_argidx);
- return;
- } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
- // Complete constructor.
- List<MethodInfo> constructors;
- Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);
-
- int i = 0;
- for (const MethodInfo &E : constructors) {
- if (p_argidx >= E.arguments.size()) {
- continue;
- }
- if (i > 0) {
- r_arghint += "\n";
- }
- r_arghint += _make_arguments_hint(E, p_argidx);
- i++;
- }
- return;
- } else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {
- base = p_context.base;
-
- if (p_context.current_class) {
- base_type = p_context.current_class->get_datatype();
- _static = !p_context.current_function || p_context.current_function->is_static;
- }
- } else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
+ if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
@@ -2579,16 +2641,52 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
if (subscript->is_attribute) {
- GDScriptCompletionIdentifier ci;
- if (_guess_expression_type(p_context, subscript->base, ci)) {
- base_type = ci.type;
- base = ci.value;
- } else {
- return;
+ bool found_type = _get_subscript_type(p_context, subscript, base_type, &base);
+
+ if (!found_type) {
+ GDScriptCompletionIdentifier ci;
+ if (_guess_expression_type(p_context, subscript->base, ci)) {
+ base_type = ci.type;
+ base = ci.value;
+ } else {
+ return;
+ }
}
_static = base_type.is_meta_type;
}
+ } else if (Variant::has_utility_function(call->function_name)) {
+ MethodInfo info = Variant::get_utility_function_info(call->function_name);
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
+ } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
+ // Complete constructor.
+ List<MethodInfo> constructors;
+ Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);
+
+ int i = 0;
+ for (const MethodInfo &E : constructors) {
+ if (p_argidx >= E.arguments.size()) {
+ continue;
+ }
+ if (i > 0) {
+ r_arghint += "\n";
+ }
+ r_arghint += _make_arguments_hint(E, p_argidx);
+ i++;
+ }
+ return;
+ } else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {
+ base = p_context.base;
+
+ if (p_context.current_class) {
+ base_type = p_context.current_class->get_datatype();
+ _static = !p_context.current_function || p_context.current_function->is_static;
+ }
} else {
return;
}
@@ -2750,7 +2848,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);
if (attr->base) {
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(completion_context, attr->base, base)) {
+ bool found_type = _get_subscript_type(completion_context, attr, base.type);
+ if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {
break;
}
@@ -2764,16 +2863,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
break;
}
- GDScriptParser::CompletionContext c = completion_context;
- c.current_function = nullptr;
- c.current_suite = nullptr;
- c.base = base.value.get_type() == Variant::OBJECT ? base.value.operator Object *() : nullptr;
- if (base.type.kind == GDScriptParser::DataType::CLASS) {
- c.current_class = base.type.class_type;
- } else {
- c.current_class = nullptr;
- }
-
_find_identifiers_in_base(base, false, options, 0);
} break;
case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {
@@ -2805,7 +2894,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_forced = true;
} break;
case GDScriptParser::COMPLETION_RESOURCE_PATH: {
- if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) {
+ if (EDITOR_GET("text_editor/completion/complete_file_paths")) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
r_forced = true;
}
@@ -2903,7 +2992,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// The path needs quotes if it's not a valid identifier (with an exception
// for "/" as path separator, which also doesn't require quotes).
if (!opt.replace("/", "_").is_valid_identifier()) {
- opt = opt.quote(quote_style); // Handle user preference.
+ // Ignore quote_style and just use double quotes for paths with apostrophes.
+ // Double quotes don't need to be checked because they're not valid in node and property names.
+ opt = opt.quote(opt.contains("'") ? "\"" : quote_style); // Handle user preference.
}
ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);
options.insert(option.display, option);
@@ -3036,8 +3127,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
r_result.location = base_type.class_type->get_member(p_symbol).get_line();
r_result.class_path = base_type.script_path;
- r_result.script = GDScriptCache::get_shallow_script(r_result.class_path);
- return OK;
+ Error err = OK;
+ r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err);
+ return err;
}
base_type = base_type.class_type->base_type;
}
@@ -3193,15 +3285,6 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
- // 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;
- return OK;
- }
-
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;
r_result.class_name = "@GDScript";
@@ -3211,10 +3294,24 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
GDScriptParser parser;
parser.parse(p_code, p_path, true);
- GDScriptAnalyzer analyzer(&parser);
- analyzer.analyze();
GDScriptParser::CompletionContext context = parser.get_completion_context();
+ context.base = p_owner;
+
+ // Allows class functions with the names like built-ins to be handled properly.
+ if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {
+ // 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;
+ return OK;
+ }
+ }
+
+ GDScriptAnalyzer analyzer(&parser);
+ analyzer.analyze();
if (context.current_class && context.current_class->extends.size() > 0) {
bool success = false;
@@ -3287,17 +3384,17 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);
if (autoload.is_singleton) {
- String script = autoload.path;
- if (!script.ends_with(".gd")) {
+ String scr_path = autoload.path;
+ if (!scr_path.ends_with(".gd")) {
// Not a script, try find the script anyway,
// may have some success.
- script = script.get_basename() + ".gd";
+ scr_path = scr_path.get_basename() + ".gd";
}
- if (FileAccess::exists(script)) {
+ if (FileAccess::exists(scr_path)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;
r_result.location = 0;
- r_result.script = ResourceLoader::load(script);
+ r_result.script = ResourceLoader::load(scr_path);
return OK;
}
}
@@ -3372,7 +3469,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
break;
}
GDScriptCompletionIdentifier base;
- if (!_guess_expression_type(context, subscript->base, base)) {
+
+ bool found_type = _get_subscript_type(context, subscript, base.type);
+ if (!found_type && !_guess_expression_type(context, subscript->base, base)) {
break;
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index cd3b7d69c5..71831a3a97 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_function.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_function.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_function.h"
@@ -142,20 +142,27 @@ GDScriptFunction::GDScriptFunction() {
name = "<anonymous>";
#ifdef DEBUG_ENABLED
{
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
GDScriptLanguage::get_singleton()->function_list.add(&function_list);
}
#endif
}
GDScriptFunction::~GDScriptFunction() {
+ get_script()->member_functions.erase(name);
+
for (int i = 0; i < lambdas.size(); i++) {
memdelete(lambdas[i]);
}
+ for (int i = 0; i < argument_types.size(); i++) {
+ argument_types.write[i].script_type_ref = Ref<Script>();
+ }
+ return_type.script_type_ref = Ref<Script>();
+
#ifdef DEBUG_ENABLED
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
#endif
@@ -201,7 +208,7 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
}
if (p_extended_check) {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
// Script gone?
if (!scripts_list.in_list()) {
@@ -219,7 +226,7 @@ bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
Variant GDScriptFunctionState::resume(const Variant &p_arg) {
ERR_FAIL_COND_V(!function, Variant());
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
if (!scripts_list.in_list()) {
#ifdef DEBUG_ENABLED
@@ -304,7 +311,7 @@ GDScriptFunctionState::GDScriptFunctionState() :
GDScriptFunctionState::~GDScriptFunctionState() {
{
- MutexLock lock(GDScriptLanguage::singleton->lock);
+ MutexLock lock(GDScriptLanguage::singleton->mutex);
scripts_list.remove_from_list();
instances_list.remove_from_list();
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index e44038d6da..76214f3482 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_function.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_function.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
@@ -405,6 +405,7 @@ public:
ADDR_TYPE_STACK = 0,
ADDR_TYPE_CONSTANT = 1,
ADDR_TYPE_MEMBER = 2,
+ ADDR_TYPE_MAX = 3,
};
enum FixedAddresses {
@@ -416,12 +417,6 @@ public:
ADDR_NIL = ADDR_STACK_NIL | (ADDR_TYPE_STACK << ADDR_BITS),
};
- enum Instruction {
- INSTR_BITS = 20,
- INSTR_MASK = ((1 << INSTR_BITS) - 1),
- INSTR_ARGS_MASK = ~INSTR_MASK,
- };
-
struct StackDebug {
int line;
int pos;
@@ -430,6 +425,7 @@ public:
};
private:
+ friend class GDScript;
friend class GDScriptCompiler;
friend class GDScriptByteCodeGenerator;
@@ -513,7 +509,6 @@ private:
Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
- _FORCE_INLINE_ Variant *_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const;
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
friend class GDScriptLanguage;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index a25bf9a306..e9fe17bb17 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_lambda_callable.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_lambda_callable.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_lambda_callable.h"
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 1954089983..33bdf6dfc1 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_lambda_callable.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_lambda_callable.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_LAMBDA_CALLABLE_H
#define GDSCRIPT_LAMBDA_CALLABLE_H
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 6f5397e1a3..f5d3306376 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_parser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_parser.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_parser.h"
@@ -127,7 +127,7 @@ GDScriptParser::GDScriptParser() {
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_placeholder", PropertyInfo(Variant::STRING, "placeholder")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
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>);
@@ -147,6 +147,10 @@ GDScriptParser::GDScriptParser() {
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", 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);
+
+#ifdef DEBUG_ENABLED
+ is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
+#endif
}
GDScriptParser::~GDScriptParser() {
@@ -230,7 +234,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.leftmost_column = p_source->leftmost_column;
warning.rightmost_column = p_source->rightmost_column;
- if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
+ if (warn_level == GDScriptWarning::WarnLevel::ERROR || bool(GLOBAL_GET("debug/gdscript/warnings/treat_warnings_as_errors"))) {
push_error(warning.get_message(), p_source);
return;
}
@@ -534,44 +538,30 @@ void GDScriptParser::end_statement(const String &p_context) {
void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
+ head->fqcn = script_path;
current_class = head;
+ bool can_have_class_or_extends = true;
- // 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, script-level, or standalone annotation.
+ while (match(GDScriptTokenizer::Token::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?).
- _is_tool = true;
- if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after "@tool" annotation.)");
- }
- // @tool annotation has no specific target.
- annotation->apply(this, nullptr);
- } else 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.)");
- }
+ if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
annotation->apply(this, head);
} else {
- premature_annotation = annotation;
annotation_stack.push_back(annotation);
+ // This annotation must appear after script-level annotations
+ // and class_name/extends (ex: could be @onready or @export),
+ // so we stop looking for script-level stuff.
+ can_have_class_or_extends = false;
+ break;
}
}
}
- for (bool should_break = false; !should_break;) {
+ while (can_have_class_or_extends) {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (premature_annotation != nullptr) {
- push_error(R"("class_name" should be used before annotations (except @tool).)");
- }
advance();
if (head->identifier != nullptr) {
push_error(R"("class_name" can only be used once.)");
@@ -580,9 +570,6 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (premature_annotation != nullptr) {
- push_error(R"("extends" should be used before annotations (except @tool).)");
- }
advance();
if (head->extends_used) {
push_error(R"("extends" can only be used once.)");
@@ -592,7 +579,8 @@ void GDScriptParser::parse_program() {
}
break;
default:
- should_break = true;
+ // No tokens are allowed between script annotations and class/extends.
+ can_have_class_or_extends = false;
break;
}
@@ -601,21 +589,6 @@ void GDScriptParser::parse_program() {
}
}
- if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for a script-level, or standalone annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
- if (annotation != nullptr) {
- if (annotation->applies_to(AnnotationInfo::SCRIPT | 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);
- }
- }
- }
-
parse_class_body(true);
complete_extents(head);
@@ -637,6 +610,53 @@ void GDScriptParser::parse_program() {
clear_unused_annotations();
}
+GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const {
+ String first = p_qualified_name.get_slice("::", 0);
+
+ Vector<String> class_names;
+ GDScriptParser::ClassNode *result = nullptr;
+ // Empty initial name means start at the head.
+ if (first.is_empty() || (head->identifier && first == head->identifier->name)) {
+ class_names = p_qualified_name.split("::");
+ result = head;
+ } else if (p_qualified_name.begins_with(script_path)) {
+ // Script path could have a class path separator("::") in it.
+ class_names = p_qualified_name.trim_prefix(script_path).split("::");
+ result = head;
+ } else if (head->has_member(first)) {
+ class_names = p_qualified_name.split("::");
+ GDScriptParser::ClassNode::Member member = head->get_member(first);
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ result = member.m_class;
+ }
+ }
+
+ // Starts at index 1 because index 0 was handled above.
+ for (int i = 1; result != nullptr && i < class_names.size(); i++) {
+ String current_name = class_names[i];
+ GDScriptParser::ClassNode *next = nullptr;
+ if (result->has_member(current_name)) {
+ GDScriptParser::ClassNode::Member member = result->get_member(current_name);
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ next = member.m_class;
+ }
+ }
+ result = next;
+ }
+
+ return result;
+}
+
+bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
+ if (head->fqcn.is_empty() && p_class->fqcn.get_slice("::", 0).is_empty()) {
+ return p_class == head;
+ } else if (p_class->fqcn.begins_with(head->fqcn)) {
+ return find_class(p_class->fqcn.trim_prefix(head->fqcn)) == p_class;
+ }
+
+ return false;
+}
+
GDScriptParser::ClassNode *GDScriptParser::parse_class() {
ClassNode *n_class = alloc_node<ClassNode>();
@@ -646,6 +666,15 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the class name after "class".)")) {
n_class->identifier = parse_identifier();
+ if (n_class->outer) {
+ String fqcn = n_class->outer->fqcn;
+ if (fqcn.is_empty()) {
+ fqcn = script_path;
+ }
+ n_class->fqcn = fqcn + "::" + n_class->identifier->name;
+ } else {
+ n_class->fqcn = n_class->identifier->name;
+ }
}
if (match(GDScriptTokenizer::Token::EXTENDS)) {
@@ -684,17 +713,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
void GDScriptParser::parse_class_name() {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for the global class name after "class_name".)")) {
current_class->identifier = parse_identifier();
- }
-
- // TODO: Move this to annotation
- if (match(GDScriptTokenizer::Token::COMMA)) {
- // Icon path.
- if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected class icon path string after ",".)")) {
- if (previous.literal.get_type() != Variant::STRING) {
- push_error(vformat(R"(Only strings can be used for the class icon path, found "%s" instead.)", Variant::get_type_name(previous.literal.get_type())));
- }
- current_class->icon_path = previous.literal;
- }
+ current_class->fqcn = String(current_class->identifier->name);
}
if (match(GDScriptTokenizer::Token::EXTENDS)) {
@@ -1168,7 +1187,7 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
if (match(GDScriptTokenizer::Token::EQUAL)) {
// Default value.
- parameter->default_value = parse_expression(false);
+ parameter->initializer = parse_expression(false);
}
complete_extents(parameter);
@@ -1199,7 +1218,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
push_error("Expected signal parameter name.");
break;
}
- if (parameter->default_value != nullptr) {
+ if (parameter->initializer != nullptr) {
push_error(R"(Signal parameters cannot have a default value.)");
}
if (signal->parameters_indices.has(parameter->identifier->name)) {
@@ -1248,16 +1267,18 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
EnumNode::Value item;
GDScriptParser::IdentifierNode *identifier = parse_identifier();
#ifdef DEBUG_ENABLED
- for (MethodInfo &info : gdscript_funcs) {
- if (info.name == identifier->name) {
+ if (!named) { // Named enum identifiers do not shadow anything since you can only access them with NamedEnum.ENUM_VALUE
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
}
}
- if (Variant::has_utility_function(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
- } else if (ClassDB::class_exists(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
- }
#endif
item.identifier = identifier;
item.parent_enum = enum_node;
@@ -1268,14 +1289,8 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
} else if (!named) {
- // TODO: Abstract this recursive member check.
- ClassNode *parent = current_class;
- while (parent != nullptr) {
- if (parent->members_indices.has(item.identifier->name)) {
- push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, parent->get_member(item.identifier->name).get_type_name()));
- break;
- }
- parent = parent->outer;
+ if (current_class->members_indices.has(item.identifier->name)) {
+ push_error(vformat(R"(Name "%s" is already used as a class %s.)", item.identifier->name, current_class->get_member(item.identifier->name).get_type_name()));
}
}
@@ -1344,7 +1359,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
if (parameter == nullptr) {
break;
}
- if (parameter->default_value != nullptr) {
+ if (parameter->initializer != nullptr) {
default_used = true;
} else {
if (default_used) {
@@ -1541,7 +1556,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
VariableNode *variable = static_cast<VariableNode *>(statement);
const SuiteNode::Local &local = current_suite->get_local(variable->identifier->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(), variable->identifier->name));
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name), variable->identifier);
}
current_suite->add_local(variable, current_function);
break;
@@ -1556,7 +1571,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
} else {
name = "variable";
}
- push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name), constant->identifier);
}
current_suite->add_local(constant, current_function);
break;
@@ -1748,24 +1763,29 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
// TODO: Add assert message.
AssertNode *assert = alloc_node<AssertNode>();
+ push_multiline(true);
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "assert".)");
+
assert->condition = parse_expression(false);
if (assert->condition == nullptr) {
push_error("Expected expression to assert.");
+ pop_multiline();
complete_extents(assert);
return nullptr;
}
- if (match(GDScriptTokenizer::Token::COMMA)) {
- // Error message.
+ if (match(GDScriptTokenizer::Token::COMMA) && !check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
assert->message = parse_expression(false);
if (assert->message == nullptr) {
push_error(R"(Expected error message for assert after ",".)");
+ pop_multiline();
complete_extents(assert);
return nullptr;
}
+ match(GDScriptTokenizer::Token::COMMA);
}
+ pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
complete_extents(assert);
@@ -1831,9 +1851,9 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
}
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
- suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite);
+ n_for->loop->is_loop = true;
complete_extents(n_for);
// Reset break/continue state.
@@ -2165,6 +2185,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
+ n_while->loop->is_loop = true;
complete_extents(n_while);
// Reset break/continue state.
@@ -2235,7 +2256,7 @@ GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
if (!previous.is_identifier()) {
- ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
+ ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing identifier node without identifier token.");
}
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
@@ -2943,11 +2964,16 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
// Allow for trailing comma.
break;
}
+ bool use_identifier_completion = current.cursor_place == GDScriptTokenizer::CURSOR_END || current.cursor_place == GDScriptTokenizer::CURSOR_MIDDLE;
ExpressionNode *argument = parse_expression(false);
if (argument == nullptr) {
push_error(R"(Expected expression as the function argument.)");
} else {
call->arguments.push_back(argument);
+
+ if (argument->type == Node::IDENTIFIER && use_identifier_completion) {
+ completion_context.type = COMPLETION_IDENTIFIER;
+ }
}
ct = COMPLETION_CALL_ARGUMENTS;
} while (match(GDScriptTokenizer::Token::COMMA));
@@ -3731,6 +3757,32 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
+ if (p_annotation->name == SNAME("@export_range")) {
+ if (export_type.builtin_type == Variant::INT) {
+ variable->export_info.type = Variant::INT;
+ }
+ }
+ if (p_annotation->name == SNAME("@export_multiline")) {
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
+ DataType inner_type = export_type.get_container_element_type();
+ if (inner_type.builtin_type != Variant::STRING) {
+ push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
+ return false;
+ }
+
+ String hint_prefix = itos(inner_type.builtin_type) + "/" + itos(variable->export_info.hint);
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::ARRAY;
+
+ return true;
+ } else if (export_type.builtin_type == Variant::DICTIONARY) {
+ variable->export_info.type = Variant::DICTIONARY;
+
+ return true;
+ }
+ }
+
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
@@ -3769,13 +3821,48 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
return false;
}
break;
+ case GDScriptParser::DataType::CLASS:
+ if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = export_type.to_string();
+ } 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.to_string();
+ } else {
+ push_error(R"(Export type can only be built-in, a resource, a node or an enum.)", variable);
+ return false;
+ }
+
+ break;
+ case GDScriptParser::DataType::SCRIPT: {
+ StringName class_name;
+ StringName native_base;
+ if (export_type.script_type.is_valid()) {
+ class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path());
+ native_base = export_type.script_type->get_instance_base_type();
+ }
+ if (class_name == StringName()) {
+ Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
+ if (script.is_valid()) {
+ class_name = script->get_language()->get_global_class_name(export_type.script_path);
+ native_base = script->get_instance_base_type();
+ }
+ }
+ if (class_name != StringName() && native_base != StringName() && ClassDB::is_parent_class(native_base, SNAME("Resource"))) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = class_name;
+ }
+ } break;
case GDScriptParser::DataType::ENUM: {
variable->export_info.type = Variant::INT;
variable->export_info.hint = PROPERTY_HINT_ENUM;
String enum_hint_string;
bool first = true;
- for (const KeyValue<StringName, int> &E : export_type.enum_values) {
+ for (const KeyValue<StringName, int64_t> &E : export_type.enum_values) {
if (!first) {
enum_hint_string += ",";
} else {
@@ -3932,28 +4019,22 @@ GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const
}
String GDScriptParser::SuiteNode::Local::get_name() const {
- String name;
switch (type) {
case SuiteNode::Local::PARAMETER:
- name = "parameter";
- break;
+ return "parameter";
case SuiteNode::Local::CONSTANT:
- name = "constant";
- break;
+ return "constant";
case SuiteNode::Local::VARIABLE:
- name = "variable";
- break;
+ return "variable";
case SuiteNode::Local::FOR_VARIABLE:
- name = "for loop iterator";
- break;
+ return "for loop iterator";
case SuiteNode::Local::PATTERN_BIND:
- name = "pattern_bind";
- break;
+ return "pattern_bind";
case SuiteNode::Local::UNDEFINED:
- name = "<undefined>";
- break;
+ return "<undefined>";
+ default:
+ return String();
}
- return name;
}
String GDScriptParser::DataType::to_string() const {
@@ -3985,7 +4066,7 @@ String GDScriptParser::DataType::to_string() const {
if (is_meta_type) {
return script_type->get_class_name().operator String();
}
- String name = script_type->get_name();
+ String name = script_type != nullptr ? script_type->get_name() : "";
if (!name.is_empty()) {
return name;
}
@@ -3995,13 +4076,17 @@ String GDScriptParser::DataType::to_string() const {
}
return native_type.operator String();
}
- case ENUM:
- return enum_type.operator String() + " (enum)";
+ case ENUM: {
+ // native_type contains either the native class defining the enum
+ // or the fully qualified class name of the script defining the enum
+ return String(native_type).get_file(); // Remove path, keep filename
+ }
+ case RESOLVING:
case UNRESOLVED:
return "<unresolved type>";
}
- ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
+ ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range.");
}
static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
@@ -4683,9 +4768,9 @@ void GDScriptParser::TreePrinter::print_parameter(ParameterNode *p_parameter) {
push_text(" : ");
print_type(p_parameter->datatype_specifier);
}
- if (p_parameter->default_value != nullptr) {
+ if (p_parameter->initializer != nullptr) {
push_text(" = ");
- print_expression(p_parameter->default_value);
+ print_expression(p_parameter->initializer);
}
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index d4efab173b..0903f62061 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_parser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_parser.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
@@ -57,6 +57,7 @@ public:
struct AnnotationNode;
struct ArrayNode;
struct AssertNode;
+ struct AssignableNode;
struct AssignmentNode;
struct AwaitNode;
struct BinaryOpNode;
@@ -107,6 +108,7 @@ public:
CLASS, // GDScript.
ENUM, // Enumeration.
VARIANT, // Can be any type.
+ RESOLVING, // Currently resolving.
UNRESOLVED,
};
Kind kind = UNRESOLVED;
@@ -131,11 +133,12 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- HashMap<StringName, int> enum_values; // For enums.
+ HashMap<StringName, int64_t> enum_values; // For enums.
- _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
+ _FORCE_INLINE_ bool is_set() const { return kind != RESOLVING && kind != UNRESOLVED; }
+ _FORCE_INLINE_ bool is_resolving() const { return kind == RESOLVING; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
- _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; }
+ _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == RESOLVING || kind == UNRESOLVED; }
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
@@ -182,12 +185,13 @@ public:
case BUILTIN:
return builtin_type == p_other.builtin_type;
case NATIVE:
- case ENUM:
- return native_type == p_other.native_type && enum_type == p_other.enum_type;
+ case ENUM: // Enums use native_type to identify the enum and its base class.
+ return native_type == p_other.native_type;
case SCRIPT:
return script_type == p_other.script_type;
case CLASS:
return class_type == p_other.class_type;
+ case RESOLVING:
case UNRESOLVED:
break;
}
@@ -351,6 +355,20 @@ public:
}
};
+ struct AssignableNode : public Node {
+ IdentifierNode *identifier = nullptr;
+ ExpressionNode *initializer = nullptr;
+ TypeNode *datatype_specifier = nullptr;
+ bool infer_datatype = false;
+ bool use_conversion_assign = false;
+ int usages = 0;
+
+ virtual ~AssignableNode() {}
+
+ protected:
+ AssignableNode() {}
+ };
+
struct AssignmentNode : public ExpressionNode {
// Assignment is not really an expression but it's easier to parse as if it were.
enum Operation {
@@ -469,7 +487,7 @@ public:
EnumNode *parent_enum = nullptr;
int index = -1;
bool resolved = false;
- int value = 0;
+ int64_t value = 0;
int line = 0;
int leftmost_column = 0;
int rightmost_column = 0;
@@ -480,6 +498,7 @@ public:
IdentifierNode *identifier = nullptr;
Vector<Value> values;
+ Variant dictionary;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@@ -516,6 +535,32 @@ public:
};
EnumNode::Value enum_value;
+ String get_name() const {
+ switch (type) {
+ case UNDEFINED:
+ return "<undefined member>";
+ case CLASS:
+ // All class-type members have an id.
+ return m_class->identifier->name;
+ case CONSTANT:
+ return constant->identifier->name;
+ case FUNCTION:
+ return function->identifier->name;
+ case SIGNAL:
+ return signal->identifier->name;
+ case VARIABLE:
+ return variable->identifier->name;
+ case ENUM:
+ // All enum-type members have an id.
+ return m_enum->identifier->name;
+ case ENUM_VALUE:
+ return enum_value.identifier->name;
+ case GROUP:
+ return annotation->export_info.name;
+ }
+ return "";
+ }
+
String get_type_name() const {
switch (type) {
case UNDEFINED:
@@ -576,31 +621,42 @@ public:
return variable->get_datatype();
case ENUM:
return m_enum->get_datatype();
- case ENUM_VALUE: {
- // Always integer.
- DataType type;
- type.type_source = DataType::ANNOTATED_EXPLICIT;
- type.kind = DataType::BUILTIN;
- type.builtin_type = Variant::INT;
- return type;
- }
- case SIGNAL: {
- DataType type;
- type.type_source = DataType::ANNOTATED_EXPLICIT;
- type.kind = DataType::BUILTIN;
- type.builtin_type = Variant::SIGNAL;
- // TODO: Add parameter info.
- return type;
- }
- case GROUP: {
+ case ENUM_VALUE:
+ return enum_value.identifier->get_datatype();
+ case SIGNAL:
+ return signal->get_datatype();
+ case GROUP:
return DataType();
- }
case UNDEFINED:
return DataType();
}
ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
}
+ Node *get_source_node() const {
+ switch (type) {
+ case CLASS:
+ return m_class;
+ case CONSTANT:
+ return constant;
+ case FUNCTION:
+ return function;
+ case VARIABLE:
+ return variable;
+ case ENUM:
+ return m_enum;
+ case ENUM_VALUE:
+ return enum_value.identifier;
+ case SIGNAL:
+ return signal;
+ case GROUP:
+ return annotation;
+ case UNDEFINED:
+ return nullptr;
+ }
+ ERR_FAIL_V_MSG(nullptr, "Reaching unhandled type.");
+ }
+
Member() {}
Member(ClassNode *p_class) {
@@ -691,12 +747,7 @@ public:
}
};
- struct ConstantNode : public Node {
- IdentifierNode *identifier = nullptr;
- ExpressionNode *initializer = nullptr;
- TypeNode *datatype_specifier = nullptr;
- bool infer_datatype = false;
- int usages = 0;
+ struct ConstantNode : public AssignableNode {
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@@ -786,6 +837,7 @@ public:
LOCAL_VARIABLE,
LOCAL_ITERATOR, // `for` loop iterator.
LOCAL_BIND, // Pattern bind.
+ MEMBER_SIGNAL,
MEMBER_VARIABLE,
MEMBER_CONSTANT,
INHERITED_VARIABLE,
@@ -860,13 +912,7 @@ public:
}
};
- struct ParameterNode : public Node {
- IdentifierNode *identifier = nullptr;
- ExpressionNode *default_value = nullptr;
- TypeNode *datatype_specifier = nullptr;
- bool infer_datatype = false;
- int usages = 0;
-
+ struct ParameterNode : public AssignableNode {
ParameterNode() {
type = PARAMETER;
}
@@ -1054,12 +1100,12 @@ public:
HashMap<StringName, int> locals_indices;
FunctionNode *parent_function = nullptr;
- ForNode *parent_for = nullptr;
IfNode *parent_if = nullptr;
bool has_return = false;
bool has_continue = false;
bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
+ bool is_loop = false;
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
@@ -1115,18 +1161,13 @@ public:
}
};
- struct VariableNode : public Node {
+ struct VariableNode : public AssignableNode {
enum PropertyStyle {
PROP_NONE,
PROP_INLINE,
PROP_SETGET,
};
- IdentifierNode *identifier = nullptr;
- ExpressionNode *initializer = nullptr;
- TypeNode *datatype_specifier = nullptr;
- bool infer_datatype = false;
-
PropertyStyle property = PROP_NONE;
union {
FunctionNode *setter = nullptr;
@@ -1142,8 +1183,6 @@ public:
bool onready = false;
PropertyInfo export_info;
int assignments = 0;
- int usages = 0;
- bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
@@ -1216,13 +1255,14 @@ private:
bool can_break = false;
bool can_continue = false;
bool is_continue_match = false; // Whether a `continue` will act on a `match`.
- bool is_ignoring_warnings = false;
List<bool> multiline_stack;
ClassNode *head = nullptr;
Node *list = nullptr;
List<ParserError> errors;
+
#ifdef DEBUG_ENABLED
+ bool is_ignoring_warnings = false;
List<GDScriptWarning> warnings;
HashSet<String> ignored_warnings;
HashSet<uint32_t> ignored_warning_codes;
@@ -1428,6 +1468,8 @@ public:
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
+ ClassNode *find_class(const String &p_qualified_name) const;
+ bool has_class(const GDScriptParser::ClassNode *p_class) const;
static Variant::Type get_builtin_type(const StringName &p_type);
CompletionContext get_completion_context() const { return completion_context; }
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index 4e12419357..a4dd8a8d3c 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_rpc_callable.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_rpc_callable.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_rpc_callable.h"
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
index 83b9c7e2df..c1007b18b0 100644
--- a/modules/gdscript/gdscript_rpc_callable.h
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_rpc_callable.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_rpc_callable.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_RPC_CALLABLE_H
#define GDSCRIPT_RPC_CALLABLE_H
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 6c17afe939..e17a804003 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_tokenizer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_tokenizer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_tokenizer.h"
@@ -164,6 +164,7 @@ bool GDScriptTokenizer::Token::is_identifier() const {
switch (type) {
case IDENTIFIER:
case MATCH: // Used in String.match().
+ case CONST_INF: // Used in Vector{2,3,4}.INF
return true;
default:
return false;
@@ -516,15 +517,15 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
_advance();
}
- int length = _current - _start;
+ int len = _current - _start;
- if (length == 1 && _peek(-1) == '_') {
+ if (len == 1 && _peek(-1) == '_') {
// Lone underscore.
return make_token(Token::UNDERSCORE);
}
- String name(_start, length);
- if (length < MIN_KEYWORD_LENGTH || length > MAX_KEYWORD_LENGTH) {
+ String name(_start, len);
+ if (len < MIN_KEYWORD_LENGTH || len > MAX_KEYWORD_LENGTH) {
// Cannot be a keyword, as the length doesn't match any.
return make_identifier(name);
}
@@ -538,7 +539,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
const int keyword_length = sizeof(keyword) - 1; \
static_assert(keyword_length <= MAX_KEYWORD_LENGTH, "There's a keyword longer than the defined maximum length"); \
static_assert(keyword_length >= MIN_KEYWORD_LENGTH, "There's a keyword shorter than the defined minimum length"); \
- if (keyword_length == length && name == keyword) { \
+ if (keyword_length == len && name == keyword) { \
return make_token(token_type); \
} \
}
@@ -551,13 +552,13 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
}
// Check if it's a special literal
- if (length == 4) {
+ if (len == 4) {
if (name == "true") {
return make_literal(true);
} else if (name == "null") {
return make_literal(Variant());
}
- } else if (length == 5) {
+ } else if (len == 5) {
if (name == "false") {
return make_literal(false);
}
@@ -725,8 +726,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
}
// Create a string with the whole number.
- int length = _current - _start;
- String number = String(_start, length).replace("_", "");
+ int len = _current - _start;
+ String number = String(_start, len).replace("_", "");
// Convert to the appropriate literal type.
if (base == 16) {
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 68b2c6eb1c..9588922122 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_tokenizer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_tokenizer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 4b97486cb3..10d83dcfe5 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_utility_functions.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_utility_functions.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_utility_functions.h"
@@ -36,6 +36,7 @@
#include "core/object/object.h"
#include "core/templates/oa_hash_map.h"
#include "core/templates/vector.h"
+#include "core/variant/typed_array.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
@@ -261,7 +262,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
}
- static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ static inline void inst_to_dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(1);
if (p_args[0]->get_type() == Variant::NIL) {
@@ -293,6 +294,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
GDScript *p = base.ptr();
+ String path = p->get_script_path();
Vector<StringName> sname;
while (p->_owner) {
@@ -301,7 +303,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
sname.reverse();
- if (!p->path.is_resource_file()) {
+ if (!path.is_resource_file()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::DICTIONARY;
@@ -316,7 +318,7 @@ struct GDScriptUtilityFunctionsDefinitions {
Dictionary d;
d["@subpath"] = cp;
- d["@path"] = p->get_path();
+ d["@path"] = path;
for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
if (!d.has(E.key)) {
@@ -328,7 +330,7 @@ struct GDScriptUtilityFunctionsDefinitions {
}
}
- static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ static inline void dict_to_inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(1);
if (p_args[0]->get_type() != Variant::DICTIONARY) {
@@ -468,12 +470,12 @@ struct GDScriptUtilityFunctionsDefinitions {
static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(0);
if (Thread::get_caller_id() != Thread::get_main_id()) {
- *r_ret = Array();
+ *r_ret = TypedArray<Dictionary>();
return;
}
ScriptLanguage *script = GDScriptLanguage::get_singleton();
- Array ret;
+ TypedArray<Dictionary> ret;
for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
Dictionary frame;
frame["source"] = script->debug_get_stack_level_source(i);
@@ -652,8 +654,8 @@ void GDScriptUtilityFunctions::register_functions() {
REGISTER_VARARG_FUNC(str, true, Variant::STRING);
REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
- REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
- REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
+ REGISTER_FUNC(inst_to_dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
+ REGISTER_FUNC(dict_to_inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
diff --git a/modules/gdscript/gdscript_utility_functions.h b/modules/gdscript/gdscript_utility_functions.h
index 9ca7cf33d8..40e9379a3a 100644
--- a/modules/gdscript/gdscript_utility_functions.h
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_utility_functions.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_utility_functions.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_UTILITY_FUNCTIONS_H
#define GDSCRIPT_UTILITY_FUNCTIONS_H
@@ -34,6 +34,9 @@
#include "core/string/string_name.h"
#include "core/variant/variant.h"
+template <typename T>
+class TypedArray;
+
class GDScriptUtilityFunctions {
public:
typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 61e2c61abc..4ea4438b5e 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_vm.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_vm.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_function.h"
@@ -35,39 +35,6 @@
#include "gdscript.h"
#include "gdscript_lambda_callable.h"
-Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
- int address = p_address & ADDR_MASK;
-
- //sequential table (jump table generated by compiler)
- switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
- case ADDR_TYPE_STACK: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
-#endif
- return &p_stack[address];
- } break;
- case ADDR_TYPE_CONSTANT: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
-#endif
- return &_constants_ptr[address];
- } break;
- case ADDR_TYPE_MEMBER: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access member without instance.";
- return nullptr;
- }
-#endif
- //member indexing is O(1)
- return &p_instance->members.write[address];
- } break;
- }
-
- ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
- return nullptr;
-}
-
#ifdef DEBUG_ENABLED
static String _get_script_name(const Ref<Script> p_script) {
Ref<GDScript> gdscript = p_script;
@@ -413,14 +380,19 @@ void (*type_init_function_table[])(Variant *) = {
#define OPCODE(m_op) \
m_op:
-#define OPCODE_WHILE(m_test) \
- OPSWHILE:
+#define OPCODE_WHILE(m_test)
#define OPCODES_END \
OPSEXIT:
#define OPCODES_OUT \
OPSOUT:
-#define DISPATCH_OPCODE goto OPSWHILE
#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+#ifdef DEBUG_ENABLED
+#define DISPATCH_OPCODE \
+ last_opcode = _code_ptr[ip]; \
+ goto *switch_table_ops[last_opcode]
+#else
+#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
+#endif
#define OPCODE_BREAK goto OPSEXIT
#define OPCODE_OUT goto OPSOUT
#else
@@ -607,21 +579,52 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define CHECK_SPACE(m_space) \
GD_ERR_BREAK((ip + m_space) > _code_size)
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text); \
- if (unlikely(!m_v)) \
- OPCODE_BREAK;
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ { \
+ int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
+ int address_type = (address & ADDR_TYPE_MASK) >> ADDR_BITS; \
+ if (unlikely(address_type < 0 || address_type >= ADDR_TYPE_MAX)) { \
+ err_text = "Bad address type."; \
+ OPCODE_BREAK; \
+ } \
+ int address_index = address & ADDR_MASK; \
+ if (unlikely(address_index < 0 || address_index >= variant_address_limits[address_type])) { \
+ if (address_type == ADDR_TYPE_MEMBER && !p_instance) { \
+ err_text = "Cannot access member without instance."; \
+ } else { \
+ err_text = "Bad address index."; \
+ } \
+ OPCODE_BREAK; \
+ } \
+ m_v = &variant_addresses[address_type][address_index]; \
+ if (unlikely(!m_v)) \
+ OPCODE_BREAK; \
+ }
#else
#define GD_ERR_BREAK(m_cond)
#define CHECK_SPACE(m_space)
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text);
+
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ { \
+ int address = _code_ptr[ip + 1 + (m_code_ofs)]; \
+ m_v = &variant_addresses[(address & ADDR_TYPE_MASK) >> ADDR_BITS][address & ADDR_MASK]; \
+ if (unlikely(!m_v)) \
+ OPCODE_BREAK; \
+ }
#endif
+#define LOAD_INSTRUCTION_ARGS \
+ int instr_arg_count = _code_ptr[ip + 1]; \
+ for (int i = 0; i < instr_arg_count; i++) { \
+ GET_VARIANT_PTR(v, i + 1); \
+ instruction_args[i] = v; \
+ } \
+ ip += 1; // Offset to skip instruction argcount.
+
#define GET_INSTRUCTION_ARG(m_v, m_idx) \
Variant *m_v = instruction_args[m_idx]
@@ -639,21 +642,20 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool exit_ok = false;
bool awaited = false;
#endif
+#ifdef DEBUG_ENABLED
+ int variant_address_limits[ADDR_TYPE_MAX] = { _stack_size, _constant_count, p_instance ? p_instance->members.size() : 0 };
+#endif
+
+ Variant *variant_addresses[ADDR_TYPE_MAX] = { stack, _constants_ptr, p_instance ? p_instance->members.ptrw() : nullptr };
#ifdef DEBUG_ENABLED
OPCODE_WHILE(ip < _code_size) {
- int last_opcode = _code_ptr[ip] & INSTR_MASK;
+ int last_opcode = _code_ptr[ip];
#else
OPCODE_WHILE(true) {
#endif
- // Load arguments for the instruction before each instruction.
- int instr_arg_count = ((_code_ptr[ip]) & INSTR_ARGS_MASK) >> INSTR_BITS;
- for (int i = 0; i < instr_arg_count; i++) {
- GET_VARIANT_PTR(v, i + 1);
- instruction_args[i] = v;
- }
- OPCODE_SWITCH(_code_ptr[ip] & INSTR_MASK) {
+ OPCODE_SWITCH(_code_ptr[ip]) {
OPCODE(OPCODE_OPERATOR) {
CHECK_SPACE(5);
@@ -661,9 +663,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
GD_ERR_BREAK(op >= Variant::OP_MAX);
- GET_INSTRUCTION_ARG(a, 0);
- GET_INSTRUCTION_ARG(b, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(a, 0);
+ GET_VARIANT_PTR(b, 1);
+ GET_VARIANT_PTR(dst, 2);
#ifdef DEBUG_ENABLED
@@ -696,9 +698,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
- GET_INSTRUCTION_ARG(a, 0);
- GET_INSTRUCTION_ARG(b, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(a, 0);
+ GET_VARIANT_PTR(b, 1);
+ GET_VARIANT_PTR(dst, 2);
operator_func(a, b, dst);
@@ -709,9 +711,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_EXTENDS_TEST) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(a, 0);
- GET_INSTRUCTION_ARG(b, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(a, 0);
+ GET_VARIANT_PTR(b, 1);
+ GET_VARIANT_PTR(dst, 2);
#ifdef DEBUG_ENABLED
if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
@@ -784,8 +786,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_IS_BUILTIN) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(value, 0);
- GET_INSTRUCTION_ARG(dst, 1);
+ GET_VARIANT_PTR(value, 0);
+ GET_VARIANT_PTR(dst, 1);
Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
@@ -798,9 +800,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_KEYED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(index, 1);
- GET_INSTRUCTION_ARG(value, 2);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(index, 1);
+ GET_VARIANT_PTR(value, 2);
bool valid;
dst->set(*index, *value, &valid);
@@ -824,9 +826,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_KEYED_VALIDATED) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(index, 1);
- GET_INSTRUCTION_ARG(value, 2);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(index, 1);
+ GET_VARIANT_PTR(value, 2);
int index_setter = _code_ptr[ip + 4];
GD_ERR_BREAK(index_setter < 0 || index_setter >= _keyed_setters_count);
@@ -854,9 +856,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_INDEXED_VALIDATED) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(index, 1);
- GET_INSTRUCTION_ARG(value, 2);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(index, 1);
+ GET_VARIANT_PTR(value, 2);
int index_setter = _code_ptr[ip + 4];
GD_ERR_BREAK(index_setter < 0 || index_setter >= _indexed_setters_count);
@@ -886,9 +888,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_KEYED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(index, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(index, 1);
+ GET_VARIANT_PTR(dst, 2);
bool valid;
#ifdef DEBUG_ENABLED
@@ -918,9 +920,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_KEYED_VALIDATED) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(key, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(key, 1);
+ GET_VARIANT_PTR(dst, 2);
int index_getter = _code_ptr[ip + 4];
GD_ERR_BREAK(index_getter < 0 || index_getter >= _keyed_getters_count);
@@ -954,9 +956,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_INDEXED_VALIDATED) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(index, 1);
- GET_INSTRUCTION_ARG(dst, 2);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(index, 1);
+ GET_VARIANT_PTR(dst, 2);
int index_getter = _code_ptr[ip + 4];
GD_ERR_BREAK(index_getter < 0 || index_getter >= _indexed_getters_count);
@@ -986,8 +988,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_NAMED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(value, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(value, 1);
int indexname = _code_ptr[ip + 3];
@@ -1011,8 +1013,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_NAMED_VALIDATED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(value, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(value, 1);
int index_setter = _code_ptr[ip + 3];
GD_ERR_BREAK(index_setter < 0 || index_setter >= _setters_count);
@@ -1026,8 +1028,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_NAMED) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(dst, 1);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(dst, 1);
int indexname = _code_ptr[ip + 3];
@@ -1056,8 +1058,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_NAMED_VALIDATED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(dst, 1);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(dst, 1);
int index_getter = _code_ptr[ip + 3];
GD_ERR_BREAK(index_getter < 0 || index_getter >= _getters_count);
@@ -1070,7 +1072,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_SET_MEMBER) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(src, 0);
+ GET_VARIANT_PTR(src, 0);
int indexname = _code_ptr[ip + 2];
GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
const StringName *index = &_global_names_ptr[indexname];
@@ -1094,7 +1096,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_GET_MEMBER) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
+ GET_VARIANT_PTR(dst, 0);
int indexname = _code_ptr[ip + 2];
GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
const StringName *index = &_global_names_ptr[indexname];
@@ -1113,8 +1115,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(src, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(src, 1);
*dst = *src;
@@ -1124,7 +1126,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TRUE) {
CHECK_SPACE(2);
- GET_INSTRUCTION_ARG(dst, 0);
+ GET_VARIANT_PTR(dst, 0);
*dst = true;
@@ -1134,7 +1136,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_FALSE) {
CHECK_SPACE(2);
- GET_INSTRUCTION_ARG(dst, 0);
+ GET_VARIANT_PTR(dst, 0);
*dst = false;
@@ -1144,8 +1146,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(src, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(src, 1);
Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
@@ -1173,8 +1175,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(src, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(src, 1);
Array *dst_arr = VariantInternal::get_array(dst);
@@ -1198,11 +1200,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(src, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(src, 1);
#ifdef DEBUG_ENABLED
- GET_INSTRUCTION_ARG(type, 2);
+ GET_VARIANT_PTR(type, 2);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
GD_ERR_BREAK(!nc);
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
@@ -1226,11 +1228,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(dst, 0);
- GET_INSTRUCTION_ARG(src, 1);
+ GET_VARIANT_PTR(dst, 0);
+ GET_VARIANT_PTR(src, 1);
#ifdef DEBUG_ENABLED
- GET_INSTRUCTION_ARG(type, 2);
+ GET_VARIANT_PTR(type, 2);
Script *base_type = Object::cast_to<Script>(type->operator Object *());
GD_ERR_BREAK(!base_type);
@@ -1275,8 +1277,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_CAST_TO_BUILTIN) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(dst, 1);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(dst, 1);
Variant::Type to_type = (Variant::Type)_code_ptr[ip + 3];
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
@@ -1304,9 +1306,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_CAST_TO_NATIVE) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(dst, 1);
- GET_INSTRUCTION_ARG(to_type, 2);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(dst, 1);
+ GET_VARIANT_PTR(to_type, 2);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
GD_ERR_BREAK(!nc);
@@ -1335,9 +1337,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_CAST_TO_SCRIPT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(src, 0);
- GET_INSTRUCTION_ARG(dst, 1);
- GET_INSTRUCTION_ARG(to_type, 2);
+ GET_VARIANT_PTR(src, 0);
+ GET_VARIANT_PTR(dst, 1);
+ GET_VARIANT_PTR(to_type, 2);
Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
@@ -1383,6 +1385,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(2 + instr_arg_count);
ip += instr_arg_count;
@@ -1390,6 +1393,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int argc = _code_ptr[ip + 1];
Variant::Type t = Variant::Type(_code_ptr[ip + 2]);
+
Variant **argptrs = instruction_args;
GET_INSTRUCTION_ARG(dst, argc);
@@ -1409,8 +1413,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT_VALIDATED) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(2 + instr_arg_count);
-
ip += instr_arg_count;
int argc = _code_ptr[ip + 1];
@@ -1430,6 +1434,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT_ARRAY) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(1 + instr_arg_count);
ip += instr_arg_count;
@@ -1451,6 +1456,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT_TYPED_ARRAY) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1480,6 +1486,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(2 + instr_arg_count);
ip += instr_arg_count;
@@ -1504,11 +1511,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_CALL_ASYNC)
OPCODE(OPCODE_CALL_RETURN)
OPCODE(OPCODE_CALL) {
- CHECK_SPACE(3 + instr_arg_count);
- bool call_ret = (_code_ptr[ip] & INSTR_MASK) != OPCODE_CALL;
+ bool call_ret = (_code_ptr[ip]) != OPCODE_CALL;
#ifdef DEBUG_ENABLED
- bool call_async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+ bool call_async = (_code_ptr[ip]) == OPCODE_CALL_ASYNC;
#endif
+ LOAD_INSTRUCTION_ARGS
+ CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1533,8 +1541,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err;
if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1);
+#ifdef DEBUG_ENABLED
+ Variant::Type base_type = base->get_type();
+ Object *base_obj = base->get_validated_object();
+ StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
+#endif
base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED
+ if (ret->get_type() == Variant::NIL) {
+ if (base_type == Variant::OBJECT) {
+ if (base_obj) {
+ MethodBind *method = ClassDB::get_method(base_class, *methodname);
+ if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) {
+ err_text = R"(Trying to get a return value of a method that returns "void")";
+ OPCODE_BREAK;
+ }
+ }
+ } else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) {
+ err_text = R"(Trying to get a return value of a method that returns "void")";
+ OPCODE_BREAK;
+ }
+ }
+
if (!call_async && ret->get_type() == Variant::OBJECT) {
// Check if getting a function state without await.
bool was_freed = false;
@@ -1603,8 +1631,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_CALL_METHOD_BIND)
OPCODE(OPCODE_CALL_METHOD_BIND_RET) {
+ bool call_ret = (_code_ptr[ip]) == OPCODE_CALL_METHOD_BIND_RET;
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
- bool call_ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
ip += instr_arg_count;
@@ -1682,6 +1711,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_BUILTIN_STATIC) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(4 + instr_arg_count);
ip += instr_arg_count;
@@ -1727,6 +1757,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_NATIVE_STATIC) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1770,6 +1801,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ LOAD_INSTRUCTION_ARGS \
CHECK_SPACE(3 + instr_arg_count); \
ip += instr_arg_count; \
int argc = _code_ptr[ip + 1]; \
@@ -1808,6 +1840,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#else
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
+ LOAD_INSTRUCTION_ARGS \
CHECK_SPACE(3 + instr_arg_count); \
ip += instr_arg_count; \
int argc = _code_ptr[ip + 1]; \
@@ -1865,6 +1898,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_CALL_PTR(PACKED_VECTOR3_ARRAY);
OPCODE_CALL_PTR(PACKED_COLOR_ARRAY);
OPCODE(OPCODE_CALL_PTRCALL_OBJECT) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1919,6 +1953,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_PTRCALL_NO_RETURN) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -1971,6 +2006,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) {
+ LOAD_INSTRUCTION_ARGS
+
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -2005,6 +2042,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_UTILITY) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -2039,6 +2077,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -2060,6 +2099,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -2095,6 +2135,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CALL_SELF_BASE) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(3 + instr_arg_count);
ip += instr_arg_count;
@@ -2163,8 +2204,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_AWAIT) {
CHECK_SPACE(2);
- // Do the oneshot connect.
- GET_INSTRUCTION_ARG(argobj, 0);
+ // Do the one-shot connect.
+ GET_VARIANT_PTR(argobj, 0);
Signal sig;
bool is_signal = true;
@@ -2191,7 +2232,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (result.get_type() != Variant::SIGNAL) {
// Not async, return immediately using the target from OPCODE_AWAIT_RESUME.
- GET_VARIANT_PTR(target, 3);
+ GET_VARIANT_PTR(target, 2);
*target = result;
ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
is_signal = false;
@@ -2216,7 +2257,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->state.line = line;
gdfs->state.script = _script;
{
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
_script->pending_func_states.add(&gdfs->scripts_list);
if (p_instance) {
gdfs->state.instance = p_instance;
@@ -2227,14 +2268,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
#ifdef DEBUG_ENABLED
gdfs->state.function_name = name;
- gdfs->state.script_path = _script->get_path();
+ gdfs->state.script_path = _script->get_script_path();
#endif
gdfs->state.defarg = defarg;
gdfs->function = this;
retvalue = gdfs;
- Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback").bind(retvalue), Object::CONNECT_ONESHOT);
+ Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback").bind(retvalue), Object::CONNECT_ONE_SHOT);
if (err != OK) {
err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
OPCODE_BREAK;
@@ -2257,13 +2298,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE_BREAK;
}
#endif
- GET_INSTRUCTION_ARG(result, 0);
+ GET_VARIANT_PTR(result, 0);
*result = p_state->result;
ip += 2;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CREATE_LAMBDA) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(2 + instr_arg_count);
ip += instr_arg_count;
@@ -2292,6 +2334,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_CREATE_SELF_LAMBDA) {
+ LOAD_INSTRUCTION_ARGS
CHECK_SPACE(2 + instr_arg_count);
GD_ERR_BREAK(p_instance == nullptr);
@@ -2338,7 +2381,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_JUMP_IF) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(test, 0);
+ GET_VARIANT_PTR(test, 0);
bool result = test->booleanize();
@@ -2355,7 +2398,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_JUMP_IF_NOT) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(test, 0);
+ GET_VARIANT_PTR(test, 0);
bool result = test->booleanize();
@@ -2378,7 +2421,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_JUMP_IF_SHARED) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(val, 0);
+ GET_VARIANT_PTR(val, 0);
if (val->is_shared()) {
int to = _code_ptr[ip + 2];
@@ -2392,7 +2435,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_RETURN) {
CHECK_SPACE(2);
- GET_INSTRUCTION_ARG(r, 0);
+ GET_VARIANT_PTR(r, 0);
retvalue = *r;
#ifdef DEBUG_ENABLED
exit_ok = true;
@@ -2402,7 +2445,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_RETURN_TYPED_BUILTIN) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(r, 0);
+ GET_VARIANT_PTR(r, 0);
Variant::Type ret_type = (Variant::Type)_code_ptr[ip + 2];
GD_ERR_BREAK(ret_type < 0 || ret_type >= Variant::VARIANT_MAX);
@@ -2433,9 +2476,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_RETURN_TYPED_ARRAY) {
CHECK_SPACE(5);
- GET_INSTRUCTION_ARG(r, 0);
+ GET_VARIANT_PTR(r, 0);
- GET_INSTRUCTION_ARG(script_type, 1);
+ GET_VARIANT_PTR(script_type, 1);
Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 3];
int native_type_idx = _code_ptr[ip + 4];
GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
@@ -2474,9 +2517,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_RETURN_TYPED_NATIVE) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(r, 0);
+ GET_VARIANT_PTR(r, 0);
- GET_INSTRUCTION_ARG(type, 1);
+ GET_VARIANT_PTR(type, 1);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
GD_ERR_BREAK(!nc);
@@ -2514,9 +2557,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_RETURN_TYPED_SCRIPT) {
CHECK_SPACE(3);
- GET_INSTRUCTION_ARG(r, 0);
+ GET_VARIANT_PTR(r, 0);
- GET_INSTRUCTION_ARG(type, 1);
+ GET_VARIANT_PTR(type, 1);
Script *base_type = Object::cast_to<Script>(type->operator Object *());
GD_ERR_BREAK(!base_type);
@@ -2580,8 +2623,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN) {
CHECK_SPACE(8); // Space for this and a regular iterate.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
bool valid;
if (!container->iter_init(*counter, valid)) {
@@ -2595,7 +2638,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = container->iter_get(*counter, valid);
#ifdef DEBUG_ENABLED
@@ -2612,8 +2655,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_INT) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
int64_t size = *VariantInternal::get_int(container);
@@ -2621,7 +2664,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_int(counter) = 0;
if (size > 0) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::INT);
*VariantInternal::get_int(iterator) = 0;
@@ -2639,8 +2682,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_FLOAT) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
double size = *VariantInternal::get_float(container);
@@ -2648,7 +2691,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_float(counter) = 0.0;
if (size > 0) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::FLOAT);
*VariantInternal::get_float(iterator) = 0;
@@ -2666,8 +2709,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Vector2 *bounds = VariantInternal::get_vector2(container);
@@ -2675,7 +2718,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_float(counter) = bounds->x;
if (bounds->x < bounds->y) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::FLOAT);
*VariantInternal::get_float(iterator) = bounds->x;
@@ -2693,8 +2736,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2I) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Vector2i *bounds = VariantInternal::get_vector2i(container);
@@ -2702,7 +2745,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_int(counter) = bounds->x;
if (bounds->x < bounds->y) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::INT);
*VariantInternal::get_int(iterator) = bounds->x;
@@ -2720,8 +2763,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Vector3 *bounds = VariantInternal::get_vector3(container);
double from = bounds->x;
@@ -2734,7 +2777,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
if (do_continue) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::FLOAT);
*VariantInternal::get_float(iterator) = from;
@@ -2752,8 +2795,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3I) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Vector3i *bounds = VariantInternal::get_vector3i(container);
int64_t from = bounds->x;
@@ -2766,7 +2809,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
if (do_continue) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::INT);
*VariantInternal::get_int(iterator) = from;
@@ -2784,8 +2827,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_STRING) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
String *str = VariantInternal::get_string(container);
@@ -2793,7 +2836,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_int(counter) = 0;
if (!str->is_empty()) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
VariantInternal::initialize(iterator, Variant::STRING);
*VariantInternal::get_string(iterator) = str->substr(0, 1);
@@ -2811,14 +2854,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_DICTIONARY) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Dictionary *dict = VariantInternal::get_dictionary(container);
const Variant *next = dict->next(nullptr);
if (!dict->is_empty()) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*counter = *next;
*iterator = *next;
@@ -2836,8 +2879,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_ARRAY) {
CHECK_SPACE(8); // Check space for iterate instruction too.
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
Array *array = VariantInternal::get_array(container);
@@ -2845,7 +2888,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
*VariantInternal::get_int(counter) = 0;
if (!array->is_empty()) {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = array->get(0);
// Skip regular iterate.
@@ -2862,13 +2905,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(8); \
- GET_INSTRUCTION_ARG(counter, 0); \
- GET_INSTRUCTION_ARG(container, 1); \
+ GET_VARIANT_PTR(counter, 0); \
+ GET_VARIANT_PTR(container, 1); \
Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
VariantInternal::initialize(counter, Variant::INT); \
*VariantInternal::get_int(counter) = 0; \
if (!array->is_empty()) { \
- GET_INSTRUCTION_ARG(iterator, 2); \
+ GET_VARIANT_PTR(iterator, 2); \
VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
*it = array->get(0); \
@@ -2894,8 +2937,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
#ifdef DEBUG_ENABLED
bool freed = false;
@@ -2933,7 +2976,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
#ifdef DEBUG_ENABLED
if (ce.error != Callable::CallError::CALL_OK) {
@@ -2950,8 +2993,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
bool valid;
if (!container->iter_next(*counter, valid)) {
@@ -2965,7 +3008,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = container->iter_get(*counter, valid);
#ifdef DEBUG_ENABLED
@@ -2982,8 +3025,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_INT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
int64_t size = *VariantInternal::get_int(container);
int64_t *count = VariantInternal::get_int(counter);
@@ -2995,7 +3038,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_int(iterator) = *count;
ip += 5; // Loop again.
@@ -3006,8 +3049,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_FLOAT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
double size = *VariantInternal::get_float(container);
double *count = VariantInternal::get_float(counter);
@@ -3019,7 +3062,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_float(iterator) = *count;
ip += 5; // Loop again.
@@ -3030,8 +3073,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_VECTOR2) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Vector2 *bounds = VariantInternal::get_vector2((const Variant *)container);
double *count = VariantInternal::get_float(counter);
@@ -3043,7 +3086,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_float(iterator) = *count;
ip += 5; // Loop again.
@@ -3054,8 +3097,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_VECTOR2I) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Vector2i *bounds = VariantInternal::get_vector2i((const Variant *)container);
int64_t *count = VariantInternal::get_int(counter);
@@ -3067,7 +3110,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_int(iterator) = *count;
ip += 5; // Loop again.
@@ -3078,8 +3121,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_VECTOR3) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Vector3 *bounds = VariantInternal::get_vector3((const Variant *)container);
double *count = VariantInternal::get_float(counter);
@@ -3091,7 +3134,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_float(iterator) = *count;
ip += 5; // Loop again.
@@ -3102,8 +3145,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_VECTOR3I) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Vector3i *bounds = VariantInternal::get_vector3i((const Variant *)container);
int64_t *count = VariantInternal::get_int(counter);
@@ -3115,7 +3158,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_int(iterator) = *count;
ip += 5; // Loop again.
@@ -3126,8 +3169,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_STRING) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const String *str = VariantInternal::get_string((const Variant *)container);
int64_t *idx = VariantInternal::get_int(counter);
@@ -3138,7 +3181,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*VariantInternal::get_string(iterator) = str->substr(*idx, 1);
ip += 5; // Loop again.
@@ -3149,8 +3192,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_DICTIONARY) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Dictionary *dict = VariantInternal::get_dictionary((const Variant *)container);
const Variant *next = dict->next(counter);
@@ -3160,7 +3203,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*counter = *next;
*iterator = *next;
@@ -3172,8 +3215,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_ARRAY) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
const Array *array = VariantInternal::get_array((const Variant *)container);
int64_t *idx = VariantInternal::get_int(counter);
@@ -3184,7 +3227,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = array->get(*idx);
ip += 5; // Loop again.
@@ -3195,8 +3238,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
CHECK_SPACE(4); \
- GET_INSTRUCTION_ARG(counter, 0); \
- GET_INSTRUCTION_ARG(container, 1); \
+ GET_VARIANT_PTR(counter, 0); \
+ GET_VARIANT_PTR(container, 1); \
const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
int64_t *idx = VariantInternal::get_int(counter); \
(*idx)++; \
@@ -3205,7 +3248,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
ip = jumpto; \
} else { \
- GET_INSTRUCTION_ARG(iterator, 2); \
+ GET_VARIANT_PTR(iterator, 2); \
*VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
ip += 5; \
} \
@@ -3225,8 +3268,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODE(OPCODE_ITERATE_OBJECT) {
CHECK_SPACE(4);
- GET_INSTRUCTION_ARG(counter, 0);
- GET_INSTRUCTION_ARG(container, 1);
+ GET_VARIANT_PTR(counter, 0);
+ GET_VARIANT_PTR(container, 1);
#ifdef DEBUG_ENABLED
bool freed = false;
@@ -3264,7 +3307,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
ip = jumpto;
} else {
- GET_INSTRUCTION_ARG(iterator, 2);
+ GET_VARIANT_PTR(iterator, 2);
*iterator = obj->callp(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
#ifdef DEBUG_ENABLED
if (ce.error != Callable::CallError::CALL_OK) {
@@ -3283,7 +3326,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
int global_idx = _code_ptr[ip + 2];
GD_ERR_BREAK(global_idx < 0 || global_idx >= GDScriptLanguage::get_singleton()->get_global_array_size());
- GET_INSTRUCTION_ARG(dst, 0);
+ GET_VARIANT_PTR(dst, 0);
*dst = GDScriptLanguage::get_singleton()->get_global_array()[global_idx];
ip += 3;
@@ -3297,7 +3340,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
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);
+ GET_VARIANT_PTR(dst, 0);
*dst = GDScriptLanguage::get_singleton()->get_named_globals_map()[*globalname];
ip += 3;
@@ -3307,7 +3350,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define OPCODE_TYPE_ADJUST(m_v_type, m_c_type) \
OPCODE(OPCODE_TYPE_ADJUST_##m_v_type) { \
CHECK_SPACE(2); \
- GET_INSTRUCTION_ARG(arg, 0); \
+ GET_VARIANT_PTR(arg, 0); \
VariantTypeAdjust<m_c_type>::adjust(arg); \
ip += 2; \
} \
@@ -3355,13 +3398,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
CHECK_SPACE(3);
#ifdef DEBUG_ENABLED
- GET_INSTRUCTION_ARG(test, 0);
+ GET_VARIANT_PTR(test, 0);
bool result = test->booleanize();
if (!result) {
String message_str;
if (_code_ptr[ip + 2] != 0) {
- GET_INSTRUCTION_ARG(message, 1);
+ GET_VARIANT_PTR(message, 1);
message_str = *message;
}
if (message_str.is_empty()) {
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 1cae7bdfac..184cecb316 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_warning.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_warning.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_warning.h"
@@ -80,10 +80,6 @@ String GDScriptWarning::get_message() const {
case STANDALONE_EXPRESSION: {
return "Standalone expression (the line has no effect).";
} break;
- case VOID_ASSIGNMENT: {
- CHECK_SYMBOLS(1);
- return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
- } break;
case NARROWING_CONVERSION: {
return "Narrowing conversion (float is converted to int and loses precision).";
} break;
@@ -96,7 +92,7 @@ String GDScriptWarning::get_message() const {
} break;
case RETURN_VALUE_DISCARDED: {
CHECK_SYMBOLS(1);
- return "The function '" + symbols[0] + "()' returns a value, but this value is never used.";
+ return "The function '" + symbols[0] + "()' returns a value that will be discarded if not used.";
} break;
case PROPERTY_USED_AS_FUNCTION: {
CHECK_SYMBOLS(2);
@@ -155,6 +151,10 @@ String GDScriptWarning::get_message() const {
case INT_ASSIGNED_TO_ENUM: {
return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
}
+ case STATIC_CALLED_ON_INSTANCE: {
+ CHECK_SYMBOLS(2);
+ return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]);
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -167,6 +167,10 @@ int GDScriptWarning::get_default_value(Code p_code) {
if (get_name_from_code(p_code).to_lower().begins_with("unsafe_")) {
return WarnLevel::IGNORE;
}
+ // Too spammy by default on common cases (connect, Tween, etc.).
+ if (p_code == RETURN_VALUE_DISCARDED) {
+ return WarnLevel::IGNORE;
+ }
return WarnLevel::WARN;
}
@@ -194,7 +198,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"UNREACHABLE_CODE",
"UNREACHABLE_PATTERN",
"STANDALONE_EXPRESSION",
- "VOID_ASSIGNMENT",
"NARROWING_CONVERSION",
"INCOMPATIBLE_TERNARY",
"UNUSED_SIGNAL",
@@ -215,6 +218,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"EMPTY_FILE",
"SHADOWED_GLOBAL_IDENTIFIER",
"INT_ASSIGNED_TO_ENUM",
+ "STATIC_CALLED_ON_INSTANCE",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index a639e7b44e..e3aee45f33 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_warning.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_warning.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_WARNING_H
#define GDSCRIPT_WARNING_H
@@ -57,7 +57,6 @@ public:
UNREACHABLE_CODE, // Code after a return statement.
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
STANDALONE_EXPRESSION, // Expression not assigned to a variable.
- VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable.
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
UNUSED_SIGNAL, // Signal is defined but never emitted.
@@ -78,6 +77,7 @@ public:
EMPTY_FILE, // A script file is empty.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
+ STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
WARNING_MAX,
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 46a9b33eb0..146ed10ceb 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_extend_parser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_extend_parser.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_extend_parser.h"
@@ -38,8 +38,8 @@
void ExtendGDScriptParser::update_diagnostics() {
diagnostics.clear();
- const List<ParserError> &errors = get_errors();
- for (const ParserError &error : errors) {
+ const List<ParserError> &parser_errors = get_errors();
+ for (const ParserError &error : parser_errors) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Error;
diagnostic.message = error.message;
@@ -47,9 +47,9 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.code = -1;
lsp::Range range;
lsp::Position pos;
- const PackedStringArray lines = get_lines();
- int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1);
- const String &line_text = lines[line];
+ const PackedStringArray line_array = get_lines();
+ int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, line_array.size() - 1);
+ const String &line_text = line_array[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
@@ -59,8 +59,8 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostics.push_back(diagnostic);
}
- const List<GDScriptWarning> &warnings = get_warnings();
- for (const GDScriptWarning &warning : warnings) {
+ const List<GDScriptWarning> &parser_warnings = get_warnings();
+ for (const GDScriptWarning &warning : parser_warnings) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Warning;
diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message();
@@ -83,8 +83,7 @@ void ExtendGDScriptParser::update_diagnostics() {
void ExtendGDScriptParser::update_symbols() {
members.clear();
- const GDScriptParser::Node *head = get_tree();
- if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
+ if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(get_tree())) {
parse_class_symbol(gdclass, class_symbol);
for (int i = 0; i < class_symbol.children.size(); i++) {
@@ -107,26 +106,26 @@ void ExtendGDScriptParser::update_symbols() {
void ExtendGDScriptParser::update_document_links(const String &p_code) {
document_links.clear();
- GDScriptTokenizer tokenizer;
+ GDScriptTokenizer scr_tokenizer;
Ref<FileAccess> fs = FileAccess::create(FileAccess::ACCESS_RESOURCES);
- tokenizer.set_source_code(p_code);
+ scr_tokenizer.set_source_code(p_code);
while (true) {
- GDScriptTokenizer::Token token = tokenizer.scan();
+ GDScriptTokenizer::Token token = scr_tokenizer.scan();
if (token.type == GDScriptTokenizer::Token::TK_EOF) {
break;
} else if (token.type == GDScriptTokenizer::Token::LITERAL) {
const Variant &const_val = token.literal;
if (const_val.get_type() == Variant::STRING) {
- String path = const_val;
- bool exists = fs->file_exists(path);
+ String scr_path = const_val;
+ bool exists = fs->file_exists(scr_path);
if (!exists) {
- path = get_path().get_base_dir() + "/" + path;
- exists = fs->file_exists(path);
+ scr_path = get_path().get_base_dir() + "/" + scr_path;
+ exists = fs->file_exists(scr_path);
}
if (exists) {
String value = const_val;
lsp::DocumentLink link;
- link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path);
+ link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(scr_path);
link.range.start.line = LINE_NUMBER_TO_INDEX(token.start_line);
link.range.end.line = LINE_NUMBER_TO_INDEX(token.end_line);
link.range.start.character = LINE_NUMBER_TO_INDEX(token.start_column);
@@ -351,8 +350,8 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
if (parameter->get_datatype().is_hard_type()) {
parameters += ": " + parameter->get_datatype().to_string();
}
- if (parameter->default_value != nullptr) {
- parameters += " = " + parameter->default_value->reduced_value.to_json_string();
+ if (parameter->initializer != nullptr) {
+ parameters += " = " + parameter->initializer->reduced_value.to_json_string();
}
}
r_symbol.detail += parameters + ")";
@@ -437,11 +436,11 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
if (!p_docs_down) { // inline comment
String inline_comment = lines[p_line];
- int comment_start = inline_comment.find("#");
+ int comment_start = inline_comment.find("##");
if (comment_start != -1) {
inline_comment = inline_comment.substr(comment_start, inline_comment.length()).strip_edges();
if (inline_comment.length() > 1) {
- doc_lines.push_back(inline_comment.substr(1, inline_comment.length()));
+ doc_lines.push_back(inline_comment.substr(2, inline_comment.length()));
}
}
}
@@ -454,8 +453,8 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
}
String line_comment = lines[i].strip_edges(true, false);
- if (line_comment.begins_with("#")) {
- line_comment = line_comment.substr(1, line_comment.length());
+ if (line_comment.begins_with("##")) {
+ line_comment = line_comment.substr(2, line_comment.length());
if (p_docs_down) {
doc_lines.push_back(line_comment);
} else {
@@ -696,8 +695,8 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
Dictionary arg;
arg["name"] = p_func->parameters[i]->identifier->name;
arg["type"] = p_func->parameters[i]->get_datatype().to_string();
- if (p_func->parameters[i]->default_value != nullptr) {
- arg["default_value"] = p_func->parameters[i]->default_value->reduced_value;
+ if (p_func->parameters[i]->initializer != nullptr) {
+ arg["default_value"] = p_func->parameters[i]->initializer->reduced_value;
}
parameters.push_back(arg);
}
@@ -731,7 +730,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
Array nested_classes;
Array constants;
- Array members;
+ Array class_members;
Array signals;
Array methods;
Array static_functions;
@@ -792,7 +791,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
api["signature"] = symbol->detail;
api["description"] = symbol->documentation;
}
- members.push_back(api);
+ class_members.push_back(api);
} break;
case ClassNode::Member::SIGNAL: {
Dictionary api;
@@ -824,7 +823,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
class_api["sub_classes"] = nested_classes;
class_api["constants"] = constants;
- class_api["members"] = members;
+ class_api["members"] = class_members;
class_api["signals"] = signals;
class_api["methods"] = methods;
class_api["static_functions"] = static_functions;
@@ -834,8 +833,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
Dictionary ExtendGDScriptParser::generate_api() const {
Dictionary api;
- const GDScriptParser::Node *head = get_tree();
- if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
+ if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(get_tree())) {
api = dump_class_api(gdclass);
}
return api;
@@ -846,8 +844,9 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
lines = p_code.split("\n");
Error err = GDScriptParser::parse(p_code, p_path, false);
+ GDScriptAnalyzer analyzer(this);
+
if (err == OK) {
- GDScriptAnalyzer analyzer(this);
err = analyzer.analyze();
}
update_diagnostics();
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 08bba4a2d4..4500cb01f3 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_extend_parser.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_extend_parser.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_EXTEND_PARSER_H
#define 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 7460f8edff..acd75f039a 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -1,39 +1,41 @@
-/*************************************************************************/
-/* gdscript_language_protocol.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_language_protocol.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_language_protocol.h"
#include "core/config/project_settings.h"
#include "editor/doc_tools.h"
+#include "editor/editor_help.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = nullptr;
@@ -183,7 +185,9 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
if (root_uri.length() && is_same_workspace) {
workspace->root_uri = root_uri;
} else {
- workspace->root_uri = "file://" + workspace->root;
+ String r_root = workspace->root;
+ r_root = r_root.lstrip("/");
+ workspace->root_uri = "file:///" + r_root;
Dictionary params;
params["path"] = workspace->root;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 3c9cfe512f..d25814f8aa 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_language_protocol.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_language_protocol.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_LANGUAGE_PROTOCOL_H
#define GDSCRIPT_LANGUAGE_PROTOCOL_H
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 14337e87da..062be0fe20 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_language_server.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_language_server.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_language_server.h"
@@ -34,6 +34,7 @@
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
GDScriptLanguageServer::GDScriptLanguageServer() {
_EDITOR_DEF("network/language_server/remote_host", host);
@@ -60,12 +61,12 @@ void GDScriptLanguageServer::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- String host = String(_EDITOR_GET("network/language_server/remote_host"));
- int port = (int)_EDITOR_GET("network/language_server/remote_port");
- bool use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (host != this->host || port != this->port || use_thread != this->use_thread) {
- this->stop();
- this->start();
+ String remote_host = String(_EDITOR_GET("network/language_server/remote_host"));
+ int remote_port = (int)_EDITOR_GET("network/language_server/remote_port");
+ bool remote_use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
+ if (remote_host != host || remote_port != port || remote_use_thread != use_thread) {
+ stop();
+ start();
}
} break;
}
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 8de72fc9c9..a130dc3ac8 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_language_server.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_language_server.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_LANGUAGE_SERVER_H
#define GDSCRIPT_LANGUAGE_SERVER_H
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 5ad9680ea0..b9e6921034 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_text_document.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_text_document.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_text_document.h"
@@ -42,6 +42,7 @@ void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+ ClassDB::bind_method(D_METHOD("willSaveWaitUntil"), &GDScriptTextDocument::willSaveWaitUntil);
ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
@@ -81,6 +82,16 @@ void GDScriptTextDocument::didChange(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::willSaveWaitUntil(const Variant &p_param) {
+ lsp::TextDocumentItem doc = load_document_item(p_param);
+
+ String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
+ Ref<Script> scr = ResourceLoader::load(path);
+ if (scr.is_valid()) {
+ ScriptEditor::get_singleton()->clear_docs_from_script(scr);
+ }
+}
+
void GDScriptTextDocument::didSave(const Variant &p_param) {
lsp::TextDocumentItem doc = load_document_item(p_param);
Dictionary dict = p_param;
@@ -88,11 +99,17 @@ void GDScriptTextDocument::didSave(const Variant &p_param) {
sync_script_content(doc.uri, text);
- /*String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
-
- Ref<GDScript> script = ResourceLoader::load(path);
- script->load_source_code(path);
- script->reload(true);*/
+ String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
+ Ref<GDScript> scr = ResourceLoader::load(path);
+ if (scr.is_valid() && (scr->load_source_code(path) == OK)) {
+ if (scr->is_tool()) {
+ scr->get_language()->reload_tool_script(scr, true);
+ } else {
+ scr->reload(true);
+ }
+ scr->update_exports();
+ ScriptEditor::get_singleton()->update_docs_from_script(scr);
+ }
}
lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
@@ -213,8 +230,8 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
arr = native_member_completions.duplicate();
for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) {
- ExtendGDScriptParser *script = E.value;
- const Array &items = script->get_member_completions();
+ ExtendGDScriptParser *scr = E.value;
+ const Array &items = scr->get_member_completions();
const int start_size = arr.size();
arr.resize(start_size + items.size());
@@ -417,13 +434,6 @@ void GDScriptTextDocument::sync_script_content(const String &p_path, const Strin
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
EditorFileSystem::get_singleton()->update_file(path);
- Error error;
- Ref<GDScript> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error);
- if (error == OK) {
- if (script->load_source_code(path) == OK) {
- script->reload(true);
- }
- }
}
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 87bc08a34e..aeda10de89 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_text_document.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_text_document.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
@@ -45,6 +45,7 @@ protected:
void didOpen(const Variant &p_param);
void didClose(const Variant &p_param);
void didChange(const Variant &p_param);
+ void willSaveWaitUntil(const Variant &p_param);
void didSave(const Variant &p_param);
void sync_script_content(const String &p_path, const String &p_content);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 959651c024..b90c452346 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_workspace.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_workspace.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_workspace.h"
@@ -38,6 +38,7 @@
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "gdscript_language_protocol.h"
#include "scene/resources/packed_scene.h"
@@ -54,14 +55,14 @@ void GDScriptWorkspace::_bind_methods() {
}
void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) {
- Ref<Script> script = obj->get_script();
+ Ref<Script> scr = obj->get_script();
- if (script->get_language()->get_name() != "GDScript") {
+ if (scr->get_language()->get_name() != "GDScript") {
return;
}
String function_signature = "func " + function;
- String source = script->get_source_code();
+ String source = scr->get_source_code();
if (source.contains(function_signature)) {
return;
@@ -97,7 +98,7 @@ void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStr
text_edit.newText = function_body;
- String uri = get_file_uri(script->get_path());
+ String uri = get_file_uri(scr->get_path());
lsp::ApplyWorkspaceEditParams params;
params.edit.add_edit(uri, text_edit);
@@ -117,12 +118,12 @@ void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
void GDScriptWorkspace::remove_cache_parser(const String &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->value && script->value == parser->value) {
- memdelete(script->value);
+ HashMap<String, ExtendGDScriptParser *>::Iterator scr = scripts.find(p_path);
+ if (parser && scr) {
+ if (scr->value && scr->value == parser->value) {
+ memdelete(scr->value);
} else {
- memdelete(script->value);
+ memdelete(scr->value);
memdelete(parser->value);
}
parse_results.erase(p_path);
@@ -130,8 +131,8 @@ void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
} else if (parser) {
memdelete(parser->value);
parse_results.erase(p_path);
- } else if (script) {
- memdelete(script->value);
+ } else if (scr) {
+ memdelete(scr->value);
scripts.erase(p_path);
}
}
@@ -227,9 +228,9 @@ void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String>
String file_name = dir->get_next();
while (file_name.length()) {
if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
- list_script_files(p_root_dir.plus_file(file_name), r_files);
+ list_script_files(p_root_dir.path_join(file_name), r_files);
} else if (file_name.ends_with(".gd")) {
- String script_file = p_root_dir.plus_file(file_name);
+ String script_file = p_root_dir.path_join(file_name);
r_files.push_back(script_file);
}
file_name = dir->get_next();
@@ -498,11 +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("///", "//");
- path = path.replace("%3A", ":");
- path = path.replacen(root_uri + "/", "res://");
- path = path.uri_decode();
+ String path = p_uri.uri_decode();
+ String base_uri = root_uri.uri_decode();
+ path = path.replacen(base_uri + "/", "res://");
return path;
}
@@ -588,8 +587,8 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
while (!stack.is_empty()) {
current = Object::cast_to<Node>(stack.pop_back());
- Ref<GDScript> script = current->get_script();
- if (script.is_valid() && script->get_path() == path) {
+ Ref<GDScript> scr = current->get_script();
+ if (scr.is_valid() && scr->get_path() == path) {
break;
}
for (int i = 0; i < current->get_child_count(); ++i) {
@@ -597,8 +596,8 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
}
}
- Ref<GDScript> script = current->get_script();
- if (!script.is_valid() || script->get_path() != path) {
+ Ref<GDScript> scr = current->get_script();
+ if (!scr.is_valid() || scr->get_path() != path) {
current = owner_scene_node;
}
}
@@ -692,13 +691,13 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
}
for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
- const ExtendGDScriptParser *script = E.value;
- const ClassMembers &members = script->get_members();
+ const ExtendGDScriptParser *scr = E.value;
+ const ClassMembers &members = scr->get_members();
if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
r_list.push_back(*symbol);
}
- for (const KeyValue<String, ClassMembers> &F : script->get_inner_classes()) {
+ for (const KeyValue<String, ClassMembers> &F : scr->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);
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 88f3aaf957..a849ef8a8d 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_workspace.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_workspace.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_WORKSPACE_H
#define GDSCRIPT_WORKSPACE_H
diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h
index fbd40796c4..8a033204da 100644
--- a/modules/gdscript/language_server/godot_lsp.h
+++ b/modules/gdscript/language_server/godot_lsp.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godot_lsp.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godot_lsp.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GODOT_LSP_H
#define GODOT_LSP_H
@@ -546,7 +546,7 @@ struct TextDocumentSyncOptions {
* If present will save wait until requests are sent to the server. If omitted the request should not be
* sent.
*/
- bool willSaveWaitUntil = false;
+ bool willSaveWaitUntil = true;
/**
* If present save notifications are sent to the server. If omitted the notification should not be
@@ -702,7 +702,7 @@ struct DiagnosticRelatedInformation {
Dictionary to_json() const {
Dictionary dict;
- dict["location"] = location.to_json(),
+ dict["location"] = location.to_json();
dict["message"] = message;
return dict;
}
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 059ca703ab..b6feaadccf 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -71,23 +71,22 @@ class EditorExportGDScript : public EditorExportPlugin {
public:
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;
const Ref<EditorExportPreset> &preset = get_export_preset();
if (preset.is_valid()) {
- script_mode = preset->get_script_export_mode();
script_key = preset->get_script_encryption_key().to_lower();
}
- if (!p_path.ends_with(".gd") || script_mode == EditorExportPreset::MODE_SCRIPT_TEXT) {
+ if (!p_path.ends_with(".gd")) {
return;
}
- // TODO: Re-add compiled GDScript on export.
return;
}
+
+ virtual String _get_name() const override { return "GDScript"; }
};
static void _editor_init() {
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
index a7e6b02dcf..f7ec521ec8 100644
--- a/modules/gdscript/register_types.h
+++ b/modules/gdscript/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_REGISTER_TYPES_H
#define GDSCRIPT_REGISTER_TYPES_H
diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md
index 6e54085962..361d586d32 100644
--- a/modules/gdscript/tests/README.md
+++ b/modules/gdscript/tests/README.md
@@ -4,5 +4,5 @@ The `scripts/` folder contains integration tests in the form of GDScript files
and output files.
See the
-[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript)
+[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html#integration-tests-for-gdscript)
for information about creating and running GDScript integration tests.
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index ff4832bde0..d2c8b5c317 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_test_runner.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_test_runner.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "gdscript_test_runner.h"
@@ -36,6 +36,7 @@
#include "../gdscript_parser.h"
#include "core/config/project_settings.h"
+#include "core/core_globals.h"
#include "core/core_string_names.h"
#include "core/io/dir_access.h"
#include "core/io/file_access_pack.h"
@@ -70,27 +71,38 @@ void init_autoloads() {
continue;
}
- 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;
- Ref<Script> script = res;
- if (scn.is_valid()) {
- n = scn->instantiate();
- } else if (script.is_valid()) {
- StringName ibt = script->get_instance_base_type();
- bool valid_type = ClassDB::is_parent_class(ibt, "Node");
- ERR_CONTINUE_MSG(!valid_type, "Script does not inherit from Node: " + info.path);
+ if (ResourceLoader::get_resource_type(info.path) == "PackedScene") {
+ // Cache the scene reference before loading it (for cyclic references)
+ Ref<PackedScene> scn;
+ scn.instantiate();
+ scn->set_path(info.path);
+ scn->reload_from_file();
+ ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path));
+
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ }
+ } else {
+ Ref<Resource> res = ResourceLoader::load(info.path);
+ ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path));
- Object *obj = ClassDB::instantiate(ibt);
+ Ref<Script> scr = res;
+ if (scr.is_valid()) {
+ StringName ibt = scr->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path));
- ERR_CONTINUE_MSG(!obj, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt) + ".");
+ Object *obj = ClassDB::instantiate(ibt);
- n = Object::cast_to<Node>(obj);
- n->set_script(script);
+ ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(scr);
+ }
}
- ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path));
n->set_name(info.name);
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@@ -142,8 +154,8 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
#endif
// Enable printing to show results
- _print_line_enabled = true;
- _print_error_enabled = true;
+ CoreGlobals::print_line_enabled = true;
+ CoreGlobals::print_error_enabled = true;
}
GDScriptTestRunner::~GDScriptTestRunner() {
@@ -246,15 +258,18 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
next = dir->get_next();
continue;
}
- if (!make_tests_for_dir(current_dir.plus_file(next))) {
+ if (!make_tests_for_dir(current_dir.path_join(next))) {
return false;
}
} else {
- if (next.get_extension().to_lower() == "gd") {
+ if (next.ends_with(".notest.gd")) {
+ next = dir->get_next();
+ continue;
+ } else if (next.get_extension().to_lower() == "gd") {
#ifndef DEBUG_ENABLED
// On release builds, skip tests marked as debug only.
Error open_err = OK;
- Ref<FileAccess> script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err));
+ Ref<FileAccess> script_file(FileAccess::open(current_dir.path_join(next), FileAccess::READ, &open_err));
if (open_err != OK) {
ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next));
next = dir->get_next();
@@ -271,7 +286,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
if (!is_generating && !dir->file_exists(out_file)) {
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
}
- GDScriptTest test(current_dir.plus_file(next), current_dir.plus_file(out_file), source_dir);
+ GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
tests.push_back(test);
}
}
@@ -460,7 +475,6 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
Ref<GDScript> script;
script.instantiate();
script->set_path(source_file);
- script->set_script_path(source_file);
err = script->load_source_code(source_file);
if (err != OK) {
enable_stdout();
@@ -478,9 +492,9 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
result.output = get_text_for_status(result.status) + "\n";
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const GDScriptParser::ParserError &E : errors) {
- result.output += E.message + "\n"; // TODO: line, column?
- break; // Only the first error since the following might be cascading.
+ if (!errors.is_empty()) {
+ // Only the first error since the following might be cascading.
+ result.output += errors[0].message + "\n"; // TODO: line, column?
}
if (!p_is_generating) {
result.passed = check_output(result.output);
@@ -497,9 +511,9 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
result.output = get_text_for_status(result.status) + "\n";
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const GDScriptParser::ParserError &E : errors) {
- result.output += E.message + "\n"; // TODO: line, column?
- break; // Only the first error since the following might be cascading.
+ if (!errors.is_empty()) {
+ // Only the first error since the following might be cascading.
+ result.output += errors[0].message + "\n"; // TODO: line, column?
}
if (!p_is_generating) {
result.passed = check_output(result.output);
@@ -597,6 +611,9 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
}
enable_stdout();
+
+ GDScriptCache::remove_script(script->get_path());
+
return result;
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
index 033d2fcad1..b097f1b485 100644
--- a/modules/gdscript/tests/gdscript_test_runner.h
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_test_runner.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_test_runner.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_TEST_RUNNER_H
#define GDSCRIPT_TEST_RUNNER_H
diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h
index 0722fb800e..aed0ac2baf 100644
--- a/modules/gdscript/tests/gdscript_test_runner_suite.h
+++ b/modules/gdscript/tests/gdscript_test_runner_suite.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gdscript_test_runner_suite.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gdscript_test_runner_suite.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef GDSCRIPT_TEST_RUNNER_SUITE_H
#define GDSCRIPT_TEST_RUNNER_SUITE_H
@@ -69,6 +69,40 @@ func _init():
CHECK_MESSAGE(int(ref_counted->get_meta("result")) == 42, "The script should assign object metadata successfully.");
}
+TEST_CASE("[Modules][GDScript] Validate built-in API") {
+ GDScriptLanguage *lang = GDScriptLanguage::get_singleton();
+
+ // Validate methods.
+ List<MethodInfo> builtin_methods;
+ lang->get_public_functions(&builtin_methods);
+
+ SUBCASE("[Modules][GDScript] Validate built-in methods") {
+ for (const MethodInfo &mi : builtin_methods) {
+ for (int j = 0; j < mi.arguments.size(); j++) {
+ PropertyInfo arg = mi.arguments[j];
+
+ TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")),
+ vformat("Unnamed argument in position %d of built-in method '%s'.", j, mi.name));
+ }
+ }
+ }
+
+ // Validate annotations.
+ List<MethodInfo> builtin_annotations;
+ lang->get_public_annotations(&builtin_annotations);
+
+ SUBCASE("[Modules][GDScript] Validate built-in annotations") {
+ for (const MethodInfo &ai : builtin_annotations) {
+ for (int j = 0; j < ai.arguments.size(); j++) {
+ PropertyInfo arg = ai.arguments[j];
+
+ TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")),
+ vformat("Unnamed argument in position %d of built-in annotation '%s'.", j, ai.name));
+ }
+ }
+ }
+}
+
} // namespace GDScriptTests
#endif // GDSCRIPT_TEST_RUNNER_SUITE_H
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
new file mode 100644
index 0000000000..38c2faa859
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
@@ -0,0 +1,2 @@
+func test():
+ CanvasItem.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
new file mode 100644
index 0000000000..9eff912b59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Native class "CanvasItem" cannot be constructed as it is abstract.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
new file mode 100644
index 0000000000..118e7e8a45
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
@@ -0,0 +1,9 @@
+class A extends CanvasItem:
+ func _init():
+ print('no')
+
+class B extends A:
+ pass
+
+func test():
+ B.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
new file mode 100644
index 0000000000..8b956f5974
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd
new file mode 100644
index 0000000000..8123fc53d9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.gd
@@ -0,0 +1,3 @@
+enum { V }
+func test():
+ V = 1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd
new file mode 100644
index 0000000000..da2b13d690
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.gd
@@ -0,0 +1,3 @@
+enum NamedEnum { V }
+func test():
+ NamedEnum.V = 1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_named_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd
new file mode 100644
index 0000000000..0e1f7256f8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.gd
@@ -0,0 +1,4 @@
+signal your_base
+signal my_base
+func test():
+ your_base = my_base
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assign_signal.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd
new file mode 100644
index 0000000000..71616ea3af
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd
@@ -0,0 +1,5 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2, OTHER_ENUM_VALUE_3 }
+
+func test():
+ print(MyOtherEnum.OTHER_ENUM_VALUE_3 as MyEnum)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
new file mode 100644
index 0000000000..3a8d2a205a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid cast. Enum "cast_enum_bad_enum.gd::MyEnum" does not have value corresponding to "MyOtherEnum.OTHER_ENUM_VALUE_3" (2).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd
new file mode 100644
index 0000000000..60a31fb318
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd
@@ -0,0 +1,4 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ print(2 as MyEnum)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
new file mode 100644
index 0000000000..bc0d8b7834
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid cast. Enum "cast_enum_bad_int.gd::MyEnum" does not have enum value 2.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
index 87863baf75..b9a1d301ad 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The member "Vector2" cannot have the same name as a builtin type.
+Class "Vector2" hides a built-in type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd
new file mode 100644
index 0000000000..b8603dd4ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.gd
@@ -0,0 +1,5 @@
+const array: Array = [0]
+
+func test():
+ var key: int = 0
+ array[key] = 0
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_array_index_assign.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd
new file mode 100644
index 0000000000..9b5112b788
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.gd
@@ -0,0 +1,5 @@
+const dictionary := {}
+
+func test():
+ var key: int = 0
+ dictionary[key] = 0
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_dictionary_index_assign.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
new file mode 100644
index 0000000000..87fbe1229c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.gd
@@ -0,0 +1,5 @@
+const base := [0]
+
+func test():
+ var sub := base[0]
+ if sub is String: pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_subscript_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd
new file mode 100644
index 0000000000..251be70088
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.gd
@@ -0,0 +1,10 @@
+class A:
+ func _init():
+ pass
+
+class B extends A: pass
+class C extends A: pass
+
+func test():
+ var x := B.new()
+ print(x is C)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out
new file mode 100644
index 0000000000..91d5125ec0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constructor_call_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "B" so it can't be of type "C".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd
new file mode 100644
index 0000000000..d2f6404cd2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd
@@ -0,0 +1,8 @@
+func test():
+ print(InnerA.new())
+
+class InnerA extends InnerB:
+ pass
+
+class InnerB extends InnerA:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out
new file mode 100644
index 0000000000..75a94baa17
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cyclic inheritance.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd
new file mode 100644
index 0000000000..4292534951
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd
@@ -0,0 +1,5 @@
+func test():
+ print(c1)
+
+const c1 = c2
+const c2 = c1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out
new file mode 100644
index 0000000000..e71b3fc56a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "c1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd
new file mode 100644
index 0000000000..1caef3d366
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd
@@ -0,0 +1,5 @@
+func test():
+ print(E1.V)
+
+enum E1 {V = E2.V}
+enum E2 {V = E1.V}
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out
new file mode 100644
index 0000000000..1b6569ba3a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "E1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd
new file mode 100644
index 0000000000..237758f340
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd
@@ -0,0 +1,5 @@
+func test():
+ print(EV1)
+
+enum {EV1 = EV2}
+enum {EV2 = EV1}
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out
new file mode 100644
index 0000000000..233f5fee25
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "EV1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd
new file mode 100644
index 0000000000..52e0d60389
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd
@@ -0,0 +1,6 @@
+func test():
+ print(v)
+
+var v = A.v
+
+const A = preload("cyclic_ref_external_a.notest.gd")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out
new file mode 100644
index 0000000000..64a6bd417d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd
new file mode 100644
index 0000000000..9ef1769250
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd
@@ -0,0 +1,3 @@
+const B = preload("cyclic_ref_external.gd")
+
+var v = B.v
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd
new file mode 100644
index 0000000000..b610464c44
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd
@@ -0,0 +1,9 @@
+func test():
+ print(f1())
+ print(f2())
+
+static func f1(p := f2()) -> int:
+ return 1
+
+static func f2(p := f1()) -> int:
+ return 2
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out
new file mode 100644
index 0000000000..d3ec4b0692
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "f1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd
new file mode 100644
index 0000000000..f750715838
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd
@@ -0,0 +1,12 @@
+func test():
+ print(v)
+
+var v := InnerA.new().f()
+
+class InnerA:
+ func f(p := InnerB.new().f()) -> int:
+ return 1
+
+class InnerB extends InnerA:
+ func f(p := 1) -> int:
+ return super.f()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out
new file mode 100644
index 0000000000..6bca25b330
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "f": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd
new file mode 100644
index 0000000000..6913888724
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd
@@ -0,0 +1,5 @@
+func test():
+ print(v1)
+
+var v1 := v2
+var v2 := v1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out
new file mode 100644
index 0000000000..c337882d9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.gd
new file mode 100644
index 0000000000..4dd2b556ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/62957
+
+func test():
+ var dict = {
+ &"key": "StringName",
+ "key": "String"
+ }
+
+ print("Invalid dictionary: %s" % dict)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.out
new file mode 100644
index 0000000000..189d8a7955
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_string_stringname_equivalent.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Key "key" was already used in this dictionary (at line 5).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd
new file mode 100644
index 0000000000..2940c03515
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.gd
@@ -0,0 +1,4 @@
+enum Enum {V1, V2}
+
+func test():
+ Enum.clear()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out
new file mode 100644
index 0000000000..9ca86eca9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call non-const Dictionary function "clear()" on enum "Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd
new file mode 100644
index 0000000000..a66e2714d9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.gd
@@ -0,0 +1,4 @@
+enum Enum {V1, V2}
+
+func test():
+ var bad = Enum.V3
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
new file mode 100644
index 0000000000..ddbdc17a42
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_bad_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "V3" in base "enum_bad_value.gd::Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
index fde7e92f8c..02c4633586 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
+Value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
index b1710c798d..441cccbf7b 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
+Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd
new file mode 100644
index 0000000000..2c7dfafd06
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.gd
@@ -0,0 +1,5 @@
+enum Enum {V1, V2}
+
+func test():
+ var Enum2 = Enum
+ Enum2.clear()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out
new file mode 100644
index 0000000000..9ca86eca9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_duplicate_bad_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot call non-const Dictionary function "clear()" on enum "Enum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd
new file mode 100644
index 0000000000..62ac1c3108
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func enum_func(e : MyEnum) -> void:
+ print(e)
+
+func test():
+ enum_func(MyOtherEnum.OTHER_ENUM_VALUE_1)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
new file mode 100644
index 0000000000..e85f7d6f9f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid argument for "enum_func()" function: argument 1 should be "enum_function_parameter_wrong_type.gd::MyEnum" but is "enum_function_parameter_wrong_type.gd::MyOtherEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd
new file mode 100644
index 0000000000..18b3ffb0fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func enum_func() -> MyEnum:
+ return MyOtherEnum.OTHER_ENUM_VALUE_1
+
+func test():
+ print(enum_func())
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
new file mode 100644
index 0000000000..f7ea3267fa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot return value of type "enum_function_return_wrong_type.gd::MyOtherEnum" because the function return type is "enum_function_return_wrong_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..2b006f1f69
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.gd
@@ -0,0 +1,10 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+class InnerClass:
+ enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = InnerClass.MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
new file mode 100644
index 0000000000..38df5a0cd8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" cannot be assigned to a variable of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
index fde7e92f8c..2adcbd9edf 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
+Value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
index b1710c798d..331113dd30 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
+Cannot assign a value of type enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "local_var" with specified type enum_local_var_init_with_wrong_enum_type.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd
new file mode 100644
index 0000000000..744c2e47ce
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.gd
@@ -0,0 +1,2 @@
+func test():
+ var _bad = TileSet.TileShape.THIS_DOES_NOT_EXIST
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
new file mode 100644
index 0000000000..49f041a2dd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_native_bad_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "THIS_DOES_NOT_EXIST" in base "TileSet::TileShape".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
new file mode 100644
index 0000000000..81d5d59ae8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.gd
@@ -0,0 +1,7 @@
+enum MyEnum { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+func test():
+ const P = preload("../features/enum_value_from_parent.gd")
+ var local_var: MyEnum
+ local_var = P.VALUE_B
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
new file mode 100644
index 0000000000..6298c026b4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_value_from_parent.gd::<anonymous enum>" cannot be assigned to a variable of type "enum_preload_unnamed_assign_to_named.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd
new file mode 100644
index 0000000000..96904c297a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.gd
@@ -0,0 +1,8 @@
+class A:
+ enum { V }
+
+class B extends A:
+ enum { V }
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out
new file mode 100644
index 0000000000..7961a1a481
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_shadows_base_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "V" already exists in parent class A.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd
new file mode 100644
index 0000000000..7e749db6b5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.gd
@@ -0,0 +1,7 @@
+enum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = ENUM_VALUE_1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
new file mode 100644
index 0000000000..b70121ed81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type enum_unnamed_assign_to_named.gd::<anonymous enum> to variable "local_var" with specified type enum_unnamed_assign_to_named.gd::MyEnum.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
new file mode 100644
index 0000000000..cf56a0a933
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
@@ -0,0 +1,6 @@
+const constant_float = 1.0
+
+func test():
+ for x in constant_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
new file mode 100644
index 0000000000..5ee8ac19e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
@@ -0,0 +1,6 @@
+const constant_int = 1
+
+func test():
+ for x in constant_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
new file mode 100644
index 0000000000..b3db4f3b49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
@@ -0,0 +1,6 @@
+enum { enum_value = 1 }
+
+func test():
+ for x in enum_value:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
new file mode 100644
index 0000000000..87c54f7402
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_float := 1.0
+
+ for x in hard_float:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
new file mode 100644
index 0000000000..e309831b3e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
new file mode 100644
index 0000000000..2a43f5a930
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_int := 1
+
+ for x in hard_int:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
new file mode 100644
index 0000000000..c3920d35b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
@@ -0,0 +1,14 @@
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_iterator := Iterator.new()
+
+ for x in hard_iterator:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
new file mode 100644
index 0000000000..a48591a3b4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "StringName" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
new file mode 100644
index 0000000000..b36d87aabe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
@@ -0,0 +1,6 @@
+func test():
+ var hard_string := 'a'
+
+ for x in hard_string:
+ if x is int:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
new file mode 100644
index 0000000000..92c5ebc599
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "String" so it can't be of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
new file mode 100644
index 0000000000..060a8bedf9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
@@ -0,0 +1,3 @@
+func test():
+ for x in true:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
new file mode 100644
index 0000000000..94cb038885
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Unable to iterate on value of type "bool".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
new file mode 100644
index 0000000000..6cfc822482
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
@@ -0,0 +1,4 @@
+func test():
+ for x in 1:
+ if x is String:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
new file mode 100644
index 0000000000..54c190cf8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
index 665c229339..c70a1df10d 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int = default)".
+The function signature doesn't match the parent. Parent signature is "my_function(int = default) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
index 5b22739a93..61004ff627 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function()".
+The function signature doesn't match the parent. Parent signature is "my_function() -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
new file mode 100644
index 0000000000..664e364493
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.gd
@@ -0,0 +1,3 @@
+func test():
+ var foo: bool = true
+ foo += 'bar'
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
new file mode 100644
index 0000000000..358b096a64
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/incompatible_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands "bool" and "String" for assignment operator.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.gd b/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.gd
new file mode 100644
index 0000000000..70973c33d4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.gd
@@ -0,0 +1,4 @@
+func test():
+ var lambda := func() -> int:
+ print('no return')
+ lambda.call()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.out
new file mode 100644
index 0000000000..fe1472c54d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_no_return.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Not all code paths return a value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.gd b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.gd
new file mode 100644
index 0000000000..3c247a5b02
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.gd
@@ -0,0 +1,4 @@
+func test():
+ var lambda := func() -> int:
+ return 'string'
+ print(lambda.call())
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out
new file mode 100644
index 0000000000..53e2b012e6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot return value of type "String" because the function return type is "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd
new file mode 100644
index 0000000000..e1bed94406
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.gd
@@ -0,0 +1,2 @@
+func test():
+ TileSet.this_does_not_exist # Does not exist
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out
new file mode 100644
index 0000000000..06180c3a55
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/native_type_errors.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "this_does_not_exist" in base "TileSet".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
new file mode 100644
index 0000000000..1cf3870a8e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ print(type.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
new file mode 100644
index 0000000000..73a54d7820
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
new file mode 100644
index 0000000000..c1074df915
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var type: = Outer.Inner
+ var type_v: Variant = type
+ print(type_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
new file mode 100644
index 0000000000..92e7b9316e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'GDScript').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
new file mode 100644
index 0000000000..2631c3c500
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.gd
@@ -0,0 +1,8 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ print(instance.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
new file mode 100644
index 0000000000..892f8e2c3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants.gd
+>> 8
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
new file mode 100644
index 0000000000..cba788381e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.gd
@@ -0,0 +1,9 @@
+class Outer:
+ const OUTER_CONST: = 0
+ class Inner:
+ pass
+
+func test() -> void:
+ var instance: = Outer.Inner.new()
+ var instance_v: Variant = instance
+ print(instance_v.OUTER_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
new file mode 100644
index 0000000000..8257e74f57
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_instance_constants_as_variant.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> analyzer/errors/outer_class_instance_constants_as_variant.gd
+>> 9
+>> Invalid get index 'OUTER_CONST' (on base: 'RefCounted (Inner)').
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
new file mode 100644
index 0000000000..200c352223
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.gd
@@ -0,0 +1,12 @@
+class A:
+ class B:
+ func test():
+ print(A.B.D)
+
+class C:
+ class D:
+ pass
+
+func test():
+ var inst = A.B.new()
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out
new file mode 100644
index 0000000000..6baed366f6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/outer_class_lookup.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot find member "D" in base "B".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.gd b/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.gd
new file mode 100644
index 0000000000..5c8b9fa4ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.gd
@@ -0,0 +1,6 @@
+extends Node
+
+var script: int
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.out b/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.out
new file mode 100644
index 0000000000..8454aaa404
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/overload_script_variable.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Member "script" redefined (original in native class 'Node')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd
new file mode 100644
index 0000000000..4e75ded96a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.gd
@@ -0,0 +1,6 @@
+enum LocalNamed { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+func test():
+ const P = preload("../features/enum_from_outer.gd")
+ var x : LocalNamed
+ x = P.Named.VALUE_A
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
new file mode 100644
index 0000000000..5e3c446bf6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "enum_from_outer.gd::Named" cannot be assigned to a variable of type "preload_enum_error.gd::LocalNamed".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
index bbadf1ce27..bf776029b9 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type "String" to a target of type "int".
+Value of type "String" cannot be assigned to a variable of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
new file mode 100644
index 0000000000..393b66c9f0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.gd
@@ -0,0 +1,2 @@
+func test() -> void:
+ return null
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out
new file mode 100644
index 0000000000..3c09f44ba9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_null_in_void_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+A void function cannot return a value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
new file mode 100644
index 0000000000..6be2730bab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.gd
@@ -0,0 +1,4 @@
+func test() -> void:
+ var a
+ a = 1
+ return a
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out
new file mode 100644
index 0000000000..3c09f44ba9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/return_variant_in_void_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+A void function cannot return a value.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
index 9eb2a42ccd..2857cd53c8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "int" cannot be assigned to a variable of type "String".
+Cannot assign a value of type int to variable "x" with specified type String.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd
new file mode 100644
index 0000000000..a3450966cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.gd
@@ -0,0 +1,3 @@
+func test():
+ var builtin := []
+ print(builtin.reverse()) # Built-in type method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out
new file mode 100644
index 0000000000..225c85e9c7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_builtin_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "reverse()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd
new file mode 100644
index 0000000000..2162a181ac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.gd
@@ -0,0 +1,5 @@
+func foo() -> void:
+ pass
+
+func test():
+ print(foo()) # Custom method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out
new file mode 100644
index 0000000000..2b1a607883
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_custom_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "foo()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd
new file mode 100644
index 0000000000..f3443d985e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.gd
@@ -0,0 +1,2 @@
+func test():
+ print(print_debug()) # GDScript utility function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out
new file mode 100644
index 0000000000..502c18ab9d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_gd_utility.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "print_debug()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd
new file mode 100644
index 0000000000..b8e81b160a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.gd
@@ -0,0 +1,3 @@
+func test():
+ var obj := Node.new()
+ print(obj.free()) # Native type method.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out
new file mode 100644
index 0000000000..88be39345b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_native_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "free()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd
new file mode 100644
index 0000000000..8eabed4271
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.gd
@@ -0,0 +1,2 @@
+func test():
+ print(print()) # Built-in utility function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out
new file mode 100644
index 0000000000..ebf43186be
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/use_value_of_void_function_utility.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot get return value of call to "print()" because it returns "void".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.gd
new file mode 100644
index 0000000000..28561ff94b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.gd
@@ -0,0 +1,9 @@
+func test():
+ pass
+
+class A:
+ func overload_me():
+ pass
+
+class B extends A:
+ var overload_me
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.out b/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.out
new file mode 100644
index 0000000000..32357f9f6a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_overloads_superclass_function.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "overload_me" already exists in parent class A.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.gd
new file mode 100644
index 0000000000..eb0003eed8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.gd
@@ -0,0 +1,15 @@
+
+var m_string_array: Array[String] = [&"abc"]
+var m_stringname_array: Array[StringName] = ["abc"]
+
+func test():
+ print(m_string_array)
+ print(m_stringname_array)
+
+ # Converted to String when initialized
+ var string_array: Array[String] = [&"abc"]
+ print(string_array)
+
+ # Converted to StringName when initialized
+ var stringname_array: Array[StringName] = ["abc"]
+ print(stringname_array)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.out
new file mode 100644
index 0000000000..09c199bde1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/array_string_stringname_equivalent.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+["abc"]
+[&"abc"]
+["abc"]
+[&"abc"]
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
new file mode 100644
index 0000000000..2d2c2bef19
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.gd
@@ -0,0 +1,19 @@
+func test():
+ var one_0 = 0
+ one_0 = 1
+ var one_1 := one_0
+ print(one_1)
+
+ var two: Variant = 0
+ two += 2
+ print(two)
+
+ var three_0: Variant = 1
+ var three_1: int = 2
+ three_0 += three_1
+ print(three_0)
+
+ var four_0: int = 3
+ var four_1: Variant = 1
+ four_0 += four_1
+ print(four_0)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assignments_with_untyped.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
new file mode 100644
index 0000000000..a94487d989
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
@@ -0,0 +1,13 @@
+const A: = preload("base_outer_resolution_a.notest.gd")
+const B: = preload("base_outer_resolution_b.notest.gd")
+const C: = preload("base_outer_resolution_c.notest.gd")
+
+const Extend: = preload("base_outer_resolution_extend.notest.gd")
+
+func test() -> void:
+ Extend.test_a(A.new())
+ Extend.test_b(B.new())
+ Extend.InnerClass.test_c(C.new())
+ Extend.InnerClass.InnerInnerClass.test_a_b_c(A.new(), B.new(), C.new())
+ Extend.InnerClass.InnerInnerClass.test_enum(C.TestEnum.HELLO_WORLD)
+ Extend.InnerClass.InnerInnerClass.test_a_prime(A.APrime.new())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.out b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.out
new file mode 100644
index 0000000000..bd27bd31f6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_a.notest.gd
new file mode 100644
index 0000000000..966c8bfc8f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_a.notest.gd
@@ -0,0 +1,2 @@
+class APrime:
+ pass
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_b.notest.gd
index e69de29bb2..e69de29bb2 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_b.notest.gd
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_base.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_base.notest.gd
new file mode 100644
index 0000000000..666b147ced
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_base.notest.gd
@@ -0,0 +1,4 @@
+const A: = preload("base_outer_resolution_a.notest.gd")
+
+class InnerClassInBase:
+ const C: = preload("base_outer_resolution_c.notest.gd")
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_c.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_c.notest.gd
new file mode 100644
index 0000000000..814be35314
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_c.notest.gd
@@ -0,0 +1,3 @@
+enum TestEnum {
+ HELLO_WORLD
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_extend.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_extend.notest.gd
new file mode 100644
index 0000000000..fbd28779d4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution_extend.notest.gd
@@ -0,0 +1,23 @@
+extends "base_outer_resolution_base.notest.gd"
+
+const B: = preload("base_outer_resolution_b.notest.gd")
+
+static func test_a(a: A) -> void:
+ print(a is A)
+
+static func test_b(b: B) -> void:
+ print(b is B)
+
+class InnerClass extends InnerClassInBase:
+ static func test_c(c: C) -> void:
+ print(c is C)
+
+ class InnerInnerClass:
+ static func test_a_b_c(a: A, b: B, c: C) -> void:
+ print(a is A and b is B and c is C)
+
+ static func test_enum(test_enum: C.TestEnum) -> void:
+ print(test_enum == C.TestEnum.HELLO_WORLD)
+
+ static func test_a_prime(a_prime: A.APrime) -> void:
+ print(a_prime is A.APrime)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd
new file mode 100644
index 0000000000..ba1b198cbf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.gd
@@ -0,0 +1,5 @@
+# https://github.com/godotengine/godot/issues/69504#issuecomment-1345725988
+
+func test():
+ print("cast to Variant == null: ", 1 as Variant == null)
+ print("cast to Object == null: ", self as Object == null)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out
new file mode 100644
index 0000000000..541de99b8e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_non_null.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+cast to Variant == null: false
+cast to Object == null: false
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
index 30e7deb05a..7c846c59bd 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
@@ -1,19 +1,19 @@
class A:
- var x = 3
+ var x = 3
class B:
- var x = 4
+ var x = 4
class C:
- var x = 5
+ var x = 5
class Test:
- var a = A.new()
- var b: B = B.new()
- var c := C.new()
+ var a = A.new()
+ var b: B = B.new()
+ var c := C.new()
func test():
- var test_instance := Test.new()
- prints(test_instance.a.x)
- prints(test_instance.b.x)
- prints(test_instance.c.x)
+ var test_instance := Test.new()
+ prints(test_instance.a.x)
+ prints(test_instance.b.x)
+ prints(test_instance.c.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd
new file mode 100644
index 0000000000..d0d04897e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd
@@ -0,0 +1,6 @@
+func check(arg: float = 3):
+ return typeof(arg) == typeof(3.0)
+
+func test():
+ if check():
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd
new file mode 100644
index 0000000000..9bc08f2dc5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.gd
@@ -0,0 +1,29 @@
+class_name EnumAccessOuterClass
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ static func print_enums():
+ print("Inner - Inner")
+ print(MyEnum.V0, MyEnum.V1, MyEnum.V2)
+ print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2)
+ print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2)
+
+ print("Inner - Outer")
+ print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2)
+
+
+enum MyEnum { V0, V1, V2 }
+
+func print_enums():
+ print("Outer - Outer")
+ print(MyEnum.V0, MyEnum.V1, MyEnum.V2)
+ print(EnumAccessOuterClass.MyEnum.V0, EnumAccessOuterClass.MyEnum.V1, EnumAccessOuterClass.MyEnum.V2)
+
+ print("Outer - Inner")
+ print(InnerClass.MyEnum.V0, InnerClass.MyEnum.V1, InnerClass.MyEnum.V2)
+ print(EnumAccessOuterClass.InnerClass.MyEnum.V0, EnumAccessOuterClass.InnerClass.MyEnum.V1, EnumAccessOuterClass.InnerClass.MyEnum.V2)
+
+func test():
+ print_enums()
+ InnerClass.print_enums()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out
new file mode 100644
index 0000000000..02e2e2b396
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_access_types.out
@@ -0,0 +1,13 @@
+GDTEST_OK
+Outer - Outer
+012
+012
+Outer - Inner
+021
+021
+Inner - Inner
+021
+021
+021
+Inner - Outer
+012
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd
new file mode 100644
index 0000000000..2ce588373b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.gd
@@ -0,0 +1,29 @@
+class Outer:
+ enum OuterEnum { OuterValue = 3 }
+ const OuterConst := OuterEnum
+
+ class Inner:
+ enum InnerEnum { InnerValue = 7 }
+ const InnerConst := InnerEnum
+
+ static func test() -> void:
+ print(OuterEnum.size());
+ print(OuterEnum.OuterValue);
+ print(OuterConst.size());
+ print(OuterConst.OuterValue);
+ print(Outer.OuterEnum.size());
+ print(Outer.OuterEnum.OuterValue);
+ print(Outer.OuterConst.size());
+ print(Outer.OuterConst.OuterValue);
+
+ print(InnerEnum.size());
+ print(InnerEnum.InnerValue);
+ print(InnerConst.size());
+ print(InnerConst.InnerValue);
+ print(Inner.InnerEnum.size());
+ print(Inner.InnerEnum.InnerValue);
+ print(Inner.InnerConst.size());
+ print(Inner.InnerConst.InnerValue);
+
+func test():
+ Outer.Inner.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out
new file mode 100644
index 0000000000..e049f85b6e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_as_const.out
@@ -0,0 +1,17 @@
+GDTEST_OK
+1
+3
+1
+3
+1
+3
+1
+3
+1
+7
+1
+7
+1
+7
+1
+7
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd
new file mode 100644
index 0000000000..3076e7069f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.gd
@@ -0,0 +1,13 @@
+enum Enum {V1, V2}
+
+func test():
+ var enumAsDict : Dictionary = Enum.duplicate()
+ var enumAsVariant = Enum.duplicate()
+ print(Enum.has("V1"))
+ print(enumAsDict.has("V1"))
+ print(enumAsVariant.has("V1"))
+ enumAsDict.clear()
+ enumAsVariant.clear()
+ print(Enum.has("V1"))
+ print(enumAsDict.has("V1"))
+ print(enumAsVariant.has("V1"))
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out
new file mode 100644
index 0000000000..a41924d0c9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_duplicate_into_dict.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+true
+true
+true
+false
+false
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd
new file mode 100644
index 0000000000..b3f9941903
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.gd
@@ -0,0 +1,13 @@
+class A:
+ enum Named { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+class B extends A:
+ var a = Named.VALUE_A
+ var b = Named.VALUE_B
+ var c = Named.VALUE_C
+
+func test():
+ var test_instance = B.new()
+ prints("a", test_instance.a, test_instance.a == A.Named.VALUE_A)
+ prints("b", test_instance.b, test_instance.b == A.Named.VALUE_B)
+ prints("c", test_instance.c, test_instance.c == B.Named.VALUE_C)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out
index c160839da3..c160839da3 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_base.out
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd
index 5f57c5b8c2..4d6852a9be 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.gd
@@ -1,5 +1,3 @@
-extends Node
-
enum Named { VALUE_A, VALUE_B, VALUE_C = 42 }
class Test:
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out
new file mode 100644
index 0000000000..c160839da3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_outer.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+a 0 true
+b 1 true
+c 42 true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd
new file mode 100644
index 0000000000..8a4e89d0d6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.gd
@@ -0,0 +1,112 @@
+class_name EnumFunctionTypecheckOuterClass
+
+enum MyEnum { V0, V1, V2 }
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ func inner_inner_no_class(e : MyEnum) -> MyEnum:
+ print(e)
+ return e
+
+ func inner_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum:
+ print(e)
+ return e
+
+ func inner_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum:
+ print(e)
+ return e
+
+ func inner_outer(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum:
+ print(e)
+ return e
+
+ func test():
+ var _d
+ print("Inner")
+
+ var o := EnumFunctionTypecheckOuterClass.new()
+
+ _d = o.outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = o.outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = o.outer_inner_class(MyEnum.V1)
+ _d = o.outer_inner_class(InnerClass.MyEnum.V1)
+ _d = o.outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = o.outer_inner_class_class(MyEnum.V1)
+ _d = o.outer_inner_class_class(InnerClass.MyEnum.V1)
+ _d = o.outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ print()
+
+
+ _d = inner_inner_no_class(MyEnum.V1)
+ _d = inner_inner_no_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_inner_class(MyEnum.V1)
+ _d = inner_inner_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_inner_class_class(MyEnum.V1)
+ _d = inner_inner_class_class(InnerClass.MyEnum.V1)
+ _d = inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ print()
+
+
+func outer_outer_no_class(e : MyEnum) -> MyEnum:
+ print(e)
+ return e
+
+func outer_outer_class(e : EnumFunctionTypecheckOuterClass.MyEnum) -> EnumFunctionTypecheckOuterClass.MyEnum:
+ print(e)
+ return e
+
+func outer_inner_class(e : InnerClass.MyEnum) -> InnerClass.MyEnum:
+ print(e)
+ return e
+
+func outer_inner_class_class(e : EnumFunctionTypecheckOuterClass.InnerClass.MyEnum) -> EnumFunctionTypecheckOuterClass.InnerClass.MyEnum:
+ print(e)
+ return e
+
+func test():
+ var _d
+ print("Outer")
+
+ _d = outer_outer_no_class(MyEnum.V1)
+ _d = outer_outer_no_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = outer_outer_class(MyEnum.V1)
+ _d = outer_outer_class(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ _d = outer_inner_class(InnerClass.MyEnum.V1)
+ _d = outer_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = outer_inner_class_class(InnerClass.MyEnum.V1)
+ _d = outer_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ print()
+
+ var i := EnumFunctionTypecheckOuterClass.InnerClass.new()
+
+ _d = i.inner_inner_no_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_no_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_inner_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_inner_class_class(InnerClass.MyEnum.V1)
+ _d = i.inner_inner_class_class(EnumFunctionTypecheckOuterClass.InnerClass.MyEnum.V1)
+ print()
+ _d = i.inner_outer(MyEnum.V1)
+ _d = i.inner_outer(EnumFunctionTypecheckOuterClass.MyEnum.V1)
+ print()
+ print()
+
+ i.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out
new file mode 100644
index 0000000000..2e3ce1aa10
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_function_typecheck.out
@@ -0,0 +1,55 @@
+GDTEST_OK
+Outer
+1
+1
+
+1
+1
+
+2
+2
+
+2
+2
+
+
+2
+2
+
+2
+2
+
+2
+2
+
+1
+1
+
+
+Inner
+1
+
+1
+
+2
+2
+2
+
+2
+2
+2
+
+
+2
+2
+2
+
+2
+2
+2
+
+2
+2
+2
+
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd
new file mode 100644
index 0000000000..b97d9bbb6b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.gd
@@ -0,0 +1,16 @@
+const A := 1
+enum { B }
+enum NamedEnum { C }
+
+class Parent:
+ const D := 2
+ enum { E }
+ enum NamedEnum2 { F }
+
+class Child extends Parent:
+ enum TestEnum { A, B, C, D, E, F, Node, Object, Child, Parent}
+
+func test():
+ print(A, B, NamedEnum.C, Parent.D, Parent.E, Parent.NamedEnum2.F)
+ print(Child.TestEnum.A, Child.TestEnum.B, Child.TestEnum.C, Child.TestEnum.D, Child.TestEnum.E, Child.TestEnum.F)
+ print(Child.TestEnum.Node, Child.TestEnum.Object, Child.TestEnum.Child, Child.TestEnum.Parent)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out
new file mode 100644
index 0000000000..864ba2a549
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_named_no_shadow.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+100200
+012345
+6789
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd
new file mode 100644
index 0000000000..6a0a1e1969
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.gd
@@ -0,0 +1,19 @@
+func print_enum(e : TileSet.TileShape) -> TileSet.TileShape:
+ print(e)
+ return e
+
+func test():
+ var v : TileSet.TileShape
+ v = TileSet.TILE_SHAPE_SQUARE
+ v = print_enum(v)
+ v = print_enum(TileSet.TILE_SHAPE_SQUARE)
+ v = TileSet.TileShape.TILE_SHAPE_SQUARE
+ v = print_enum(v)
+ v = print_enum(TileSet.TileShape.TILE_SHAPE_SQUARE)
+
+ v = TileSet.TILE_SHAPE_ISOMETRIC
+ v = print_enum(v)
+ v = print_enum(TileSet.TILE_SHAPE_ISOMETRIC)
+ v = TileSet.TileShape.TILE_SHAPE_ISOMETRIC
+ v = print_enum(v)
+ v = print_enum(TileSet.TileShape.TILE_SHAPE_ISOMETRIC)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out
new file mode 100644
index 0000000000..1126dcc6ec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_native_access_types.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+0
+0
+0
+0
+1
+1
+1
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd
new file mode 100644
index 0000000000..b05ae82048
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.gd
@@ -0,0 +1,86 @@
+class_name EnumTypecheckOuterClass
+
+enum MyEnum { V0, V1, V2 }
+
+class InnerClass:
+ enum MyEnum { V0, V2, V1 }
+
+ static func test_inner_from_inner():
+ print("Inner - Inner")
+ var e1 : MyEnum
+ var e2 : InnerClass.MyEnum
+ var e3 : EnumTypecheckOuterClass.InnerClass.MyEnum
+
+ print("Self ", e1, e2, e3)
+ e1 = MyEnum.V1
+ e2 = MyEnum.V1
+ e3 = MyEnum.V1
+ print("MyEnum ", e1, e2, e3)
+ e1 = InnerClass.MyEnum.V1
+ e2 = InnerClass.MyEnum.V1
+ e3 = InnerClass.MyEnum.V1
+ print("Inner.MyEnum ", e1, e2, e3)
+ e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e3 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ print("Outer.Inner.MyEnum ", e1, e2, e3)
+
+ e1 = e2
+ e1 = e3
+ e2 = e1
+ e2 = e3
+ e3 = e1
+ e3 = e2
+
+ print()
+
+ static func test_outer_from_inner():
+ print("Inner - Outer")
+ var e : EnumTypecheckOuterClass.MyEnum
+
+ e = EnumTypecheckOuterClass.MyEnum.V1
+ print("Outer.MyEnum ", e)
+
+ print()
+
+func test_outer_from_outer():
+ print("Outer - Outer")
+ var e1 : MyEnum
+ var e2 : EnumTypecheckOuterClass.MyEnum
+
+ print("Self ", e1, e2)
+ e1 = MyEnum.V1
+ e2 = MyEnum.V1
+ print("Outer ", e1, e2)
+ e1 = EnumTypecheckOuterClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.MyEnum.V1
+ print("Outer.MyEnum ", e1, e2)
+
+ e1 = e2
+ e2 = e1
+
+ print()
+
+func test_inner_from_outer():
+ print("Outer - Inner")
+ var e1 : InnerClass.MyEnum
+ var e2 : EnumTypecheckOuterClass.InnerClass.MyEnum
+
+ print("Inner ", e1, e2)
+ e1 = InnerClass.MyEnum.V1
+ e2 = InnerClass.MyEnum.V1
+ print("Outer.Inner ", e1, e2)
+ e1 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ e2 = EnumTypecheckOuterClass.InnerClass.MyEnum.V1
+ print("Outer.Inner.MyEnum ", e1, e2)
+
+ e1 = e2
+ e2 = e1
+
+ print()
+
+func test():
+ test_outer_from_outer()
+ test_inner_from_outer()
+ InnerClass.test_outer_from_inner()
+ InnerClass.test_inner_from_inner()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out
new file mode 100644
index 0000000000..3b2dcade26
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_typecheck_inner_class.out
@@ -0,0 +1,19 @@
+GDTEST_OK
+Outer - Outer
+Self 00
+Outer 11
+Outer.MyEnum 11
+
+Outer - Inner
+Inner 00
+Outer.Inner 22
+Outer.Inner.MyEnum 22
+
+Inner - Outer
+Outer.MyEnum 1
+
+Inner - Inner
+Self 000
+MyEnum 222
+Inner.MyEnum 222
+Outer.Inner.MyEnum 222
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd
new file mode 100644
index 0000000000..f351fc1f7b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.gd
@@ -0,0 +1,7 @@
+enum {
+ V1,
+ V2 = V1,
+}
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out
index d73c5eb7cd..d73c5eb7cd 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_unnamed_depend.out
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
new file mode 100644
index 0000000000..95c3268130
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd
@@ -0,0 +1,12 @@
+class A extends CanvasItem:
+ func _init():
+ pass
+
+class B extends A:
+ pass
+
+class C extends CanvasItem:
+ pass
+
+func test():
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
new file mode 100644
index 0000000000..0c740935b9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.gd
@@ -0,0 +1,6 @@
+const External = preload("external_enum_as_constant_external.notest.gd")
+const MyEnum = External.MyEnum
+
+func test():
+ print(MyEnum.WAITING == 0)
+ print(MyEnum.GODOT == 1)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
new file mode 100644
index 0000000000..24c1e41aab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_enum_as_constant_external.notest.gd
@@ -0,0 +1,4 @@
+enum MyEnum {
+ WAITING,
+ GODOT
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd
new file mode 100644
index 0000000000..3f9bfe189c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.gd
@@ -0,0 +1,4 @@
+extends "inner_base.gd".InnerA.InnerAB
+
+func test():
+ super.test()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out
new file mode 100644
index 0000000000..62f1383392
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_base.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+InnerA.InnerAB.test
+InnerB.test
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd
new file mode 100644
index 0000000000..18dca109fb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.gd
@@ -0,0 +1,7 @@
+const External = preload("external_inner_class_as_constant_external.notest.gd")
+const ExternalInnerClass = External.InnerClass
+
+func test():
+ var inst_external: ExternalInnerClass = ExternalInnerClass.new()
+ inst_external.x = 4.0
+ print(inst_external.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out
new file mode 100644
index 0000000000..15666c46ad
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd
new file mode 100644
index 0000000000..788c99d469
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/external_inner_class_as_constant_external.notest.gd
@@ -0,0 +1,2 @@
+class InnerClass:
+ var x: = 3.0
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
new file mode 100644
index 0000000000..7b74be6f2c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
@@ -0,0 +1,15 @@
+func test():
+ var variant_int: Variant = 1
+ var weak_int = 1
+
+ for x in variant_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ for x in weak_int:
+ if x is String:
+ print('never')
+ print(x)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
new file mode 100644
index 0000000000..7677671cfd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+0
+0
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd
index fb0ace6a90..c3fc176679 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.notest.gd
@@ -1,5 +1,4 @@
-func test():
- pass
+const A := 42
func something():
return "OK"
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd
new file mode 100644
index 0000000000..39ced354df
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/61159
+
+func get_param():
+ return null
+
+func test():
+ var v = get_param()
+ v = get_param()
+ print(v)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out
new file mode 100644
index 0000000000..f0c83a69b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inferred_return_type.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+<null>
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd
new file mode 100644
index 0000000000..a825b59255
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_base.gd
@@ -0,0 +1,18 @@
+extends InnerA
+
+func test():
+ super.test()
+
+class InnerA extends InnerAB:
+ func test():
+ print("InnerA.test")
+ super.test()
+
+ class InnerAB extends InnerB:
+ func test():
+ print("InnerA.InnerAB.test")
+ super.test()
+
+class InnerB:
+ func test():
+ print("InnerB.test")
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_base.out b/modules/gdscript/tests/scripts/analyzer/features/inner_base.out
new file mode 100644
index 0000000000..ddd5ffcfd3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_base.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+InnerA.test
+InnerA.InnerAB.test
+InnerB.test
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.gd
new file mode 100644
index 0000000000..114d7f7652
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.gd
@@ -0,0 +1,12 @@
+func test():
+ var lambda_0 := func() -> void:
+ print(0)
+ lambda_0.call()
+
+ var lambda_1 := func(printed: int) -> void:
+ print(printed)
+ lambda_1.call(1)
+
+ var lambda_2 := func(identity: int) -> int:
+ return identity
+ print(lambda_2.call(2))
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.out b/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.out
new file mode 100644
index 0000000000..63e262aeff
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lambda_typed.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+0
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
new file mode 100644
index 0000000000..541da78332
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.gd
@@ -0,0 +1,50 @@
+# Inner-outer class lookup
+class A:
+ const Q: = "right one"
+
+class X:
+ const Q: = "wrong one"
+
+class Y extends X:
+ class B extends A:
+ static func check() -> void:
+ print(Q)
+
+# External class lookup
+const External: = preload("lookup_class_external.notest.gd")
+
+class Internal extends External.A:
+ static func check() -> void:
+ print(TARGET)
+
+ class E extends External.E:
+ static func check() -> void:
+ print(TARGET)
+ print(WAITING)
+
+# Variable lookup
+class C:
+ var Q := 'right one'
+
+class D:
+ const Q := 'wrong one'
+
+class E extends D:
+ class F extends C:
+ func check() -> void:
+ print(Q)
+
+# Test
+func test() -> void:
+ # Inner-outer class lookup
+ Y.B.check()
+ print("---")
+
+ # External class lookup
+ Internal.check()
+ Internal.E.check()
+ print("---")
+
+ # Variable lookup
+ var f: = E.F.new()
+ f.check()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
new file mode 100644
index 0000000000..a0983c1438
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+right one
+---
+wrong
+right
+godot
+---
+right one
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
new file mode 100644
index 0000000000..a2904e20a8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_class_external.notest.gd
@@ -0,0 +1,15 @@
+class A:
+ const TARGET: = "wrong"
+
+ class B:
+ const TARGET: = "wrong"
+ const WAITING: = "godot"
+
+ class D extends C:
+ pass
+
+class C:
+ const TARGET: = "right"
+
+class E extends A.B.D:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
new file mode 100644
index 0000000000..26cf6c7322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.gd
@@ -0,0 +1,41 @@
+signal hello
+
+func get_signal() -> Signal:
+ return hello
+
+class A:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+ class B:
+ signal hello
+
+ func get_signal() -> Signal:
+ return hello
+
+class C extends A.B:
+ func get_signal() -> Signal:
+ return hello
+
+func test():
+ var a: = A.new()
+ var b: = A.B.new()
+ var c: = C.new()
+
+ var hello_a_result: = hello == a.get_signal()
+ var hello_b_result: = hello == b.get_signal()
+ var hello_c_result: = hello == c.get_signal()
+ var a_b_result: = a.get_signal() == b.get_signal()
+ var a_c_result: = a.get_signal() == c.get_signal()
+ var b_c_result: = b.get_signal() == c.get_signal()
+ var c_c_result: = c.get_signal() == c.get_signal()
+
+ print("hello == A.hello? %s" % hello_a_result)
+ print("hello == A.B.hello? %s" % hello_b_result)
+ print("hello == C.hello? %s" % hello_c_result)
+ print("A.hello == A.B.hello? %s" % a_b_result)
+ print("A.hello == C.hello? %s" % a_c_result)
+ print("A.B.hello == C.hello? %s" % b_c_result)
+ print("C.hello == C.hello? %s" % c_c_result)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
new file mode 100644
index 0000000000..6b0d32eaf8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/lookup_signal.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+hello == A.hello? false
+hello == A.B.hello? false
+hello == C.hello? false
+A.hello == A.B.hello? false
+A.hello == C.hello? false
+A.B.hello == C.hello? false
+C.hello == C.hello? true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd
new file mode 100644
index 0000000000..5a413e2015
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd
@@ -0,0 +1,32 @@
+func check(input: int) -> bool:
+ return input == 1
+
+var recur = null
+var prop = null
+
+func check_arg(arg = null) -> void:
+ if arg != null:
+ print(check(arg))
+
+func check_recur() -> void:
+ if recur != null:
+ print(check(recur))
+ else:
+ recur = 1
+ check_recur()
+
+func test() -> void:
+ check_arg(1)
+
+ check_recur()
+
+ if prop == null:
+ set('prop', 1)
+ print(check(prop))
+ set('prop', null)
+
+ var loop = null
+ while loop != 2:
+ if loop != null:
+ print(check(loop))
+ loop = 1 if loop == null else 2
diff --git a/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out
new file mode 100644
index 0000000000..f9783e4362
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/null_initializer.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd
new file mode 100644
index 0000000000..11349cc916
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd
@@ -0,0 +1,51 @@
+func test():
+ print("v1: ", v1)
+ print("v1 is String: ", v1 is String)
+ print("v2: ", v2)
+ print("v2 is bool: ", v2 is bool)
+ print("c1: ", c1)
+ print("c1 is int: ", c1 is int)
+ print("c2: ", c2)
+ print("c2 is int: ", c2 is int)
+ print("E1.V1: ", E1.V1)
+ print("E1.V2: ", E1.V2)
+ print("E2.V: ", E2.V)
+ print("EV1: ", EV1)
+ print("EV2: ", EV2)
+ print("EV3: ", EV3)
+
+var v1 := InnerA.new().fn()
+
+class InnerA extends InnerAB:
+ func fn(p2 := E1.V2) -> String:
+ return "%s, p2=%s" % [super.fn(), p2]
+
+ class InnerAB:
+ func fn(p1 := c1) -> String:
+ return "p1=%s" % p1
+
+var v2 := f()
+
+func f() -> bool:
+ return true
+
+const c1 := E1.V1
+
+enum E1 {
+ V1 = E2.V + 2,
+ V2 = V1 - 1
+}
+
+enum E2 {V = 2}
+
+const c2 := EV2
+
+enum {
+ EV1 = 42,
+ UNUSED = EV3,
+ EV2
+}
+
+enum {
+ EV3 = EV1 + 1
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out
new file mode 100644
index 0000000000..b1e75d611d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+v1: p1=4, p2=3
+v1 is String: true
+v2: true
+v2 is bool: true
+c1: 4
+c1 is int: true
+c2: 44
+c2 is int: true
+E1.V1: 4
+E1.V2: 3
+E2.V: 2
+EV1: 42
+EV2: 44
+EV3: 43
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd
new file mode 100644
index 0000000000..0b162bdff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd
@@ -0,0 +1,39 @@
+const B = preload("out_of_order_external_a.notest.gd")
+
+func test():
+ print("v1: ", v1)
+ print("v1 is String: ", v1 is String)
+ print("v2: ", v2)
+ print("v2 is bool: ", v2 is bool)
+ print("c1: ", c1)
+ print("c1 is int: ", c1 is int)
+ print("c2: ", c2)
+ print("c2 is int: ", c2 is int)
+ print("E1.V1: ", E1.V1)
+ print("E1.V2: ", E1.V2)
+ print("B.E2.V: ", B.E2.V)
+ print("EV1: ", EV1)
+ print("EV2: ", EV2)
+ print("B.EV3: ", B.EV3)
+
+var v1 := Inner.new().fn()
+
+class Inner extends B.Inner:
+ func fn(p2 := E1.V2) -> String:
+ return "%s, p2=%s" % [super.fn(), p2]
+
+var v2 := B.new().f()
+
+const c1 := E1.V1
+
+enum E1 {
+ V1 = B.E2.V + 2,
+ V2 = V1 - 1
+}
+
+const c2 := EV2
+
+enum {
+ EV1 = 42,
+ EV2 = B.EV3 + 1
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out
new file mode 100644
index 0000000000..437f782fe6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+v1: p1=4, p2=3
+v1 is String: true
+v2: true
+v2 is bool: true
+c1: 4
+c1 is int: true
+c2: 44
+c2 is int: true
+E1.V1: 4
+E1.V2: 3
+B.E2.V: 2
+EV1: 42
+EV2: 44
+B.EV3: 43
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd
new file mode 100644
index 0000000000..d276f72fcf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd
@@ -0,0 +1,12 @@
+const A = preload("out_of_order_external.gd")
+
+class Inner:
+ func fn(p1 := A.c1) -> String:
+ return "p1=%s" % p1
+
+func f(p := A.c1) -> bool:
+ return p is int
+
+enum E2 {V = 2}
+
+enum {EV3 = A.EV1 + 1}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd
new file mode 100644
index 0000000000..9d0324ead8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd
@@ -0,0 +1,6 @@
+const Constants = preload("gdscript_to_preload.notest.gd")
+
+func test():
+ var a := Constants.A
+ print(a)
+
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out
new file mode 100644
index 0000000000..0982f3718c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+42
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd
new file mode 100644
index 0000000000..b730453a8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.gd
@@ -0,0 +1,4 @@
+const A = preload("preload_cyclic_reference_a.notest.gd")
+
+func test():
+ A.test_cyclic_reference()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out
new file mode 100644
index 0000000000..14bb971221
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+godot
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd
new file mode 100644
index 0000000000..7a6035ded1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_a.notest.gd
@@ -0,0 +1,12 @@
+const B = preload("preload_cyclic_reference_b.notest.gd")
+
+const WAITING_FOR = "godot"
+
+static func test_cyclic_reference():
+ B.test_cyclic_reference()
+
+static func test_cyclic_reference_2():
+ B.test_cyclic_reference_2()
+
+static func test_cyclic_reference_3():
+ B.test_cyclic_reference_3()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd
new file mode 100644
index 0000000000..3ea5b01156
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_cyclic_reference_b.notest.gd
@@ -0,0 +1,10 @@
+const A = preload("preload_cyclic_reference_a.notest.gd")
+
+static func test_cyclic_reference():
+ A.test_cyclic_reference_2()
+
+static func test_cyclic_reference_2():
+ A.test_cyclic_reference_3()
+
+static func test_cyclic_reference_3():
+ print(A.WAITING_FOR)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.gd
new file mode 100644
index 0000000000..25381035b2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.gd
@@ -0,0 +1,15 @@
+const Preloaded := preload( 'preload_script_native_type.notest.gd' )
+
+func test() -> void:
+ var inferred := Preloaded.new()
+ var inferred_owner := inferred.owner
+
+ var typed: Preloaded
+ typed = Preloaded.new()
+ var typed_owner := typed.owner
+
+ print(typed_owner == inferred_owner)
+
+ inferred.free()
+ typed.free()
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.notest.gd
new file mode 100644
index 0000000000..61510e14cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.notest.gd
@@ -0,0 +1 @@
+extends Node
diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.out b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.out
new file mode 100644
index 0000000000..3e24a1e2af
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/preload_script_native_type.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
index 5482592e90..63e59398ae 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
@@ -1,5 +1,5 @@
GDTEST_OK
-null
+<null>
0
1
2
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
new file mode 100644
index 0000000000..95f04421d1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.gd
@@ -0,0 +1,5 @@
+func variant() -> Variant:
+ return 'variant'
+
+func test():
+ print(variant())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out
new file mode 100644
index 0000000000..57fe608cc5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_variant_typed.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+variant
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.gd
new file mode 100644
index 0000000000..fc343377fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.gd
@@ -0,0 +1,17 @@
+func print_untyped(array = [0]) -> void:
+ print(array)
+ print(array.get_typed_builtin())
+
+func print_inferred(array := [1]) -> void:
+ print(array)
+ print(array.get_typed_builtin())
+
+func print_typed(array: Array[int] = [2]) -> void:
+ print(array)
+ print(array.get_typed_builtin())
+
+func test():
+ print_untyped()
+ print_inferred()
+ print_typed()
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out
new file mode 100644
index 0000000000..082e3ade19
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+[0]
+0
+[1]
+2
+[2]
+2
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
index 5f73064cc0..beabf3d2e5 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
+++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
@@ -1,4 +1,4 @@
-const preloaded : GDScript = preload("gdscript_to_preload.gd")
+const preloaded : GDScript = preload("gdscript_to_preload.notest.gd")
func test():
var preloaded_instance: preloaded = preloaded.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
new file mode 100644
index 0000000000..da24c06b2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.gd
@@ -0,0 +1,6 @@
+class Check extends Node:
+ func _set(_property: StringName, _value: Variant) -> bool:
+ return true
+
+func test() -> void:
+ print('OK')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/variant_arg_in_virtual_method.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
diff --git a/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd
new file mode 100644
index 0000000000..c5f3ccc59e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd
@@ -0,0 +1,5 @@
+func test():
+ var bar = 1
+ var foo: float = bar
+ print(typeof(foo))
+ print(foo is float)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out
new file mode 100644
index 0000000000..5d798c1f24
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+3
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
index 26b6e13d4f..ad2e6558d7 100644
--- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
+++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int].
+Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int].
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.gd b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.gd
new file mode 100644
index 0000000000..939496324c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.gd
@@ -0,0 +1,6 @@
+var shadow: int
+
+func test():
+ var lambda := func(shadow: String) -> void:
+ print(shadow)
+ lambda.call('shadow')
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out
new file mode 100644
index 0000000000..a98d80514c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_shadowing_arg.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> SHADOWED_VARIABLE
+>> The local function parameter "shadow" is shadowing an already-declared variable at line 1.
+shadow
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd
new file mode 100644
index 0000000000..6fc90ea29c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd
@@ -0,0 +1,4 @@
+func test():
+ var lambda := func(unused: Variant) -> void:
+ pass
+ lambda.call()
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
new file mode 100644
index 0000000000..b018091c18
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_PARAMETER
+>>
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 ada6030132..0085b3f367 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 (except @tool).
-@icon("res://path/to/optional/icon.svg")
+# Error here. Annotations should be used before `class_name`, not after.
class_name HelloWorld
+@icon("res://path/to/optional/icon.svg")
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 02b33c8692..a598ff8424 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 (except @tool).
+Annotation "@icon" is not allowed in this level.
diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
index 92dfb2366d..816783f239 100644
--- a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
@@ -1,2 +1,2 @@
func test():
- var dictionary = { hello = "world",, }
+ var dictionary = { hello = "world",, }
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
index 4608c778aa..7a745bd995 100644
--- 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
@@ -1,4 +1,4 @@
func test():
- match 1:
- [[[var a]]], 2:
- pass
+ match 1:
+ [[[var a]]], 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
index 43b513045b..a7197bf68f 100644
--- a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
@@ -1,34 +1,34 @@
func foo(x):
- match x:
- 1 + 1:
- print("1+1")
- [1,2,[1,{1:2,2:var z,..}]]:
- print("[1,2,[1,{1:2,2:var z,..}]]")
- print(z)
- 1 if true else 2:
- print("1 if true else 2")
- 1 < 2:
- print("1 < 2")
- 1 or 2 and 1:
- print("1 or 2 and 1")
- 6 | 1:
- print("1 | 1")
- 1 >> 1:
- print("1 >> 1")
- 1, 2 or 3, 4:
- print("1, 2 or 3, 4")
- _:
- print("wildcard")
+ match x:
+ 1 + 1:
+ print("1+1")
+ [1,2,[1,{1:2,2:var z,..}]]:
+ print("[1,2,[1,{1:2,2:var z,..}]]")
+ print(z)
+ 1 if true else 2:
+ print("1 if true else 2")
+ 1 < 2:
+ print("1 < 2")
+ 1 or 2 and 1:
+ print("1 or 2 and 1")
+ 6 | 1:
+ print("1 | 1")
+ 1 >> 1:
+ print("1 >> 1")
+ 1, 2 or 3, 4:
+ print("1, 2 or 3, 4")
+ _:
+ print("wildcard")
func test():
- foo(6 | 1)
- foo(1 >> 1)
- foo(2)
- foo(1)
- foo(1+1)
- foo(1 < 2)
- foo([2, 1])
- foo(4)
- foo([1, 2, [1, {1 : 2, 2:3}]])
- foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
- foo([1, 2, [1, {1 : 2}]])
+ foo(6 | 1)
+ foo(1 >> 1)
+ foo(2)
+ foo(1)
+ foo(1+1)
+ foo(1 < 2)
+ foo([2, 1])
+ foo(4)
+ foo([1, 2, [1, {1 : 2, 2:3}]])
+ foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
+ foo([1, 2, [1, {1 : 2}]])
diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
index 2b46f1e88a..c959c6c6af 100644
--- a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
+++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
@@ -1,27 +1,27 @@
func foo(x):
- match x:
- 1:
- print("1")
- 2:
- print("2")
- [1, 2]:
- print("[1, 2]")
- 3 or 4:
- print("3 or 4")
- 4:
- print("4")
- {1 : 2, 2 : 3}:
- print("{1 : 2, 2 : 3}")
- _:
- print("wildcard")
+ match x:
+ 1:
+ print("1")
+ 2:
+ print("2")
+ [1, 2]:
+ print("[1, 2]")
+ 3 or 4:
+ print("3 or 4")
+ 4:
+ print("4")
+ {1 : 2, 2 : 3}:
+ print("{1 : 2, 2 : 3}")
+ _:
+ print("wildcard")
func test():
- foo(0)
- foo(1)
- foo(2)
- foo([1, 2])
- foo(3)
- foo(4)
- foo([4,4])
- foo({1 : 2, 2 : 3})
- foo({1 : 2, 4 : 3})
+ foo(0)
+ foo(1)
+ foo(2)
+ foo([1, 2])
+ foo(3)
+ foo(4)
+ foo([4,4])
+ foo({1 : 2, 2 : 3})
+ foo({1 : 2, 4 : 3})
diff --git a/modules/gdscript/tests/scripts/parser/features/class.gd b/modules/gdscript/tests/scripts/parser/features/class.gd
index 6652f85ad9..af24b32322 100644
--- a/modules/gdscript/tests/scripts/parser/features/class.gd
+++ b/modules/gdscript/tests/scripts/parser/features/class.gd
@@ -21,5 +21,5 @@ func test():
assert(test_sub.number == 25) # From Test.
assert(test_sub.other_string == "bye") # From TestSub.
- TestConstructor.new()
- TestConstructor.new(500)
+ var _test_constructor = TestConstructor.new()
+ _test_constructor = TestConstructor.new(500)
diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd
index 8bd188e247..19009e433d 100644
--- a/modules/gdscript/tests/scripts/parser/features/class_name.gd
+++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd
@@ -1,5 +1,5 @@
-class_name HelloWorld
@icon("res://path/to/optional/icon.svg")
+class_name HelloWorld
func test():
pass
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out
index 5f999f573a..e1eeb46f78 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"]}
+{ 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 5143d040a9..553d40d953 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 dd28609850..cf79845f53 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/function_many_parameters.out b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out
index 3a979227d4..80df7a3d4c 100644
--- a/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out
+++ b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out
@@ -1,2 +1,2 @@
GDTEST_OK
-123456789101112131415161718192212223242526272829303132333435363738394041424344454647falsetruenull
+123456789101112131415161718192212223242526272829303132333435363738394041424344454647falsetrue<null>
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
index c3b2506156..17d00bce3c 100644
--- a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
@@ -1,4 +1,4 @@
func test():
- var my_lambda = func(x):
- print(x)
- my_lambda.call("hello")
+ var my_lambda = func(x):
+ print(x)
+ my_lambda.call("hello")
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
index 377dd25e9e..75857fb8ff 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -1,43 +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")
+ 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")
+ 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"})
+ 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_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
index dbe223f5f5..a278ea1154 100644
--- 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
@@ -1,26 +1,26 @@
func foo(x):
- match x:
- 1, [2]:
- print('1, [2]')
- _:
- print('wildcard')
+ 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')
+ 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])
+ 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_variable_binds_in_pattern.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_variable_binds_in_pattern.gd
index a0ae7fb17c..0a71f33c25 100644
--- 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
@@ -1,6 +1,6 @@
func test():
- match [1, 2, 3]:
- [var a, var b, var c]:
- print(a == 1)
- print(b == 2)
- print(c == 3)
+ 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/multiline_assert.gd b/modules/gdscript/tests/scripts/parser/features/multiline_assert.gd
new file mode 100644
index 0000000000..8c699c604d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_assert.gd
@@ -0,0 +1,24 @@
+func test():
+ var x := 5
+
+ assert(x > 0)
+ assert(x > 0,)
+ assert(x > 0, 'message')
+ assert(x > 0, 'message',)
+
+ assert(
+ x > 0
+ )
+ assert(
+ x > 0,
+ )
+ assert(
+ x > 0,
+ 'message'
+ )
+ assert(
+ x > 0,
+ 'message',
+ )
+
+ print('OK')
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_assert.out b/modules/gdscript/tests/scripts/parser/features/multiline_assert.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_assert.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
index 8b8c33202f..508f0ff217 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/features/str_preserves_case.out b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out
index abba38e87c..867f45f0ac 100644
--- a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out
+++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out
@@ -1,4 +1,4 @@
GDTEST_OK
-null
+<null>
true
false
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
new file mode 100644
index 0000000000..4cbb464f59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.gd
@@ -0,0 +1,17 @@
+class A:
+ enum { X = 1 }
+
+ class B:
+ enum { X = 2 }
+
+class C:
+ const X = 3
+
+ class D:
+ enum { X = 4 }
+
+func test():
+ print(A.X)
+ print(A.B.X)
+ print(C.X)
+ print(C.D.X)
diff --git a/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
new file mode 100644
index 0000000000..7536c38490
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/unnamed_enums_outer_conflicts.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.gd b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
new file mode 100644
index 0000000000..039d51d9ed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.gd
@@ -0,0 +1,6 @@
+func test():
+ var vec2: = Vector2.INF
+ var vec3: = Vector3.INF
+
+ print(vec2.x == INF)
+ print(vec3.z == INF)
diff --git a/modules/gdscript/tests/scripts/parser/features/vector_inf.out b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/vector_inf.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
index d73c5eb7cd..e89bb9226f 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
@@ -1 +1,5 @@
GDTEST_OK
+>> WARNING
+>> Line: 6
+>> RETURN_VALUE_DISCARDED
+>> The function 'i_return_int()' returns a value that will be discarded if not used.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd
new file mode 100644
index 0000000000..29d8501b78
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd
@@ -0,0 +1,11 @@
+class Player:
+ var x = 3
+
+func test():
+ # These should not emit a warning.
+ var _player = Player.new()
+ print(String.num_uint64(8589934592)) # 2 ^ 33
+
+ # This should emit a warning.
+ var some_string = String()
+ print(some_string.num_uint64(8589934592)) # 2 ^ 33
diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
new file mode 100644
index 0000000000..3933a35178
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 11
+>> STATIC_CALLED_ON_INSTANCE
+>> The function 'num_uint64()' is a static function but was called from an instance. Instead, it should be directly called from the type: 'String.num_uint64()'.
+8589934592
+8589934592
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
deleted file mode 100644
index b4a42b3e3d..0000000000
--- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
+++ /dev/null
@@ -1,6 +0,0 @@
-func i_return_void() -> void:
- return
-
-
-func test():
- var __ = i_return_void()
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
deleted file mode 100644
index 84c9598f9a..0000000000
--- a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
+++ /dev/null
@@ -1,5 +0,0 @@
-GDTEST_OK
->> WARNING
->> Line: 6
->> VOID_ASSIGNMENT
->> Assignment operation, but the function 'i_return_void()' returns void.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..a72ac9b5ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.gd
@@ -0,0 +1,8 @@
+var weakling = 'not float'
+func weak(x: float = weakling):
+ print(x)
+ print('typeof x is', typeof(x))
+
+func test():
+ print(typeof(weak()))
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
new file mode 100644
index 0000000000..8543cf976e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/bad_conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: weak()
+>> runtime/errors/bad_conversion_for_default_parameter.gd
+>> 2
+>> Trying to assign value of type 'String' to a variable of type 'float'.
+0
+not ok
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd
new file mode 100644
index 0000000000..a5ecaba38d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.gd
@@ -0,0 +1,6 @@
+const array: Array = [{}]
+
+func test():
+ var dictionary := array[0]
+ var key: int = 0
+ dictionary[key] = 0
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
new file mode 100644
index 0000000000..2a97eaea44
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_is_deep.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/constant_array_is_deep.gd
+>> 6
+>> Invalid set index '0' (on base: 'Dictionary') with value of type 'int'
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd
new file mode 100644
index 0000000000..3e71cd0518
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.gd
@@ -0,0 +1,4 @@
+const array: Array = [0]
+
+func test():
+ array.push_back(0)
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out
new file mode 100644
index 0000000000..ba3e1c46c6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_array_push_back.out
@@ -0,0 +1,7 @@
+GDTEST_RUNTIME_ERROR
+>> ERROR
+>> on function: push_back()
+>> core/variant/array.cpp
+>> 253
+>> Condition "_p->read_only" is true.
+>> Array is in read-only state.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
new file mode 100644
index 0000000000..7b350e81ad
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.gd
@@ -0,0 +1,4 @@
+const dictionary := {}
+
+func test():
+ dictionary.erase(0)
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out
new file mode 100644
index 0000000000..3e7ca11a4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_erase.out
@@ -0,0 +1,7 @@
+GDTEST_RUNTIME_ERROR
+>> ERROR
+>> on function: erase()
+>> core/variant/dictionary.cpp
+>> 177
+>> Condition "_p->read_only" is true. Returning: false
+>> Dictionary is in read-only state.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd
new file mode 100644
index 0000000000..4763210a7f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.gd
@@ -0,0 +1,6 @@
+const dictionary := {0: [0]}
+
+func test():
+ var array := dictionary[0]
+ var key: int = 0
+ array[key] = 0
diff --git a/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
new file mode 100644
index 0000000000..c807db6b0c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/constant_dictionary_is_deep.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/constant_dictionary_is_deep.gd
+>> 6
+>> Invalid set index '0' (on base: 'Array') with value of type 'int'
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd
new file mode 100644
index 0000000000..a3daf70627
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var obj
+ obj = Node.new()
+ print(obj.free())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out
new file mode 100644
index 0000000000..5edaf19442
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_free_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd
new file mode 100644
index 0000000000..49fb76ad1f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var value
+ value = []
+ print(value.reverse())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out
new file mode 100644
index 0000000000..128356ff8a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_builtin_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd
new file mode 100644
index 0000000000..44f9aa467a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd
@@ -0,0 +1,4 @@
+func test():
+ var obj
+ obj = RefCounted.new()
+ print(obj.notify_property_list_changed())
diff --git a/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out
new file mode 100644
index 0000000000..e02c206778
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_native_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"
diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
new file mode 100644
index 0000000000..bd38259cec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd
@@ -0,0 +1,35 @@
+# https://github.com/godotengine/godot/issues/63965
+
+func test():
+ var array_str: Array = []
+ array_str.push_back("godot")
+ print("StringName in Array: ", &"godot" in array_str)
+
+ var array_sname: Array = []
+ array_sname.push_back(&"godot")
+ print("String in Array: ", "godot" in array_sname)
+
+ # Not equal because the values are different types.
+ print("Arrays not equal: ", array_str != array_sname)
+
+ var string_array: Array[String] = []
+ var stringname_array: Array[StringName] = []
+
+ string_array.push_back(&"abc")
+ print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING)
+
+ stringname_array.push_back("abc")
+ print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME)
+
+ print("StringName in Array[String]: ", &"abc" in string_array)
+ print("String in Array[StringName]: ", "abc" in stringname_array)
+
+ var packed_string_array: PackedStringArray = []
+ assert(!packed_string_array.push_back("abc"))
+ print("StringName in PackedStringArray: ", &"abc" in packed_string_array)
+
+ string_array.push_back("abc")
+ print("StringName finds String in Array: ", string_array.find(&"abc"))
+
+ stringname_array.push_back(&"abc")
+ print("String finds StringName in Array: ", stringname_array.find("abc"))
diff --git a/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.out
new file mode 100644
index 0000000000..98ab78e8f1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+StringName in Array: true
+String in Array: true
+Arrays not equal: true
+Array[String] insert converted: true
+Array[StringName] insert converted: true
+StringName in Array[String]: true
+String in Array[StringName]: true
+StringName in PackedStringArray: true
+StringName finds String in Array: 0
+String finds StringName in Array: 0
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
new file mode 100644
index 0000000000..46b9fbc951
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.gd
@@ -0,0 +1,7 @@
+func wait() -> void:
+ pass
+
+func test():
+ @warning_ignore(redundant_await)
+ await wait()
+ print("end")
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_on_void.out b/modules/gdscript/tests/scripts/runtime/features/await_on_void.out
new file mode 100644
index 0000000000..5bc3dcf2db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_on_void.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+end
diff --git a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
index 5e7ccf534a..22929bf636 100644
--- a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
+++ b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
@@ -1,6 +1,6 @@
GDTEST_OK
-{1:(2, 0)}
-{3:(4, 0)}
+{ 1: (2, 0) }
+{ 3: (4, 0) }
[[(5, 0)]]
[[(6, 0)]]
[[(7, 0)]]
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd
index c6645c2c34..809d0d28a9 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd
@@ -69,6 +69,10 @@ func test():
value = Transform3D()
print(value == null)
+ # Projection
+ value = Projection()
+ print(value == null)
+
# Color
value = Color()
print(value == null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out
index 639f6027b9..27423ab8e7 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out
@@ -33,3 +33,4 @@ false
false
false
false
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd
index ee622bf22f..f46afb0f18 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd
@@ -69,6 +69,10 @@ func test():
value = Transform3D()
print(value != null)
+ # Projection
+ value = Projection()
+ print(value != null)
+
# Color
value = Color()
print(value != null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out
index d1e332afba..a11c47854a 100644
--- a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out
@@ -33,3 +33,4 @@ true
true
true
true
+true
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
new file mode 100644
index 0000000000..9d0c6317b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.gd
@@ -0,0 +1,19 @@
+func literal(x: float = 1):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var inferring := 2
+func inferred(x: float = inferring):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+var weakling = 3
+func weak(x: float = weakling):
+ print('x is ', x)
+ print('typeof x is ', typeof(x))
+
+func test():
+ literal()
+ inferred()
+ weak()
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
new file mode 100644
index 0000000000..a9ef4919cf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/conversion_for_default_parameter.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+x is 1
+typeof x is 3
+x is 2
+typeof x is 3
+x is 3
+typeof x is 3
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
new file mode 100644
index 0000000000..94bac1974f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd
@@ -0,0 +1,17 @@
+# https://github.com/godotengine/godot/issues/62957
+
+func test():
+ var string_dict = {}
+ string_dict["abc"] = 42
+ var stringname_dict = {}
+ stringname_dict[&"abc"] = 24
+
+ print("String key is TYPE_STRING: ", typeof(string_dict.keys()[0]) == TYPE_STRING)
+ print("StringName key is TYPE_STRING: ", typeof(stringname_dict.keys()[0]) == TYPE_STRING)
+
+ print("StringName gets String: ", string_dict.get(&"abc"))
+ print("String gets StringName: ", stringname_dict.get("abc"))
+
+ stringname_dict[&"abc"] = 42
+ # They compare equal because StringName keys are converted to String.
+ print("String Dictionary == StringName Dictionary: ", string_dict == stringname_dict)
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out
new file mode 100644
index 0000000000..ab5b89d55c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+String key is TYPE_STRING: true
+StringName key is TYPE_STRING: true
+StringName gets String: 42
+String gets StringName: 24
+String Dictionary == StringName Dictionary: true
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
new file mode 100644
index 0000000000..1d4b400d81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.gd
@@ -0,0 +1,17 @@
+# https://github.com/godotengine/godot/issues/71177
+
+func test():
+ builtin_method()
+ builtin_method_static()
+ print("done")
+
+func builtin_method():
+ var pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ pba.resize(1) # Built-in validated.
+
+
+func builtin_method_static():
+ var _pba := PackedByteArray()
+ @warning_ignore(return_value_discarded)
+ Vector2.from_angle(PI) # Static built-in validated.
diff --git a/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
new file mode 100644
index 0000000000..8e68c97774
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/does_not_override_temp_values.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+done
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
new file mode 100644
index 0000000000..81355e0255
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
@@ -0,0 +1,51 @@
+const constant_float = 1.0
+const constant_int = 1
+enum { enum_value = 1 }
+
+class Iterator:
+ func _iter_init(_count):
+ return true
+ func _iter_next(_count):
+ return false
+ func _iter_get(_count) -> StringName:
+ return &'custom'
+
+func test():
+ var hard_float := 1.0
+ var hard_int := 1
+ var hard_string := '0'
+ var hard_iterator := Iterator.new()
+
+ var variant_float: Variant = hard_float
+ var variant_int: Variant = hard_int
+ var variant_string: Variant = hard_string
+ var variant_iterator: Variant = hard_iterator
+
+ for i in 1.0:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in 1:
+ print(typeof(i) == TYPE_INT)
+ for i in 'a':
+ print(typeof(i) == TYPE_STRING)
+ for i in Iterator.new():
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in hard_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in hard_int:
+ print(typeof(i) == TYPE_INT)
+ for i in hard_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in hard_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ for i in variant_float:
+ print(typeof(i) == TYPE_FLOAT)
+ for i in variant_int:
+ print(typeof(i) == TYPE_INT)
+ for i in variant_string:
+ print(typeof(i) == TYPE_STRING)
+ for i in variant_iterator:
+ print(typeof(i) == TYPE_STRING_NAME)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
new file mode 100644
index 0000000000..b3e82d52ef
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+ok
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.gd b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
new file mode 100644
index 0000000000..f2368643de
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.gd
@@ -0,0 +1,20 @@
+func test():
+ var gdscr: = GDScript.new()
+ gdscr.source_code = '''
+extends Resource
+
+func test() -> void:
+ prints("Outer")
+ var inner = InnerClass.new()
+
+class InnerClass:
+ func _init() -> void:
+ prints("Inner")
+'''
+ @warning_ignore(return_value_discarded)
+ gdscr.reload()
+
+ var inst = gdscr.new()
+
+ @warning_ignore(unsafe_method_access)
+ inst.test()
diff --git a/modules/gdscript/tests/scripts/runtime/features/gdscript.out b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
new file mode 100644
index 0000000000..16114f57f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/gdscript.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Outer
+Inner
diff --git a/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.gd
new file mode 100644
index 0000000000..55be021a90
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.gd
@@ -0,0 +1,14 @@
+# https://github.com/godotengine/godot/issues/60145
+
+func test():
+ match "abc":
+ &"abc":
+ print("String matched StringName")
+ _:
+ print("no match")
+
+ match &"abc":
+ "abc":
+ print("StringName matched String")
+ _:
+ print("no match")
diff --git a/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.out
new file mode 100644
index 0000000000..9d5a18da3d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/match_string_stringname_equivalent.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+String matched StringName
+StringName matched String
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
new file mode 100644
index 0000000000..252e100bda
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
@@ -0,0 +1,25 @@
+# https://github.com/godotengine/godot/pull/69620
+
+var a: int = 1
+
+func shadow_regular_assignment(a: Variant, b: Variant) -> void:
+ print(a)
+ print(self.a)
+ a = b
+ print(a)
+ print(self.a)
+
+
+var v := Vector2(0.0, 0.0)
+
+func shadow_subscript_assignment(v: Vector2, x: float) -> void:
+ print(v)
+ print(self.v)
+ v.x += x
+ print(v)
+ print(self.v)
+
+
+func test():
+ shadow_regular_assignment('a', 'b')
+ shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
new file mode 100644
index 0000000000..5b981bc8bb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
@@ -0,0 +1,17 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local function parameter "a" is shadowing an already-declared variable at line 3.
+>> WARNING
+>> Line: 15
+>> SHADOWED_VARIABLE
+>> The local function parameter "v" is shadowing an already-declared variable at line 13.
+a
+1
+b
+1
+(1, 1)
+(0, 0)
+(6, 1)
+(0, 0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.gd b/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.gd
new file mode 100644
index 0000000000..e24137a20d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.gd
@@ -0,0 +1,60 @@
+func test():
+ # All combinations of 1/2/3 arguments, each being int/float.
+
+ for number in range(5):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(5.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+
+ for number in range(1, 5):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1, 5.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+
+ for number in range(1, 5, 2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1, 5, 2.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1, 5.2, 2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1, 5.2, 2.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5, 2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5.2, 2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5, 2.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ for number in range(1.2, 5.2, 2.2):
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
diff --git a/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.out b/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/range_optimized_in_for_has_int_iterator.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.gd b/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.gd
new file mode 100644
index 0000000000..63c3b84305
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.gd
@@ -0,0 +1,77 @@
+func test():
+ # All combinations of 1/2/3 arguments, each being int/float.
+ # Store result in variable to ensure actual array is created (avoid `for` + `range` optimization).
+
+ var result
+
+ result = range(5)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(5.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+
+ result = range(1, 5)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1, 5.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+
+ result = range(1, 5, 2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1, 5, 2.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1, 5.2, 2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1, 5.2, 2.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5, 2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5.2, 2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5, 2.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
+
+ result = range(1.2, 5.2, 2.2)
+ for number in result:
+ if typeof(number) != TYPE_INT:
+ print("Number returned from `range` was not an int!")
diff --git a/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.out b/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/range_returns_ints.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
new file mode 100644
index 0000000000..cc34e71b01
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.gd
@@ -0,0 +1,45 @@
+# https://github.com/godotengine/godot/issues/70964
+
+func test():
+ test_construct(0, false)
+ test_utility(0, false)
+ test_builtin_call(Vector2.UP, false)
+ test_builtin_call_validated(Vector2.UP, false)
+ test_object_call(RefCounted.new(), false)
+ test_object_call_method_bind(Resource.new(), false)
+ test_object_call_ptrcall(RefCounted.new(), false)
+
+ print("end")
+
+func test_construct(v, f):
+ Vector2(v, v) # Built-in type construct.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_utility(v, f):
+ abs(v) # Utility function.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_builtin_call(v, f):
+ @warning_ignore(unsafe_method_access)
+ v.angle() # Built-in method call.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_builtin_call_validated(v: Vector2, f):
+ @warning_ignore(return_value_discarded)
+ v.abs() # Built-in method call validated.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call(v, f):
+ @warning_ignore(unsafe_method_access)
+ v.get_reference_count() # Native type method call.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call_method_bind(v: Resource, f):
+ @warning_ignore(return_value_discarded)
+ v.duplicate() # Native type method call with MethodBind.
+ assert(not f) # Test unary operator reading from `nil`.
+
+func test_object_call_ptrcall(v: RefCounted, f):
+ @warning_ignore(return_value_discarded)
+ v.get_reference_count() # Native type method call with ptrcall.
+ assert(not f) # Test unary operator reading from `nil`.
diff --git a/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out
new file mode 100644
index 0000000000..5bc3dcf2db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/standalone-calls-do-not-write-to-nil.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+end
diff --git a/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.gd
new file mode 100644
index 0000000000..f8bd46523e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.gd
@@ -0,0 +1,11 @@
+# https://github.com/godotengine/godot/issues/64171
+
+func test():
+ print("Compare ==: ", "abc" == &"abc")
+ print("Compare ==: ", &"abc" == "abc")
+ print("Compare !=: ", "abc" != &"abc")
+ print("Compare !=: ", &"abc" != "abc")
+
+ print("Concat: ", "abc" + &"def")
+ print("Concat: ", &"abc" + "def")
+ print("Concat: ", &"abc" + &"def")
diff --git a/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.out b/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.out
new file mode 100644
index 0000000000..7e9c364b60
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/string_stringname_equivalent.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+Compare ==: true
+Compare ==: true
+Compare !=: false
+Compare !=: false
+Concat: abcdef
+Concat: abcdef
+Concat: abcdef
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out
index d4468737a5..1f33de00cc 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.out
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out
@@ -21,7 +21,7 @@ hello/world
RID(0)
Node::get_name
Node::[signal]property_list_changed
-{"hello":123}
+{ "hello": 123 }
["hello", 123]
[255, 0, 1]
[-1, 0, 1]
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd
new file mode 100644
index 0000000000..22e54cf91c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.gd
@@ -0,0 +1,9 @@
+func test():
+ var x: int = 2
+ var y = 3.14
+ var z := 2.72
+ print(typeof(x))
+ x = y
+ print(typeof(x))
+ x = z
+ print(typeof(x))
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out
new file mode 100644
index 0000000000..4a268dd8e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_assignment.out
@@ -0,0 +1,12 @@
+GDTEST_OK
+>> WARNING
+>> Line: 6
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 8
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+2
+2
+2
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
new file mode 100644
index 0000000000..af3f3cb941
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.gd
@@ -0,0 +1,9 @@
+# https://github.com/godotengine/godot/issues/71172
+
+func test():
+ @warning_ignore(narrowing_conversion)
+ var foo: int = 0.0
+ print(typeof(foo) == TYPE_INT)
+ var dict : Dictionary = {"a":0.0}
+ foo = dict.get("a")
+ print(typeof(foo) == TYPE_INT)
diff --git a/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
new file mode 100644
index 0000000000..9d111a8322
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/use_conversion_assign_with_variant_value.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+true
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index cbcd7b2955..ad38312abe 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* test_gdscript.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* test_gdscript.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "test_gdscript.h"
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
index b6b1f26203..d719e3d94a 100644
--- a/modules/gdscript/tests/test_gdscript.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* test_gdscript.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* test_gdscript.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef TEST_GDSCRIPT_H
#define TEST_GDSCRIPT_H
diff --git a/modules/glslang/glslang_resource_limits.h b/modules/glslang/glslang_resource_limits.h
index 02d3daff07..94ac607576 100644
--- a/modules/glslang/glslang_resource_limits.h
+++ b/modules/glslang/glslang_resource_limits.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* glslang_resource_limits.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* glslang_resource_limits.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GLSLANG_RESOURCE_LIMITS_H
#define GLSLANG_RESOURCE_LIMITS_H
@@ -129,6 +129,15 @@ const TBuiltInResource DefaultTBuiltInResource = {
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
+ /* .maxMeshOutputVerticesEXT = */ 256,
+ /* .maxMeshOutputPrimitivesEXT = */ 256,
+ /* .maxMeshWorkGroupSizeX_EXT = */ 128,
+ /* .maxMeshWorkGroupSizeY_EXT = */ 128,
+ /* .maxMeshWorkGroupSizeZ_EXT = */ 128,
+ /* .maxTaskWorkGroupSizeX_EXT = */ 128,
+ /* .maxTaskWorkGroupSizeY_EXT = */ 128,
+ /* .maxTaskWorkGroupSizeZ_EXT = */ 128,
+ /* .maxMeshViewCountEXT = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index b1c2140039..f96d2c50b7 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -53,7 +53,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
};
int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100
- bool check_subgroup_support = true; // assume we support subgroups
glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2;
glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
@@ -63,7 +62,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
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 (capabilities->version_major == 1 && capabilities->version_minor == 1) {
ClientVersion = glslang::EShTargetVulkan_1_1;
TargetVersion = glslang::EShTargetSpv_1_3;
@@ -88,7 +86,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
shader.setEnvClient(glslang::EShClientVulkan, ClientVersion);
shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion);
- if (check_subgroup_support) {
+ {
uint32_t stage_bit = 1 << p_stage;
uint32_t subgroup_in_shaders = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
diff --git a/modules/glslang/register_types.h b/modules/glslang/register_types.h
index d9611cc02f..d0b8cdd307 100644
--- a/modules/glslang/register_types.h
+++ b/modules/glslang/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GLSLANG_REGISTER_TYPES_H
#define GLSLANG_REGISTER_TYPES_H
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
index 6634d5df7b..d1f337715f 100644
--- a/modules/gltf/SCsub
+++ b/modules/gltf/SCsub
@@ -5,9 +5,9 @@ Import("env_modules")
env_gltf = env_modules.Clone()
-# Godot's own source files
+# Godot 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"]:
+SConscript("extensions/SCsub")
+if env.editor_build:
env_gltf.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/gltf/config.py b/modules/gltf/config.py
index 189b5a831a..130c06d264 100644
--- a/modules/gltf/config.py
+++ b/modules/gltf/config.py
@@ -26,6 +26,7 @@ def get_doc_classes():
"GLTFSpecGloss",
"GLTFState",
"GLTFTexture",
+ "GLTFTextureSampler",
]
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index 9b9eff6141..49efaa1564 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -1,19 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFCamera" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ Represents a GLTF camera.
</brief_description>
<description>
+ Represents a camera as defined by the base GLTF spec.
</description>
<tutorials>
+ <link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
+ <link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
</tutorials>
+ <methods>
+ <method name="from_dictionary" qualifiers="static">
+ <return type="GLTFCamera" />
+ <param index="0" name="dictionary" type="Dictionary" />
+ <description>
+ Creates a new GLTFCamera instance by parsing the given [Dictionary].
+ </description>
+ </method>
+ <method name="from_node" qualifiers="static">
+ <return type="GLTFCamera" />
+ <param index="0" name="camera_node" type="Camera3D" />
+ <description>
+ Create a new GLTFCamera instance from the given Godot [Camera3D] node.
+ </description>
+ </method>
+ <method name="to_dictionary" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Serializes this GLTFCamera instance into a [Dictionary].
+ </description>
+ </method>
+ <method name="to_node" qualifiers="const">
+ <return type="Camera3D" />
+ <description>
+ Converts this GLTFCamera instance into a Godot [Camera3D] node.
+ </description>
+ </method>
+ </methods>
<members>
<member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
+ The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]zfar[/code] property.
</member>
<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
+ The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]znear[/code] property.
</member>
- <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0">
+ <member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
+ The FOV of the camera. This class and GLTF define the camera FOV in radians, while Godot uses degrees. This maps to GLTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
</member>
<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
+ Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to GLTF's camera [code]type[/code] property. See [member Camera3D.projection] and the GLTF spec for more information.
+ </member>
+ <member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5">
+ The size of the camera. This class and GLTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to GLTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index cb0e3b6754..d7e8141eb1 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -10,56 +10,75 @@
<methods>
<method name="append_from_buffer">
<return type="int" enum="Error" />
- <argument index="0" name="bytes" type="PackedByteArray" />
- <argument index="1" name="base_path" type="String" />
- <argument index="2" name="state" type="GLTFState" />
- <argument index="3" name="flags" type="int" default="0" />
- <argument index="4" name="bake_fps" type="int" default="30" />
+ <param index="0" name="bytes" type="PackedByteArray" />
+ <param index="1" name="base_path" type="String" />
+ <param index="2" name="state" type="GLTFState" />
+ <param index="3" name="flags" type="int" default="0" />
<description>
+ Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter.
+ [b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
</description>
</method>
<method name="append_from_file">
<return type="int" enum="Error" />
- <argument index="0" name="path" type="String" />
- <argument index="1" name="state" type="GLTFState" />
- <argument index="2" name="flags" type="int" default="0" />
- <argument index="3" name="bake_fps" type="int" default="30" />
- <argument index="4" name="base_path" type="String" default="&quot;&quot;" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="state" type="GLTFState" />
+ <param index="2" name="flags" type="int" default="0" />
+ <param index="3" name="base_path" type="String" default="&quot;&quot;" />
<description>
+ Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
+ [b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
</description>
</method>
<method name="append_from_scene">
<return type="int" enum="Error" />
- <argument index="0" name="node" type="Node" />
- <argument index="1" name="state" type="GLTFState" />
- <argument index="2" name="flags" type="int" default="0" />
- <argument index="3" name="bake_fps" type="int" default="30" />
+ <param index="0" name="node" type="Node" />
+ <param index="1" name="state" type="GLTFState" />
+ <param index="2" name="flags" type="int" default="0" />
<description>
+ Takes a Godot Engine scene node and exports it and its descendants to the given [GLTFState] object through the [param state] parameter.
</description>
</method>
<method name="generate_buffer">
<return type="PackedByteArray" />
- <argument index="0" name="state" type="GLTFState" />
+ <param index="0" name="state" type="GLTFState" />
<description>
+ Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray].
</description>
</method>
<method name="generate_scene">
<return type="Node" />
- <argument index="0" name="state" type="GLTFState" />
- <argument index="1" name="bake_fps" type="int" default="30" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="bake_fps" type="float" default="30" />
+ <param index="2" name="trimming" type="bool" default="false" />
<description>
+ Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node.
+ </description>
+ </method>
+ <method name="register_gltf_document_extension" qualifiers="static">
+ <return type="void" />
+ <param index="0" name="extension" type="GLTFDocumentExtension" />
+ <param index="1" name="first_priority" type="bool" default="false" />
+ <description>
+ Registers the given [GLTFDocumentExtension] instance with GLTFDocument. If [param first_priority] is true, this extension will be run first. Otherwise, it will be run last.
+ [b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
+ </description>
+ </method>
+ <method name="unregister_gltf_document_extension" qualifiers="static">
+ <return type="void" />
+ <param index="0" name="extension" type="GLTFDocumentExtension" />
+ <description>
+ Unregisters the given [GLTFDocumentExtension] instance.
</description>
</method>
<method name="write_to_filesystem">
<return type="int" enum="Error" />
- <argument index="0" name="state" type="GLTFState" />
- <argument index="1" name="path" type="String" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="path" type="String" />
<description>
+ Takes a [GLTFState] object through the [param state] parameter and writes a glTF file to the filesystem.
+ [b]Note:[/b] The extension of the glTF file determines if it is a .glb binary file or a .gltf file.
</description>
</method>
</methods>
- <members>
- <member name="extensions" type="GLTFDocumentExtension[]" setter="set_extensions" getter="get_extensions" default="[]">
- </member>
- </members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 3c28546ad7..6004de32f1 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -1,59 +1,116 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFDocumentExtension" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ [GLTFDocument] extension class.
</brief_description>
<description>
+ Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export.
+ To use, make a new class extending GLTFDocumentExtension, override any methods you need, make an instance of your class, and register it using [method GLTFDocument.register_gltf_document_extension].
+ [b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
</description>
<tutorials>
</tutorials>
<methods>
+ <method name="_convert_scene_node" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="gltf_node" type="GLTFNode" />
+ <param index="2" name="scene_node" type="Node" />
+ <description>
+ Part of the export process. This method is run after [method _export_preflight] and before [method _export_node].
+ Runs when converting the data from a Godot scene node. This method can be used to process the Godot scene node data into a format that can be used by [method _export_node].
+ </description>
+ </method>
<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" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="gltf_node" type="GLTFNode" />
+ <param index="2" name="json" type="Dictionary" />
+ <param index="3" name="node" type="Node" />
<description>
+ Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_post].
+ This method can be used to modify the final JSON of each node.
</description>
</method>
<method name="_export_post" qualifiers="virtual">
<return type="int" />
- <argument index="0" name="state" type="GLTFState" />
+ <param index="0" name="state" type="GLTFState" />
<description>
+ Part of the export process. This method is run last, after all other parts of the export process.
+ This method can be used to modify the final JSON of the generated GLTF file.
</description>
</method>
<method name="_export_preflight" qualifiers="virtual">
<return type="int" />
- <argument index="0" name="root" type="Node" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="root" type="Node" />
<description>
+ Part of the export process. This method is run first, before all other parts of the export process.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ </description>
+ </method>
+ <method name="_generate_scene_node" qualifiers="virtual">
+ <return type="Node3D" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="gltf_node" type="GLTFNode" />
+ <param index="2" name="scene_parent" type="Node" />
+ <description>
+ Part of the import process. This method is run after [method _parse_node_extensions] and before [method _import_post_parse].
+ Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node.
+ </description>
+ </method>
+ <method name="_get_supported_extensions" qualifiers="virtual">
+ <return type="PackedStringArray" />
+ <description>
+ Part of the import process. This method is run after [method _import_preflight] and before [method _parse_node_extensions].
+ Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded.
</description>
</method>
<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" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="gltf_node" type="GLTFNode" />
+ <param index="2" name="json" type="Dictionary" />
+ <param index="3" name="node" type="Node" />
<description>
+ Part of the import process. This method is run after [method _import_post_parse] and before [method _import_post].
+ This method can be used to make modifications to each of the generated Godot scene nodes.
</description>
</method>
<method name="_import_post" qualifiers="virtual">
<return type="int" />
- <argument index="0" name="state" type="GLTFState" />
- <argument index="1" name="root" type="Node" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="root" type="Node" />
<description>
+ Part of the import process. This method is run last, after all other parts of the import process.
+ This method can be used to modify the final Godot scene generated by the import process.
</description>
</method>
<method name="_import_post_parse" qualifiers="virtual">
<return type="int" />
- <argument index="0" name="state" type="GLTFState" />
+ <param index="0" name="state" type="GLTFState" />
<description>
+ Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_node].
+ This method can be used to modify any of the data imported so far, including any scene nodes, before running the final per-node import step.
</description>
</method>
<method name="_import_preflight" qualifiers="virtual">
<return type="int" />
- <argument index="0" name="state" type="GLTFState" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="extensions" type="PackedStringArray" />
+ <description>
+ Part of the import process. This method is run first, before all other parts of the import process.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given GLTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ </description>
+ </method>
+ <method name="_parse_node_extensions" qualifiers="virtual">
+ <return type="int" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="gltf_node" type="GLTFNode" />
+ <param index="2" name="extensions" type="Dictionary" />
<description>
+ Part of the import process. This method is run after [method _get_supported_extensions] and before [method _generate_scene_node].
+ Runs when parsing the node extensions of a GLTFNode. This method can be used to process the extension JSON data into a format that can be used by [method _generate_scene_node]. The return value should be a member of the [enum Error] enum.
</description>
</method>
</methods>
diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml
index 354cd48a06..7fd59e14bc 100644
--- a/modules/gltf/doc_classes/GLTFLight.xml
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -1,11 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFLight" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ Represents a GLTF light.
</brief_description>
<description>
+ Represents a light as defined by the [code]KHR_lights_punctual[/code] GLTF extension.
</description>
<tutorials>
+ <link title="KHR_lights_punctual GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link>
</tutorials>
+ <methods>
+ <method name="from_dictionary" qualifiers="static">
+ <return type="GLTFLight" />
+ <param index="0" name="dictionary" type="Dictionary" />
+ <description>
+ Creates a new GLTFLight instance by parsing the given [Dictionary].
+ </description>
+ </method>
+ <method name="from_node" qualifiers="static">
+ <return type="GLTFLight" />
+ <param index="0" name="light_node" type="Light3D" />
+ <description>
+ Create a new GLTFLight instance from the given Godot [Light3D] node.
+ </description>
+ </method>
+ <method name="to_dictionary" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Serializes this GLTFLight instance into a [Dictionary].
+ </description>
+ </method>
+ <method name="to_node" qualifiers="const">
+ <return type="Light3D" />
+ <description>
+ Converts this GLTFLight instance into a Godot [Light3D] node.
+ </description>
+ </method>
+ </methods>
<members>
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)">
The [Color] of the light. Defaults to white. A black color causes the light to have no effect.
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index bac351cc20..4d2df872ea 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -9,7 +9,7 @@
<members>
<member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()">
</member>
- <member name="instance_materials" type="Array" setter="set_instance_materials" getter="get_instance_materials" default="[]">
+ <member name="instance_materials" type="Material[]" setter="set_instance_materials" getter="get_instance_materials" default="[]">
</member>
<member name="mesh" type="ImporterMesh" setter="set_mesh" getter="get_mesh">
</member>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index e933e6046a..8e48066623 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -1,11 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFNode" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ GLTF node class.
</brief_description>
<description>
+ Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes).
</description>
<tutorials>
+ <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
</tutorials>
+ <methods>
+ <method name="get_additional_data">
+ <return type="Variant" />
+ <param index="0" name="extension_name" type="StringName" />
+ <description>
+ Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ </description>
+ </method>
+ <method name="set_additional_data">
+ <return type="void" />
+ <param index="0" name="extension_name" type="StringName" />
+ <param index="1" name="additional_data" type="Variant" />
+ <description>
+ Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ </description>
+ </method>
+ </methods>
<members>
<member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
</member>
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
index dad985e886..d6ec09f113 100644
--- a/modules/gltf/doc_classes/GLTFSkeleton.xml
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -9,7 +9,7 @@
<methods>
<method name="get_bone_attachment">
<return type="BoneAttachment3D" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
</description>
</method>
@@ -29,19 +29,19 @@
</description>
</method>
<method name="get_unique_names">
- <return type="Array" />
+ <return type="String[]" />
<description>
</description>
</method>
<method name="set_godot_bone_node">
<return type="void" />
- <argument index="0" name="godot_bone_node" type="Dictionary" />
+ <param index="0" name="godot_bone_node" type="Dictionary" />
<description>
</description>
</method>
<method name="set_unique_names">
<return type="void" />
- <argument index="0" name="unique_names" type="Array" />
+ <param index="0" name="unique_names" type="String[]" />
<description>
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml
index b6a2bdb957..4de32857b5 100644
--- a/modules/gltf/doc_classes/GLTFSkin.xml
+++ b/modules/gltf/doc_classes/GLTFSkin.xml
@@ -8,7 +8,7 @@
</tutorials>
<methods>
<method name="get_inverse_binds">
- <return type="Array" />
+ <return type="Transform3D[]" />
<description>
</description>
</method>
@@ -24,19 +24,19 @@
</method>
<method name="set_inverse_binds">
<return type="void" />
- <argument index="0" name="inverse_binds" type="Array" />
+ <param index="0" name="inverse_binds" type="Transform3D[]" />
<description>
</description>
</method>
<method name="set_joint_i_to_bone_i">
<return type="void" />
- <argument index="0" name="joint_i_to_bone_i" type="Dictionary" />
+ <param index="0" name="joint_i_to_bone_i" type="Dictionary" />
<description>
</description>
</method>
<method name="set_joint_i_to_name">
<return type="void" />
- <argument index="0" name="joint_i_to_name" type="Dictionary" />
+ <param index="0" name="joint_i_to_name" type="Dictionary" />
<description>
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
index 8433cf8dd7..8882e48257 100644
--- a/modules/gltf/doc_classes/GLTFSpecGloss.xml
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -1,21 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFSpecGloss" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
+ Archived GLTF extension for specular/glossy materials.
</brief_description>
<description>
+ KHR_materials_pbrSpecularGlossiness is an archived GLTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files.
</description>
<tutorials>
+ <link title="KHR_materials_pbrSpecularGlossiness GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link>
</tutorials>
<members>
<member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)">
+ The reflected diffuse factor of the material.
</member>
<member name="diffuse_img" type="Image" setter="set_diffuse_img" getter="get_diffuse_img">
+ The diffuse texture.
</member>
<member name="gloss_factor" type="float" setter="set_gloss_factor" getter="get_gloss_factor" default="1.0">
+ The glossiness or smoothness of the material.
</member>
<member name="spec_gloss_img" type="Image" setter="set_spec_gloss_img" getter="get_spec_gloss_img">
+ The specular-glossiness texture.
</member>
<member name="specular_factor" type="Color" setter="set_specular_factor" getter="get_specular_factor" default="Color(1, 1, 1, 1)">
+ The specular RGB color of the material. The alpha channel is unused.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 44a1723563..9a554a0d49 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -7,66 +7,82 @@
<tutorials>
</tutorials>
<methods>
+ <method name="add_used_extension">
+ <return type="void" />
+ <param index="0" name="extension_name" type="String" />
+ <param index="1" name="required" type="bool" />
+ <description>
+ Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
+ </description>
+ </method>
<method name="get_accessors">
- <return type="Array" />
+ <return type="GLTFAccessor[]" />
+ <description>
+ </description>
+ </method>
+ <method name="get_additional_data">
+ <return type="Variant" />
+ <param index="0" name="extension_name" type="StringName" />
<description>
+ Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
<method name="get_animation_player">
<return type="AnimationPlayer" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
</description>
</method>
<method name="get_animation_players_count">
<return type="int" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
</description>
</method>
<method name="get_animations">
- <return type="Array" />
+ <return type="GLTFAnimation[]" />
<description>
</description>
</method>
<method name="get_buffer_views">
- <return type="Array" />
+ <return type="GLTFBufferView[]" />
<description>
</description>
</method>
<method name="get_cameras">
- <return type="Array" />
+ <return type="GLTFCamera[]" />
<description>
</description>
</method>
<method name="get_images">
- <return type="Array" />
+ <return type="Texture2D[]" />
<description>
</description>
</method>
<method name="get_lights">
- <return type="Array" />
+ <return type="GLTFLight[]" />
<description>
</description>
</method>
<method name="get_materials">
- <return type="Array" />
+ <return type="Material[]" />
<description>
</description>
</method>
<method name="get_meshes">
- <return type="Array" />
+ <return type="GLTFMesh[]" />
<description>
</description>
</method>
<method name="get_nodes">
- <return type="Array" />
+ <return type="GLTFNode[]" />
<description>
</description>
</method>
<method name="get_scene_node">
<return type="Node" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
</description>
</method>
@@ -76,117 +92,139 @@
</description>
</method>
<method name="get_skeletons">
- <return type="Array" />
+ <return type="GLTFSkeleton[]" />
<description>
</description>
</method>
<method name="get_skins">
- <return type="Array" />
+ <return type="GLTFSkin[]" />
+ <description>
+ </description>
+ </method>
+ <method name="get_texture_samplers">
+ <return type="GLTFTextureSampler[]" />
<description>
+ Retrieves the array of texture samplers that are used by the textures contained in the GLTF.
</description>
</method>
<method name="get_textures">
- <return type="Array" />
+ <return type="GLTFTexture[]" />
<description>
</description>
</method>
<method name="get_unique_animation_names">
- <return type="Array" />
+ <return type="String[]" />
<description>
</description>
</method>
<method name="get_unique_names">
- <return type="Array" />
+ <return type="String[]" />
<description>
</description>
</method>
<method name="set_accessors">
<return type="void" />
- <argument index="0" name="accessors" type="Array" />
+ <param index="0" name="accessors" type="GLTFAccessor[]" />
<description>
</description>
</method>
+ <method name="set_additional_data">
+ <return type="void" />
+ <param index="0" name="extension_name" type="StringName" />
+ <param index="1" name="additional_data" type="Variant" />
+ <description>
+ Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ </description>
+ </method>
<method name="set_animations">
<return type="void" />
- <argument index="0" name="animations" type="Array" />
+ <param index="0" name="animations" type="GLTFAnimation[]" />
<description>
</description>
</method>
<method name="set_buffer_views">
<return type="void" />
- <argument index="0" name="buffer_views" type="Array" />
+ <param index="0" name="buffer_views" type="GLTFBufferView[]" />
<description>
</description>
</method>
<method name="set_cameras">
<return type="void" />
- <argument index="0" name="cameras" type="Array" />
+ <param index="0" name="cameras" type="GLTFCamera[]" />
<description>
</description>
</method>
<method name="set_images">
<return type="void" />
- <argument index="0" name="images" type="Array" />
+ <param index="0" name="images" type="Texture2D[]" />
<description>
</description>
</method>
<method name="set_lights">
<return type="void" />
- <argument index="0" name="lights" type="Array" />
+ <param index="0" name="lights" type="GLTFLight[]" />
<description>
</description>
</method>
<method name="set_materials">
<return type="void" />
- <argument index="0" name="materials" type="Array" />
+ <param index="0" name="materials" type="Material[]" />
<description>
</description>
</method>
<method name="set_meshes">
<return type="void" />
- <argument index="0" name="meshes" type="Array" />
+ <param index="0" name="meshes" type="GLTFMesh[]" />
<description>
</description>
</method>
<method name="set_nodes">
<return type="void" />
- <argument index="0" name="nodes" type="Array" />
+ <param index="0" name="nodes" type="GLTFNode[]" />
<description>
</description>
</method>
<method name="set_skeleton_to_node">
<return type="void" />
- <argument index="0" name="skeleton_to_node" type="Dictionary" />
+ <param index="0" name="skeleton_to_node" type="Dictionary" />
<description>
</description>
</method>
<method name="set_skeletons">
<return type="void" />
- <argument index="0" name="skeletons" type="Array" />
+ <param index="0" name="skeletons" type="GLTFSkeleton[]" />
<description>
</description>
</method>
<method name="set_skins">
<return type="void" />
- <argument index="0" name="skins" type="Array" />
+ <param index="0" name="skins" type="GLTFSkin[]" />
<description>
</description>
</method>
+ <method name="set_texture_samplers">
+ <return type="void" />
+ <param index="0" name="texture_samplers" type="GLTFTextureSampler[]" />
+ <description>
+ Sets the array of texture samplers that are used by the textures contained in the GLTF.
+ </description>
+ </method>
<method name="set_textures">
<return type="void" />
- <argument index="0" name="textures" type="Array" />
+ <param index="0" name="textures" type="GLTFTexture[]" />
<description>
</description>
</method>
<method name="set_unique_animation_names">
<return type="void" />
- <argument index="0" name="unique_animation_names" type="Array" />
+ <param index="0" name="unique_animation_names" type="String[]" />
<description>
</description>
</method>
<method name="set_unique_names">
<return type="void" />
- <argument index="0" name="unique_names" type="Array" />
+ <param index="0" name="unique_names" type="String[]" />
<description>
</description>
</method>
@@ -194,7 +232,9 @@
<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 name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]">
+ </member>
+ <member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true">
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
</member>
@@ -204,7 +244,7 @@
</member>
<member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0">
</member>
- <member name="root_nodes" type="Array" setter="set_root_nodes" getter="get_root_nodes" default="[]">
+ <member name="root_nodes" type="PackedInt32Array" setter="set_root_nodes" getter="get_root_nodes" default="PackedInt32Array()">
</member>
<member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default="&quot;&quot;">
</member>
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index c0bc424168..f4486fd504 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -7,6 +7,9 @@
<tutorials>
</tutorials>
<members>
+ <member name="sampler" type="int" setter="set_sampler" getter="get_sampler" default="-1">
+ ID of the texture sampler to use when sampling the image. If -1, then the default texture sampler is used (linear filtering, and repeat wrapping in both axes).
+ </member>
<member name="src_image" type="int" setter="set_src_image" getter="get_src_image" default="0">
</member>
</members>
diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml
new file mode 100644
index 0000000000..1cc69bfe73
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFTextureSampler" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Represents a GLTF texture sampler
+ </brief_description>
+ <description>
+ Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" default="9729">
+ Texture's magnification filter, used when texture appears larger on screen than the source image.
+ </member>
+ <member name="min_filter" type="int" setter="set_min_filter" getter="get_min_filter" default="9987">
+ Texture's minification filter, used when the texture appears smaller on screen than the source image.
+ </member>
+ <member name="wrap_s" type="int" setter="set_wrap_s" getter="get_wrap_s" default="10497">
+ Wrapping mode to use for S-axis (horizontal) texture coordinates.
+ </member>
+ <member name="wrap_t" type="int" setter="set_wrap_t" getter="get_wrap_t" default="10497">
+ Wrapping mode to use for T-axis (vertical) texture coordinates.
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index 95db1c0965..3b8d2cc701 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -1,50 +1,42 @@
-/*************************************************************************/
-/* editor_scene_exporter_gltf_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_exporter_gltf_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#ifdef TOOLS_ENABLED
#include "editor_scene_exporter_gltf_plugin.h"
#include "../gltf_document.h"
-#include "../gltf_state.h"
-#include "core/config/project_settings.h"
-#include "core/error/error_list.h"
-#include "core/object/object.h"
-#include "core/templates/vector.h"
#include "editor/editor_file_dialog.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/gui/check_box.h"
-#include "scene/main/node.h"
String SceneExporterGLTFPlugin::get_name() const {
return "ConvertGLTF2";
@@ -85,7 +77,7 @@ void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
state.instantiate();
int32_t flags = 0;
flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
- Error err = doc->append_from_scene(root, state, flags, 30.0f);
+ Error err = doc->append_from_scene(root, state, flags);
if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
}
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
index 5af46bc752..0da010ec4b 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* editor_scene_exporter_gltf_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_exporter_gltf_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
@@ -36,6 +36,8 @@
#include "editor/editor_plugin.h"
#include "editor_scene_importer_gltf.h"
+class EditorFileDialog;
+
class SceneExporterGLTFPlugin : public EditorPlugin {
GDCLASS(SceneExporterGLTFPlugin, EditorPlugin);
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 8002c185c7..2b8c057ee5 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -1,39 +1,38 @@
-/*************************************************************************/
-/* editor_scene_importer_blend.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_blend.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_scene_importer_blend.h"
#ifdef TOOLS_ENABLED
#include "../gltf_document.h"
-#include "../gltf_state.h"
#include "core/config/project_settings.h"
#include "editor/editor_file_dialog.h"
@@ -41,8 +40,7 @@
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "main/main.h"
-#include "scene/main/node.h"
-#include "scene/resources/animation.h"
+#include "scene/gui/line_edit.h"
#ifdef WINDOWS_ENABLED
// Code by Pedro Estebanez (https://github.com/godotengine/godot/pull/59766)
@@ -58,13 +56,13 @@ void EditorSceneFormatImporterBlend::get_extensions(List<String> *r_extensions)
}
Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags,
- const HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
// Escape paths to be valid Python strings to embed in the script.
const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape();
- const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file(
+ const String sink = ProjectSettings::get_singleton()->get_imported_files_path().path_join(
vformat("%s-%s.gltf", p_path.get_file().get_basename(), p_path.md5_text()));
const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape();
@@ -77,20 +75,19 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
} else {
parameters_arg += "export_extras=False,";
}
- if (p_options.has(SNAME("blender/meshes/skins")) && p_options[SNAME("blender/meshes/skins")]) {
+ if (p_options.has(SNAME("blender/meshes/skins"))) {
int32_t skins = p_options["blender/meshes/skins"];
if (skins == BLEND_BONE_INFLUENCES_NONE) {
- parameters_arg += "export_all_influences=False,";
+ parameters_arg += "export_skins=False,";
} else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) {
- parameters_arg += "export_all_influences=False,";
+ parameters_arg += "export_all_influences=False,export_skins=True,";
} else if (skins == BLEND_BONE_INFLUENCES_ALL) {
- parameters_arg += "export_all_influences=True,";
+ parameters_arg += "export_all_influences=True,export_skins=True,";
}
- parameters_arg += "export_skins=True,";
} else {
parameters_arg += "export_skins=False,";
}
- if (p_options.has(SNAME("blender/materials/export_materials")) && p_options[SNAME("blender/materials/export_materials")]) {
+ if (p_options.has(SNAME("blender/materials/export_materials"))) {
int32_t exports = p_options["blender/materials/export_materials"];
if (exports == BLEND_MATERIAL_EXPORT_PLACEHOLDER) {
parameters_arg += "export_materials='PLACEHOLDER',";
@@ -115,7 +112,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
} else {
parameters_arg += "export_colors=False,";
}
- if (p_options.has(SNAME("blender/nodes/visible")) && p_options[SNAME("blender/nodes/visible")]) {
+ if (p_options.has(SNAME("blender/nodes/visible"))) {
int32_t visible = p_options["blender/nodes/visible"];
if (visible == BLEND_VISIBLE_VISIBLE_ONLY) {
parameters_arg += "use_visible=True,";
@@ -180,28 +177,28 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
"export_format='GLTF_SEPARATE',"
"export_yup=True," +
parameters_arg;
- String script =
+ String export_script =
String("import bpy, sys;") +
"print('Blender 3.0 or higher is required.', file=sys.stderr) if bpy.app.version < (3, 0, 0) else None;" +
vformat("bpy.ops.wm.open_mainfile(filepath='%s');", source_global) +
unpack_all +
vformat("bpy.ops.export_scene.gltf(export_keep_originals=True,%s);", common_args);
- print_verbose(script);
+ print_verbose(export_script);
// Run script with configured Blender binary.
String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path");
#ifdef WINDOWS_ENABLED
- blender_path = blender_path.plus_file("blender.exe");
+ blender_path = blender_path.path_join("blender.exe");
#else
- blender_path = blender_path.plus_file("blender");
+ blender_path = blender_path.path_join("blender");
#endif
List<String> args;
args.push_back("--background");
args.push_back("--python-expr");
- args.push_back(script);
+ args.push_back(export_script);
String standard_out;
int ret;
@@ -228,14 +225,14 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) {
base_dir = sink.get_base_dir();
}
- Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, p_bake_fps, base_dir);
+ Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir);
if (err != OK) {
if (r_err) {
*r_err = FAILED;
}
return nullptr;
}
- return gltf->generate_scene(state, p_bake_fps);
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
}
Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
@@ -261,7 +258,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li
#define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE));
- ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL);
+ ADD_OPTION_ENUM("blender/nodes/visible", "All,Visible Only,Renderable", BLEND_VISIBLE_ALL);
ADD_OPTION_BOOL("blender/nodes/punctual_lights", true);
ADD_OPTION_BOOL("blender/nodes/cameras", true);
ADD_OPTION_BOOL("blender/nodes/custom_properties", true);
@@ -287,14 +284,14 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li
static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
String path = p_path;
#ifdef WINDOWS_ENABLED
- path = path.plus_file("blender.exe");
+ path = path.path_join("blender.exe");
#else
- path = path.plus_file("blender");
+ path = path.path_join("blender");
#endif
#if defined(MACOS_ENABLED)
if (!FileAccess::exists(path)) {
- path = path.plus_file("Blender");
+ path = path.path_join("Blender");
}
#endif
@@ -350,9 +347,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
bool EditorFileSystemImportFormatSupportQueryBlend::is_active() const {
bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled");
- String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path");
-
- if (blend_enabled && !_test_blender_path(blender_path)) {
+ if (blend_enabled && !_test_blender_path(EDITOR_GET("filesystem/import/blender/blender3_path").operator String())) {
// Intending to import Blender, but blend not configured.
return true;
}
@@ -451,7 +446,7 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
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->get_cancel_button()->set_tooltip_text(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));
browse_dialog = memnew(EditorFileDialog);
@@ -485,7 +480,7 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
bool found = false;
for (const String &path : mdfind_paths) {
- found = _autodetect_path(path.plus_file("Contents/MacOS"));
+ found = _autodetect_path(path.path_join("Contents/MacOS"));
if (found) {
break;
}
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
index dd1c1b9889..c77a23f9f6 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.h
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* editor_scene_importer_blend.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_blend.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_SCENE_IMPORTER_BLEND_H
#define EDITOR_SCENE_IMPORTER_BLEND_H
@@ -45,9 +45,9 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
public:
enum {
+ BLEND_VISIBLE_ALL,
BLEND_VISIBLE_VISIBLE_ONLY,
- BLEND_VISIBLE_RENDERABLE,
- BLEND_VISIBLE_ALL
+ BLEND_VISIBLE_RENDERABLE
};
enum {
BLEND_BONE_INFLUENCES_NONE,
@@ -66,7 +66,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 HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
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;
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp
index faad2d315d..6c6ab7cd03 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.cpp
+++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp
@@ -1,44 +1,42 @@
-/*************************************************************************/
-/* editor_scene_importer_fbx.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_fbx.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_scene_importer_fbx.h"
#ifdef TOOLS_ENABLED
#include "../gltf_document.h"
-#include "../gltf_state.h"
#include "core/config/project_settings.h"
#include "editor/editor_settings.h"
-#include "scene/main/node.h"
-#include "scene/resources/animation.h"
+#include "main/main.h"
uint32_t EditorSceneFormatImporterFBX::get_import_flags() const {
return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
@@ -49,7 +47,7 @@ void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) co
}
Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags,
- const HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
@@ -57,7 +55,7 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
// enclosed in double quotes by OS::execute(), so we only need to escape those.
// `c_escape_multiline()` seems to do this (escapes `\` and `"` only).
const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape_multiline();
- const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file(
+ const String sink = ProjectSettings::get_singleton()->get_imported_files_path().path_join(
vformat("%s-%s.glb", p_path.get_file().get_basename(), p_path.md5_text()));
const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape_multiline();
@@ -95,14 +93,14 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
Ref<GLTFState> state;
state.instantiate();
print_verbose(vformat("glTF path: %s", sink));
- Error err = gltf->append_from_file(sink, state, p_flags, p_bake_fps);
+ Error err = gltf->append_from_file(sink, state, p_flags);
if (err != OK) {
if (r_err) {
*r_err = FAILED;
}
return nullptr;
}
- return gltf->generate_scene(state, p_bake_fps);
+ return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
}
Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,
@@ -114,4 +112,30 @@ void EditorSceneFormatImporterFBX::get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) {
}
+bool EditorFileSystemImportFormatSupportQueryFBX::is_active() const {
+ String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path");
+ return !FileAccess::exists(fbx2gltf_path);
+}
+
+Vector<String> EditorFileSystemImportFormatSupportQueryFBX::get_file_extensions() const {
+ Vector<String> ret;
+ ret.push_back("fbx");
+ return ret;
+}
+
+bool EditorFileSystemImportFormatSupportQueryFBX::query() {
+ FBXImporterManager::get_singleton()->show_dialog(true);
+
+ while (true) {
+ OS::get_singleton()->delay_usec(1);
+ DisplayServer::get_singleton()->process_events();
+ Main::iteration();
+ if (!FBXImporterManager::get_singleton()->is_visible()) {
+ break;
+ }
+ }
+
+ return false;
+}
+
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.h b/modules/gltf/editor/editor_scene_importer_fbx.h
index b0039b1c8f..cc60830eac 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.h
+++ b/modules/gltf/editor/editor_scene_importer_fbx.h
@@ -1,38 +1,40 @@
-/*************************************************************************/
-/* editor_scene_importer_fbx.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_fbx.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_SCENE_IMPORTER_FBX_H
#define EDITOR_SCENE_IMPORTER_FBX_H
#ifdef TOOLS_ENABLED
+#include "editor/editor_file_system.h"
+#include "editor/fbx_importer_manager.h"
#include "editor/import/resource_importer_scene.h"
class Animation;
@@ -45,7 +47,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 HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
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;
@@ -53,6 +55,15 @@ public:
const HashMap<StringName, Variant> &p_options) override;
};
+class EditorFileSystemImportFormatSupportQueryFBX : public EditorFileSystemImportFormatSupportQuery {
+ GDCLASS(EditorFileSystemImportFormatSupportQueryFBX, EditorFileSystemImportFormatSupportQuery);
+
+public:
+ virtual bool is_active() const override;
+ virtual Vector<String> get_file_extensions() const override;
+ virtual bool query() override;
+};
+
#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_IMPORTER_FBX_H
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index 3fadec5167..39956a6ff6 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -1,41 +1,38 @@
-/*************************************************************************/
-/* editor_scene_importer_gltf.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_gltf.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#ifdef TOOLS_ENABLED
#include "editor_scene_importer_gltf.h"
#include "../gltf_document.h"
-#include "../gltf_state.h"
-
-#include "scene/resources/animation.h"
uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
@@ -47,20 +44,28 @@ void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) c
}
Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
- const HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) {
Ref<GLTFDocument> doc;
doc.instantiate();
Ref<GLTFState> state;
state.instantiate();
- Error err = doc->append_from_file(p_path, state, p_flags, p_bake_fps);
+ Error err = doc->append_from_file(p_path, state, p_flags);
if (err != OK) {
if (r_err) {
*r_err = err;
}
return nullptr;
}
- return doc->generate_scene(state, p_bake_fps);
+ if (p_options.has("animation/import")) {
+ state->set_create_animations(bool(p_options["animation/import"]));
+ }
+
+ if (p_options.has("animation/trimming")) {
+ return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
+ } else {
+ return doc->generate_scene(state, (float)p_options["animation/fps"], false);
+ }
}
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h
index b17a1e4eaa..c2a4bf046d 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.h
+++ b/modules/gltf/editor/editor_scene_importer_gltf.h
@@ -1,41 +1,38 @@
-/*************************************************************************/
-/* editor_scene_importer_gltf.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_scene_importer_gltf.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_SCENE_IMPORTER_GLTF_H
#define EDITOR_SCENE_IMPORTER_GLTF_H
#ifdef TOOLS_ENABLED
-#include "../gltf_document_extension.h"
-#include "../gltf_state.h"
-
#include "editor/import/resource_importer_scene.h"
class Animation;
@@ -48,7 +45,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 HashMap<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
};
diff --git a/modules/gltf/extensions/SCsub b/modules/gltf/extensions/SCsub
new file mode 100644
index 0000000000..ad214bb79c
--- /dev/null
+++ b/modules/gltf/extensions/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_gltf = env_modules.Clone()
+
+# Godot source files
+env_gltf.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp
index d0bd7651e0..496a8f6cb8 100644
--- a/modules/gltf/gltf_document_extension.cpp
+++ b/modules/gltf/extensions/gltf_document_extension.cpp
@@ -1,108 +1,133 @@
-/*************************************************************************/
-/* gltf_document_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_document_extension.h"
void GLTFDocumentExtension::_bind_methods() {
- GDVIRTUAL_BIND(_import_preflight, "state");
+ // Import process.
+ GDVIRTUAL_BIND(_import_preflight, "state", "extensions");
+ GDVIRTUAL_BIND(_get_supported_extensions);
+ GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions");
+ GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent");
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");
+ // Export process.
+ GDVIRTUAL_BIND(_export_preflight, "state", "root");
+ GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node");
GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node");
GDVIRTUAL_BIND(_export_post, "state");
}
-Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) {
- ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+// Import process.
+Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) {
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;
+ GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err);
+ return Error(err);
+}
+
+Vector<String> GLTFDocumentExtension::get_supported_extensions() {
+ Vector<String> ret;
+ GDVIRTUAL_CALL(_get_supported_extensions, ret);
+ return ret;
}
-Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) {
+Error GLTFDocumentExtension::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) {
ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
int err = OK;
- if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) {
- return Error(err);
- }
- return OK;
+ GDVIRTUAL_CALL(_parse_node_extensions, p_state, p_gltf_node, p_extensions, err);
+ return Error(err);
+}
+
+Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
+ ERR_FAIL_NULL_V(p_state, nullptr);
+ ERR_FAIL_NULL_V(p_gltf_node, nullptr);
+ ERR_FAIL_NULL_V(p_scene_parent, nullptr);
+ Node3D *ret_node = nullptr;
+ GDVIRTUAL_CALL(_generate_scene_node, p_state, p_gltf_node, p_scene_parent, ret_node);
+ return ret_node;
}
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;
+ GDVIRTUAL_CALL(_import_post_parse, p_state, err);
+ return Error(err);
}
-Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
+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;
+ GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err);
+ return Error(err);
+}
+
+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(_export_post, p_state, err)) {
- return Error(err);
- }
- return OK;
+ GDVIRTUAL_CALL(_import_post, p_state, p_root, err);
+ return Error(err);
}
-Error GLTFDocumentExtension::export_preflight(Node *p_root) {
+
+// Export process.
+Error GLTFDocumentExtension::export_preflight(Ref<GLTFState> p_state, 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;
+ GDVIRTUAL_CALL(_export_preflight, p_state, p_root, err);
+ return Error(err);
}
-Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) {
+ ERR_FAIL_NULL(p_state);
+ ERR_FAIL_NULL(p_gltf_node);
+ ERR_FAIL_NULL(p_scene_node);
+ GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node);
+}
+
+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(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) {
- return Error(err);
- }
- return OK;
+ GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err);
+ return Error(err);
}
-Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
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;
+ GDVIRTUAL_CALL(_export_post, p_state, err);
+ return Error(err);
}
diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h
index 0ef9109584..97f5045a1c 100644
--- a/modules/gltf/gltf_document_extension.h
+++ b/modules/gltf/extensions/gltf_document_extension.h
@@ -1,38 +1,37 @@
-/*************************************************************************/
-/* gltf_document_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_DOCUMENT_EXTENSION_H
#define GLTF_DOCUMENT_EXTENSION_H
-#include "gltf_state.h"
-#include "structures/gltf_node.h"
+#include "../gltf_state.h"
class GLTFDocumentExtension : public Resource {
GDCLASS(GLTFDocumentExtension, Resource);
@@ -41,18 +40,31 @@ protected:
static void _bind_methods();
public:
- virtual Error import_preflight(Ref<GLTFState> p_state);
+ // Import process.
+ virtual Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions);
+ virtual Vector<String> get_supported_extensions();
+ virtual Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions);
+ virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
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 import_post(Ref<GLTFState> p_state, Node *p_node);
+ // Export process.
+ virtual Error export_preflight(Ref<GLTFState> p_state, Node *p_root);
+ virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_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>);
+ virtual Error export_post(Ref<GLTFState> p_state);
+
+ // Import process.
+ GDVIRTUAL2R(int, _import_preflight, Ref<GLTFState>, Vector<String>);
+ GDVIRTUAL0R(Vector<String>, _get_supported_extensions);
+ GDVIRTUAL3R(int, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary);
+ GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
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 *);
+ // Export process.
+ GDVIRTUAL2R(int, _export_preflight, Ref<GLTFState>, Node *);
+ GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
GDVIRTUAL1R(int, _export_post, Ref<GLTFState>);
};
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index 1620900a04..388c3ec740 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -1,38 +1,36 @@
-/*************************************************************************/
-/* gltf_document_extension_convert_importer_mesh.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document_extension_convert_importer_mesh.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_document_extension_convert_importer_mesh.h"
-#include "gltf_state.h"
-
-#include "core/error/error_macros.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/importer_mesh.h"
@@ -61,6 +59,7 @@ Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_sta
mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
node->replace_by(mesh_instance_node_3d);
delete_queue.push_back(node);
+ node = mesh_instance_node_3d;
} else {
memdelete(mesh_instance_node_3d);
}
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
index 00e664e73f..1147c2af38 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.h
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
@@ -1,42 +1,38 @@
-/*************************************************************************/
-/* gltf_document_extension_convert_importer_mesh.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document_extension_convert_importer_mesh.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H
#define GLTF_DOCUMENT_EXTENSION_CONVERT_IMPORTER_MESH_H
#include "gltf_document_extension.h"
-#include "scene/3d/importer_mesh_instance_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/resources/importer_mesh.h"
-
class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
index af21a4e804..c60b522168 100644
--- a/modules/gltf/extensions/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
@@ -1,36 +1,44 @@
-/*************************************************************************/
-/* gltf_light.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_light.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_light.h"
+#include "scene/3d/light_3d.h"
+
void GLTFLight::_bind_methods() {
+ ClassDB::bind_static_method("GLTFLight", D_METHOD("from_node", "light_node"), &GLTFLight::from_node);
+ ClassDB::bind_method(D_METHOD("to_node"), &GLTFLight::to_node);
+
+ ClassDB::bind_static_method("GLTFLight", D_METHOD("from_dictionary", "dictionary"), &GLTFLight::from_dictionary);
+ ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFLight::to_dictionary);
+
ClassDB::bind_method(D_METHOD("get_color"), &GLTFLight::get_color);
ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color);
ClassDB::bind_method(D_METHOD("get_intensity"), &GLTFLight::get_intensity);
@@ -99,3 +107,116 @@ float GLTFLight::get_outer_cone_angle() {
void GLTFLight::set_outer_cone_angle(float p_outer_cone_angle) {
outer_cone_angle = p_outer_cone_angle;
}
+
+Ref<GLTFLight> GLTFLight::from_node(const Light3D *p_light) {
+ Ref<GLTFLight> l;
+ l.instantiate();
+ ERR_FAIL_COND_V_MSG(!p_light, l, "Tried to create a GLTFLight from a Light3D node, but the given node was null.");
+ l->color = p_light->get_color();
+ if (cast_to<DirectionalLight3D>(p_light)) {
+ l->light_type = "directional";
+ const DirectionalLight3D *light = cast_to<const DirectionalLight3D>(p_light);
+ l->intensity = light->get_param(DirectionalLight3D::PARAM_ENERGY);
+ l->range = FLT_MAX; // Range for directional lights is infinite in Godot.
+ } else if (cast_to<const OmniLight3D>(p_light)) {
+ l->light_type = "point";
+ const OmniLight3D *light = cast_to<const OmniLight3D>(p_light);
+ l->range = light->get_param(OmniLight3D::PARAM_RANGE);
+ l->intensity = light->get_param(OmniLight3D::PARAM_ENERGY);
+ } else if (cast_to<const SpotLight3D>(p_light)) {
+ l->light_type = "spot";
+ const SpotLight3D *light = cast_to<const SpotLight3D>(p_light);
+ l->range = light->get_param(SpotLight3D::PARAM_RANGE);
+ l->intensity = light->get_param(SpotLight3D::PARAM_ENERGY);
+ l->outer_cone_angle = Math::deg_to_rad(light->get_param(SpotLight3D::PARAM_SPOT_ANGLE));
+ // This equation is the inverse of the import equation (which has a desmos link).
+ float angle_ratio = 1 - (0.2 / (0.1 + light->get_param(SpotLight3D::PARAM_SPOT_ATTENUATION)));
+ angle_ratio = MAX(0, angle_ratio);
+ l->inner_cone_angle = l->outer_cone_angle * angle_ratio;
+ }
+ return l;
+}
+
+Light3D *GLTFLight::to_node() const {
+ if (light_type == "directional") {
+ DirectionalLight3D *light = memnew(DirectionalLight3D);
+ light->set_param(Light3D::PARAM_ENERGY, intensity);
+ light->set_color(color);
+ return light;
+ }
+ if (light_type == "point") {
+ OmniLight3D *light = memnew(OmniLight3D);
+ light->set_param(OmniLight3D::PARAM_ENERGY, intensity);
+ light->set_param(OmniLight3D::PARAM_RANGE, CLAMP(range, 0, 4096));
+ light->set_color(color);
+ return light;
+ }
+ if (light_type == "spot") {
+ SpotLight3D *light = memnew(SpotLight3D);
+ light->set_param(SpotLight3D::PARAM_ENERGY, intensity);
+ light->set_param(SpotLight3D::PARAM_RANGE, CLAMP(range, 0, 4096));
+ light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad_to_deg(outer_cone_angle));
+ light->set_color(color);
+ // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
+ // The points in desmos are not exact, except for (1, infinity).
+ float angle_ratio = inner_cone_angle / outer_cone_angle;
+ float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
+ light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
+ return light;
+ }
+ return memnew(Light3D);
+}
+
+Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse GLTF light, missing required field 'type'.");
+ Ref<GLTFLight> light;
+ light.instantiate();
+ const String &type = p_dictionary["type"];
+ light->light_type = type;
+
+ if (p_dictionary.has("color")) {
+ const Array &arr = p_dictionary["color"];
+ if (arr.size() == 3) {
+ light->color = Color(arr[0], arr[1], arr[2]).linear_to_srgb();
+ } else {
+ ERR_PRINT("Error parsing GLTF light: The color must have exactly 3 numbers.");
+ }
+ }
+ if (p_dictionary.has("intensity")) {
+ light->intensity = p_dictionary["intensity"];
+ }
+ if (p_dictionary.has("range")) {
+ light->range = p_dictionary["range"];
+ }
+ if (type == "spot") {
+ const Dictionary &spot = p_dictionary["spot"];
+ light->inner_cone_angle = spot["innerConeAngle"];
+ light->outer_cone_angle = spot["outerConeAngle"];
+ if (light->inner_cone_angle >= light->outer_cone_angle) {
+ ERR_PRINT("Error parsing GLTF light: The inner angle must be smaller than the outer angle.");
+ }
+ } else if (type != "point" && type != "directional") {
+ ERR_PRINT("Error parsing GLTF light: Light type '" + type + "' is unknown.");
+ }
+ return light;
+}
+
+Dictionary GLTFLight::to_dictionary() const {
+ Dictionary d;
+ Array color_array;
+ color_array.resize(3);
+ color_array[0] = color.r;
+ color_array[1] = color.g;
+ color_array[2] = color.b;
+ d["color"] = color_array;
+ d["type"] = light_type;
+ if (light_type == "spot") {
+ Dictionary spot_dict;
+ spot_dict["innerConeAngle"] = inner_cone_angle;
+ spot_dict["outerConeAngle"] = outer_cone_angle;
+ d["spot"] = spot_dict;
+ }
+ d["intensity"] = intensity;
+ d["range"] = range;
+ return d;
+}
diff --git a/modules/gltf/extensions/gltf_light.h b/modules/gltf/extensions/gltf_light.h
index 58fa299dfd..2ace08202f 100644
--- a/modules/gltf/extensions/gltf_light.h
+++ b/modules/gltf/extensions/gltf_light.h
@@ -1,39 +1,41 @@
-/*************************************************************************/
-/* gltf_light.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_light.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_LIGHT_H
#define GLTF_LIGHT_H
-#include "core/config/engine.h"
#include "core/io/resource.h"
-#include "scene/3d/light_3d.h"
+
+class Light3D;
+
+// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual
class GLTFLight : public Resource {
GDCLASS(GLTFLight, Resource)
@@ -68,6 +70,12 @@ public:
float get_outer_cone_angle();
void set_outer_cone_angle(float p_outer_cone_angle);
+
+ static Ref<GLTFLight> from_node(const Light3D *p_light);
+ Light3D *to_node() const;
+
+ static Ref<GLTFLight> from_dictionary(const Dictionary p_dictionary);
+ Dictionary to_dictionary() const;
};
#endif // GLTF_LIGHT_H
diff --git a/modules/gltf/extensions/gltf_spec_gloss.cpp b/modules/gltf/extensions/gltf_spec_gloss.cpp
index 83af91bfcc..52a5f353fd 100644
--- a/modules/gltf/extensions/gltf_spec_gloss.cpp
+++ b/modules/gltf/extensions/gltf_spec_gloss.cpp
@@ -1,35 +1,37 @@
-/*************************************************************************/
-/* gltf_spec_gloss.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_spec_gloss.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_spec_gloss.h"
+#include "core/io/image.h"
+
void GLTFSpecGloss::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_diffuse_img"), &GLTFSpecGloss::get_diffuse_img);
ClassDB::bind_method(D_METHOD("set_diffuse_img", "diffuse_img"), &GLTFSpecGloss::set_diffuse_img);
diff --git a/modules/gltf/extensions/gltf_spec_gloss.h b/modules/gltf/extensions/gltf_spec_gloss.h
index a45fa4296c..46acd67979 100644
--- a/modules/gltf/extensions/gltf_spec_gloss.h
+++ b/modules/gltf/extensions/gltf_spec_gloss.h
@@ -1,39 +1,45 @@
-/*************************************************************************/
-/* gltf_spec_gloss.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_spec_gloss.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SPEC_GLOSS_H
#define GLTF_SPEC_GLOSS_H
-#include "core/io/image.h"
#include "core/io/resource.h"
+class Image;
+
+// KHR_materials_pbrSpecularGlossiness is an archived GLTF extension.
+// This means that it is deprecated and not recommended for new files.
+// However, it is still supported for loading old files.
+// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness
+
class GLTFSpecGloss : public Resource {
GDCLASS(GLTFSpecGloss, Resource);
friend class GLTFDocument;
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
index c20c87f798..ca9a4d04e4 100644
--- a/modules/gltf/gltf_defines.h
+++ b/modules/gltf/gltf_defines.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_defines.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -36,9 +36,10 @@
// Godot classes used by GLTF headers.
class BoneAttachment3D;
class CSGShape3D;
-class DirectionalLight3D;
class GridMap;
+class ImporterMeshInstance3D;
class Light3D;
+class MeshInstance3D;
class MultiMeshInstance3D;
class Skeleton3D;
class Skin;
@@ -58,6 +59,7 @@ class GLTFSkin;
class GLTFSpecGloss;
class GLTFState;
class GLTFTexture;
+class GLTFTextureSampler;
// GLTF index aliases.
using GLTFAccessorIndex = int;
@@ -66,13 +68,14 @@ using GLTFBufferIndex = int;
using GLTFBufferViewIndex = int;
using GLTFCameraIndex = int;
using GLTFImageIndex = int;
+using GLTFLightIndex = int;
using GLTFMaterialIndex = int;
using GLTFMeshIndex = int;
-using GLTFLightIndex = int;
using GLTFNodeIndex = int;
using GLTFSkeletonIndex = int;
using GLTFSkinIndex = int;
using GLTFTextureIndex = int;
+using GLTFTextureSamplerIndex = int;
enum GLTFType {
TYPE_SCALAR,
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 4ca8482ba3..5634c51c61 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -1,64 +1,53 @@
-/*************************************************************************/
-/* gltf_document.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_document.h"
#include "extensions/gltf_spec_gloss.h"
-#include "gltf_document_extension.h"
-#include "gltf_document_extension_convert_importer_mesh.h"
-#include "gltf_state.h"
#include "core/crypto/crypto_core.h"
-#include "core/error/error_macros.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/json.h"
#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
-#include "core/math/vector2.h"
-#include "core/variant/dictionary.h"
-#include "core/variant/typed_array.h"
-#include "core/variant/variant.h"
#include "core/version.h"
#include "drivers/png/png_driver_common.h"
-#include "scene/2d/node_2d.h"
+#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_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/multimesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/animation/animation_player.h"
-#include "scene/resources/importer_mesh.h"
-#include "scene/resources/material.h"
-#include "scene/resources/mesh.h"
-#include "scene/resources/multimesh.h"
+#include "scene/resources/skin.h"
#include "scene/resources/surface_tool.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -114,140 +103,147 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
return importer_mesh;
}
-Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
- if (!state->buffers.size()) {
- state->buffers.push_back(Vector<uint8_t>());
+Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) {
+ if (!p_state->buffers.size()) {
+ p_state->buffers.push_back(Vector<uint8_t>());
}
/* STEP CONVERT MESH INSTANCES */
- _convert_mesh_instances(state);
+ _convert_mesh_instances(p_state);
/* STEP SERIALIZE CAMERAS */
- Error err = _serialize_cameras(state);
+ Error err = _serialize_cameras(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP 3 CREATE SKINS */
- err = _serialize_skins(state);
+ err = _serialize_skins(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE MESHES (we have enough info now) */
- err = _serialize_meshes(state);
+ err = _serialize_meshes(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE TEXTURES */
- err = _serialize_materials(state);
+ err = _serialize_materials(p_state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP SERIALIZE TEXTURE SAMPLERS */
+ err = _serialize_texture_samplers(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE ANIMATIONS */
- err = _serialize_animations(state);
+ err = _serialize_animations(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE ACCESSORS */
- err = _encode_accessors(state);
+ err = _encode_accessors(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE IMAGES */
- err = _serialize_images(state, p_path);
+ err = _serialize_images(p_state, p_path);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE TEXTURES */
- err = _serialize_textures(state);
+ err = _serialize_textures(p_state);
if (err != OK) {
return Error::FAILED;
}
- for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) {
- state->buffer_views.write[i]->buffer = 0;
+ for (GLTFBufferViewIndex i = 0; i < p_state->buffer_views.size(); i++) {
+ p_state->buffer_views.write[i]->buffer = 0;
}
/* STEP SERIALIZE BUFFER VIEWS */
- err = _encode_buffer_views(state);
+ err = _encode_buffer_views(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE NODES */
- err = _serialize_nodes(state);
+ err = _serialize_nodes(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE SCENE */
- err = _serialize_scenes(state);
+ err = _serialize_scenes(p_state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP SERIALIZE SCENE */
- err = _serialize_lights(state);
+ /* STEP SERIALIZE LIGHTS */
+ err = _serialize_lights(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE EXTENSIONS */
- err = _serialize_extensions(state);
+ err = _serialize_gltf_extensions(p_state);
if (err != OK) {
return Error::FAILED;
}
/* STEP SERIALIZE VERSION */
- err = _serialize_version(state);
+ err = _serialize_version(p_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];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->export_post(state);
+ err = ext->export_post(p_state);
ERR_FAIL_COND_V(err != OK, err);
}
return OK;
}
-Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const {
- Array extensions_used;
- Array extensions_required;
- if (!state->lights.is_empty()) {
+Error GLTFDocument::_serialize_gltf_extensions(Ref<GLTFState> p_state) const {
+ Vector<String> extensions_used = p_state->extensions_used;
+ Vector<String> extensions_required = p_state->extensions_required;
+ if (!p_state->lights.is_empty()) {
extensions_used.push_back("KHR_lights_punctual");
}
- if (state->use_khr_texture_transform) {
+ if (p_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;
+ extensions_used.sort();
+ p_state->json["extensionsUsed"] = extensions_used;
}
if (!extensions_required.is_empty()) {
- state->json["extensionsRequired"] = extensions_required;
+ extensions_required.sort();
+ p_state->json["extensionsRequired"] = extensions_required;
}
return OK;
}
-Error GLTFDocument::_serialize_scenes(Ref<GLTFState> state) {
+Error GLTFDocument::_serialize_scenes(Ref<GLTFState> p_state) {
Array scenes;
const int loaded_scene = 0;
- state->json["scene"] = loaded_scene;
+ p_state->json["scene"] = loaded_scene;
- if (state->nodes.size()) {
+ if (p_state->nodes.size()) {
Dictionary s;
- if (!state->scene_name.is_empty()) {
- s["name"] = state->scene_name;
+ if (!p_state->scene_name.is_empty()) {
+ s["name"] = p_state->scene_name;
}
Array nodes;
@@ -255,21 +251,21 @@ Error GLTFDocument::_serialize_scenes(Ref<GLTFState> state) {
s["nodes"] = nodes;
scenes.push_back(s);
}
- state->json["scenes"] = scenes;
+ p_state->json["scenes"] = scenes;
return OK;
}
-Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
+Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> p_state) {
Error err;
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (f.is_null()) {
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (file.is_null()) {
return err;
}
Vector<uint8_t> array;
- array.resize(f->get_length());
- f->get_buffer(array.ptrw(), array.size());
+ array.resize(file->get_length());
+ file->get_buffer(array.ptrw(), array.size());
String text;
text.parse_utf8((const char *)array.ptr(), array.size());
@@ -279,26 +275,26 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
_err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
return err;
}
- state->json = json.get_data();
+ p_state->json = json.get_data();
return OK;
}
-Error GLTFDocument::_parse_glb(Ref<FileAccess> f, Ref<GLTFState> state) {
- ERR_FAIL_NULL_V(f, ERR_INVALID_PARAMETER);
- ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(f->get_position() != 0, ERR_FILE_CANT_READ);
- uint32_t magic = f->get_32();
+Error GLTFDocument::_parse_glb(Ref<FileAccess> p_file, Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_file, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_file->get_position() != 0, ERR_FILE_CANT_READ);
+ uint32_t magic = p_file->get_32();
ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF
- f->get_32(); // version
- f->get_32(); // length
- uint32_t chunk_length = f->get_32();
- uint32_t chunk_type = f->get_32();
+ p_file->get_32(); // version
+ p_file->get_32(); // length
+ uint32_t chunk_length = p_file->get_32();
+ uint32_t chunk_type = p_file->get_32();
ERR_FAIL_COND_V(chunk_type != 0x4E4F534A, ERR_PARSE_ERROR); //JSON
Vector<uint8_t> json_data;
json_data.resize(chunk_length);
- uint32_t len = f->get_buffer(json_data.ptrw(), chunk_length);
+ uint32_t len = p_file->get_buffer(json_data.ptrw(), chunk_length);
ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
String text;
@@ -311,21 +307,21 @@ Error GLTFDocument::_parse_glb(Ref<FileAccess> f, Ref<GLTFState> state) {
return err;
}
- state->json = json.get_data();
+ p_state->json = json.get_data();
//data?
- chunk_length = f->get_32();
- chunk_type = f->get_32();
+ chunk_length = p_file->get_32();
+ chunk_type = p_file->get_32();
- if (f->eof_reached()) {
+ if (p_file->eof_reached()) {
return OK; //all good
}
ERR_FAIL_COND_V(chunk_type != 0x004E4942, ERR_PARSE_ERROR); //BIN
- state->glb_data.resize(chunk_length);
- len = f->get_buffer(state->glb_data.ptrw(), chunk_length);
+ p_state->glb_data.resize(chunk_length);
+ len = p_file->get_buffer(p_state->glb_data.ptrw(), chunk_length);
ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
return OK;
@@ -398,89 +394,88 @@ static Vector<real_t> _xform_to_array(const Transform3D p_transform) {
return array;
}
-Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
+Error GLTFDocument::_serialize_nodes(Ref<GLTFState> p_state) {
Array nodes;
- for (int i = 0; i < state->nodes.size(); i++) {
+ for (int i = 0; i < p_state->nodes.size(); i++) {
Dictionary node;
- Ref<GLTFNode> n = state->nodes[i];
+ Ref<GLTFNode> gltf_node = p_state->nodes[i];
Dictionary extensions;
node["extensions"] = extensions;
- if (!n->get_name().is_empty()) {
- node["name"] = n->get_name();
+ if (!gltf_node->get_name().is_empty()) {
+ node["name"] = gltf_node->get_name();
}
- if (n->camera != -1) {
- node["camera"] = n->camera;
+ if (gltf_node->camera != -1) {
+ node["camera"] = gltf_node->camera;
}
- if (n->light != -1) {
+ if (gltf_node->light != -1) {
Dictionary lights_punctual;
extensions["KHR_lights_punctual"] = lights_punctual;
- lights_punctual["light"] = n->light;
+ lights_punctual["light"] = gltf_node->light;
}
- if (n->mesh != -1) {
- node["mesh"] = n->mesh;
+ if (gltf_node->mesh != -1) {
+ node["mesh"] = gltf_node->mesh;
}
- if (n->skin != -1) {
- node["skin"] = n->skin;
+ if (gltf_node->skin != -1) {
+ node["skin"] = gltf_node->skin;
}
- if (n->skeleton != -1 && n->skin < 0) {
+ if (gltf_node->skeleton != -1 && gltf_node->skin < 0) {
}
- if (n->xform != Transform3D()) {
- node["matrix"] = _xform_to_array(n->xform);
+ if (gltf_node->xform != Transform3D()) {
+ node["matrix"] = _xform_to_array(gltf_node->xform);
}
- if (!n->rotation.is_equal_approx(Quaternion())) {
- node["rotation"] = _quaternion_to_array(n->rotation);
+ if (!gltf_node->rotation.is_equal_approx(Quaternion())) {
+ node["rotation"] = _quaternion_to_array(gltf_node->rotation);
}
- if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
- node["scale"] = _vec3_to_arr(n->scale);
+ if (!gltf_node->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
+ node["scale"] = _vec3_to_arr(gltf_node->scale);
}
- if (!n->position.is_equal_approx(Vector3())) {
- node["translation"] = _vec3_to_arr(n->position);
+ if (!gltf_node->position.is_zero_approx()) {
+ node["translation"] = _vec3_to_arr(gltf_node->position);
}
- if (n->children.size()) {
+ if (gltf_node->children.size()) {
Array children;
- for (int j = 0; j < n->children.size(); j++) {
- children.push_back(n->children[j]);
+ for (int j = 0; j < gltf_node->children.size(); j++) {
+ children.push_back(gltf_node->children[j]);
}
node["children"] = children;
}
- for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
- Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
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(!p_state->scene_nodes.find(i));
+ Error err = ext->export_node(p_state, gltf_node, node, p_state->scene_nodes[i]);
ERR_CONTINUE(err != OK);
}
nodes.push_back(node);
}
- state->json["nodes"] = nodes;
+ p_state->json["nodes"] = nodes;
return OK;
}
-String GLTFDocument::_gen_unique_name(Ref<GLTFState> state, const String &p_name) {
+String GLTFDocument::_gen_unique_name(Ref<GLTFState> p_state, const String &p_name) {
const String s_name = p_name.validate_node_name();
- String name;
+ String u_name;
int index = 1;
while (true) {
- name = s_name;
+ u_name = s_name;
if (index > 1) {
- name += itos(index);
+ u_name += itos(index);
}
- if (!state->unique_names.has(name)) {
+ if (!p_state->unique_names.has(u_name)) {
break;
}
index++;
}
- state->unique_names.insert(name);
+ p_state->unique_names.insert(u_name);
- return name;
+ return u_name;
}
String GLTFDocument::_sanitize_animation_name(const String &p_name) {
@@ -488,71 +483,71 @@ String GLTFDocument::_sanitize_animation_name(const String &p_name) {
// (See animation/animation_player.cpp::add_animation)
// TODO: Consider adding invalid_characters or a validate_animation_name to animation_player to mirror Node.
- String name = p_name.validate_node_name();
- name = name.replace(",", "");
- name = name.replace("[", "");
- return name;
+ String anim_name = p_name.validate_node_name();
+ anim_name = anim_name.replace(",", "");
+ anim_name = anim_name.replace("[", "");
+ return anim_name;
}
-String GLTFDocument::_gen_unique_animation_name(Ref<GLTFState> state, const String &p_name) {
+String GLTFDocument::_gen_unique_animation_name(Ref<GLTFState> p_state, const String &p_name) {
const String s_name = _sanitize_animation_name(p_name);
- String name;
+ String u_name;
int index = 1;
while (true) {
- name = s_name;
+ u_name = s_name;
if (index > 1) {
- name += itos(index);
+ u_name += itos(index);
}
- if (!state->unique_animation_names.has(name)) {
+ if (!p_state->unique_animation_names.has(u_name)) {
break;
}
index++;
}
- state->unique_animation_names.insert(name);
+ p_state->unique_animation_names.insert(u_name);
- return name;
+ return u_name;
}
String GLTFDocument::_sanitize_bone_name(const String &p_name) {
- String name = p_name;
- name = name.replace(":", "_");
- name = name.replace("/", "_");
- return name;
+ String bone_name = p_name;
+ bone_name = bone_name.replace(":", "_");
+ bone_name = bone_name.replace("/", "_");
+ return bone_name;
}
-String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i, const String &p_name) {
+String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> p_state, const GLTFSkeletonIndex p_skel_i, const String &p_name) {
String s_name = _sanitize_bone_name(p_name);
if (s_name.is_empty()) {
s_name = "bone";
}
- String name;
+ String u_name;
int index = 1;
while (true) {
- name = s_name;
+ u_name = s_name;
if (index > 1) {
- name += "_" + itos(index);
+ u_name += "_" + itos(index);
}
- if (!state->skeletons[skel_i]->unique_names.has(name)) {
+ if (!p_state->skeletons[p_skel_i]->unique_names.has(u_name)) {
break;
}
index++;
}
- state->skeletons.write[skel_i]->unique_names.insert(name);
+ p_state->skeletons.write[p_skel_i]->unique_names.insert(u_name);
- return name;
+ return u_name;
}
-Error GLTFDocument::_parse_scenes(Ref<GLTFState> state) {
- ERR_FAIL_COND_V(!state->json.has("scenes"), ERR_FILE_CORRUPT);
- const Array &scenes = state->json["scenes"];
+Error GLTFDocument::_parse_scenes(Ref<GLTFState> p_state) {
+ ERR_FAIL_COND_V(!p_state->json.has("scenes"), ERR_FILE_CORRUPT);
+ const Array &scenes = p_state->json["scenes"];
int loaded_scene = 0;
- if (state->json.has("scene")) {
- loaded_scene = state->json["scene"];
+ if (p_state->json.has("scene")) {
+ loaded_scene = p_state->json["scene"];
} else {
WARN_PRINT("The load-time scene is not defined in the glTF2 file. Picking the first scene.");
}
@@ -563,22 +558,22 @@ Error GLTFDocument::_parse_scenes(Ref<GLTFState> state) {
ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE);
const Array &nodes = s["nodes"];
for (int j = 0; j < nodes.size(); j++) {
- state->root_nodes.push_back(nodes[j]);
+ p_state->root_nodes.push_back(nodes[j]);
}
if (s.has("name") && !String(s["name"]).is_empty() && !((String)s["name"]).begins_with("Scene")) {
- state->scene_name = _gen_unique_name(state, s["name"]);
+ p_state->scene_name = _gen_unique_name(p_state, s["name"]);
} else {
- state->scene_name = _gen_unique_name(state, state->filename);
+ p_state->scene_name = _gen_unique_name(p_state, p_state->filename);
}
}
return OK;
}
-Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
- ERR_FAIL_COND_V(!state->json.has("nodes"), ERR_FILE_CORRUPT);
- const Array &nodes = state->json["nodes"];
+Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
+ ERR_FAIL_COND_V(!p_state->json.has("nodes"), ERR_FILE_CORRUPT);
+ const Array &nodes = p_state->json["nodes"];
for (int i = 0; i < nodes.size(); i++) {
Ref<GLTFNode> node;
node.instantiate();
@@ -622,6 +617,11 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
node->light = light;
}
}
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ Error err = ext->parse_node_extensions(p_state, node, extensions);
+ ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing.");
+ }
}
if (n.has("children")) {
@@ -631,35 +631,35 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
}
}
- state->nodes.push_back(node);
+ p_state->nodes.push_back(node);
}
// build the hierarchy
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
- for (int j = 0; j < state->nodes[node_i]->children.size(); j++) {
- GLTFNodeIndex child_i = state->nodes[node_i]->children[j];
+ for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); node_i++) {
+ for (int j = 0; j < p_state->nodes[node_i]->children.size(); j++) {
+ GLTFNodeIndex child_i = p_state->nodes[node_i]->children[j];
- ERR_FAIL_INDEX_V(child_i, state->nodes.size(), ERR_FILE_CORRUPT);
- ERR_CONTINUE(state->nodes[child_i]->parent != -1); //node already has a parent, wtf.
+ ERR_FAIL_INDEX_V(child_i, p_state->nodes.size(), ERR_FILE_CORRUPT);
+ ERR_CONTINUE(p_state->nodes[child_i]->parent != -1); //node already has a parent, wtf.
- state->nodes.write[child_i]->parent = node_i;
+ p_state->nodes.write[child_i]->parent = node_i;
}
}
- _compute_node_heights(state);
+ _compute_node_heights(p_state);
return OK;
}
-void GLTFDocument::_compute_node_heights(Ref<GLTFState> state) {
- state->root_nodes.clear();
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) {
- Ref<GLTFNode> node = state->nodes[node_i];
+void GLTFDocument::_compute_node_heights(Ref<GLTFState> p_state) {
+ p_state->root_nodes.clear();
+ for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) {
+ Ref<GLTFNode> node = p_state->nodes[node_i];
node->height = 0;
GLTFNodeIndex current_i = node_i;
while (current_i >= 0) {
- const GLTFNodeIndex parent_i = state->nodes[current_i]->parent;
+ const GLTFNodeIndex parent_i = p_state->nodes[current_i]->parent;
if (parent_i >= 0) {
++node->height;
}
@@ -667,7 +667,7 @@ void GLTFDocument::_compute_node_heights(Ref<GLTFState> state) {
}
if (node->height == 0) {
- state->root_nodes.push_back(node_i);
+ p_state->root_nodes.push_back(node_i);
}
}
}
@@ -690,86 +690,86 @@ static Vector<uint8_t> _parse_base64_uri(const String &uri) {
return buf;
}
-Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> state, const String &p_path) {
- print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> p_state, const String &p_path) {
+ print_verbose("glTF: Total buffers: " + itos(p_state->buffers.size()));
- if (!state->buffers.size()) {
+ if (!p_state->buffers.size()) {
return OK;
}
Array buffers;
- if (state->buffers.size()) {
- Vector<uint8_t> buffer_data = state->buffers[0];
+ if (p_state->buffers.size()) {
+ Vector<uint8_t> buffer_data = p_state->buffers[0];
Dictionary gltf_buffer;
gltf_buffer["byteLength"] = buffer_data.size();
buffers.push_back(gltf_buffer);
}
- for (GLTFBufferIndex i = 1; i < state->buffers.size() - 1; i++) {
- Vector<uint8_t> buffer_data = state->buffers[i];
+ for (GLTFBufferIndex i = 1; i < p_state->buffers.size() - 1; i++) {
+ Vector<uint8_t> buffer_data = p_state->buffers[i];
Dictionary gltf_buffer;
String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
String path = p_path.get_base_dir() + "/" + filename;
Error err;
- Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &err);
- if (f.is_null()) {
+ Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (file.is_null()) {
return err;
}
if (buffer_data.size() == 0) {
return OK;
}
- f->create(FileAccess::ACCESS_RESOURCES);
- f->store_buffer(buffer_data.ptr(), buffer_data.size());
+ file->create(FileAccess::ACCESS_RESOURCES);
+ file->store_buffer(buffer_data.ptr(), buffer_data.size());
gltf_buffer["uri"] = filename;
gltf_buffer["byteLength"] = buffer_data.size();
buffers.push_back(gltf_buffer);
}
- state->json["buffers"] = buffers;
+ p_state->json["buffers"] = buffers;
return OK;
}
-Error GLTFDocument::_encode_buffer_bins(Ref<GLTFState> state, const String &p_path) {
- print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+Error GLTFDocument::_encode_buffer_bins(Ref<GLTFState> p_state, const String &p_path) {
+ print_verbose("glTF: Total buffers: " + itos(p_state->buffers.size()));
- if (!state->buffers.size()) {
+ if (!p_state->buffers.size()) {
return OK;
}
Array buffers;
- for (GLTFBufferIndex i = 0; i < state->buffers.size(); i++) {
- Vector<uint8_t> buffer_data = state->buffers[i];
+ for (GLTFBufferIndex i = 0; i < p_state->buffers.size(); i++) {
+ Vector<uint8_t> buffer_data = p_state->buffers[i];
Dictionary gltf_buffer;
String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
String path = p_path.get_base_dir() + "/" + filename;
Error err;
- Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &err);
- if (f.is_null()) {
+ Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (file.is_null()) {
return err;
}
if (buffer_data.size() == 0) {
return OK;
}
- f->create(FileAccess::ACCESS_RESOURCES);
- f->store_buffer(buffer_data.ptr(), buffer_data.size());
+ file->create(FileAccess::ACCESS_RESOURCES);
+ file->store_buffer(buffer_data.ptr(), buffer_data.size());
gltf_buffer["uri"] = filename;
gltf_buffer["byteLength"] = buffer_data.size();
buffers.push_back(gltf_buffer);
}
- state->json["buffers"] = buffers;
+ p_state->json["buffers"] = buffers;
return OK;
}
-Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_path) {
- if (!state->json.has("buffers")) {
+Error GLTFDocument::_parse_buffers(Ref<GLTFState> p_state, const String &p_base_path) {
+ if (!p_state->json.has("buffers")) {
return OK;
}
- const Array &buffers = state->json["buffers"];
+ const Array &buffers = p_state->json["buffers"];
for (GLTFBufferIndex i = 0; i < buffers.size(); i++) {
- if (i == 0 && state->glb_data.size()) {
- state->buffers.push_back(state->glb_data);
+ if (i == 0 && p_state->glb_data.size()) {
+ p_state->buffers.push_back(p_state->glb_data);
} else {
const Dictionary &buffer = buffers[i];
@@ -787,30 +787,30 @@ Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_pa
} else { // Relative path to an external image file.
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
uri = uri.uri_decode();
- uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows.
- buffer_data = FileAccess::get_file_as_array(uri);
+ uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
+ buffer_data = FileAccess::get_file_as_bytes(uri);
ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri);
}
ERR_FAIL_COND_V(!buffer.has("byteLength"), ERR_PARSE_ERROR);
int byteLength = buffer["byteLength"];
ERR_FAIL_COND_V(byteLength < buffer_data.size(), ERR_PARSE_ERROR);
- state->buffers.push_back(buffer_data);
+ p_state->buffers.push_back(buffer_data);
}
}
}
- print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+ print_verbose("glTF: Total buffers: " + itos(p_state->buffers.size()));
return OK;
}
-Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) {
+Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> p_state) {
Array buffers;
- for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) {
+ for (GLTFBufferViewIndex i = 0; i < p_state->buffer_views.size(); i++) {
Dictionary d;
- Ref<GLTFBufferView> buffer_view = state->buffer_views[i];
+ Ref<GLTFBufferView> buffer_view = p_state->buffer_views[i];
d["buffer"] = buffer_view->buffer;
d["byteLength"] = buffer_view->byte_length;
@@ -828,19 +828,19 @@ Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) {
ERR_FAIL_COND_V(!d.has("byteLength"), ERR_INVALID_DATA);
buffers.push_back(d);
}
- print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size()));
+ print_verbose("glTF: Total buffer views: " + itos(p_state->buffer_views.size()));
if (!buffers.size()) {
return OK;
}
- state->json["bufferViews"] = buffers;
+ p_state->json["bufferViews"] = buffers;
return OK;
}
-Error GLTFDocument::_parse_buffer_views(Ref<GLTFState> state) {
- if (!state->json.has("bufferViews")) {
+Error GLTFDocument::_parse_buffer_views(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("bufferViews")) {
return OK;
}
- const Array &buffers = state->json["bufferViews"];
+ const Array &buffers = p_state->json["bufferViews"];
for (GLTFBufferViewIndex i = 0; i < buffers.size(); i++) {
const Dictionary &d = buffers[i];
@@ -865,20 +865,20 @@ Error GLTFDocument::_parse_buffer_views(Ref<GLTFState> state) {
buffer_view->indices = target == GLTFDocument::ELEMENT_ARRAY_BUFFER;
}
- state->buffer_views.push_back(buffer_view);
+ p_state->buffer_views.push_back(buffer_view);
}
- print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size()));
+ print_verbose("glTF: Total buffer views: " + itos(p_state->buffer_views.size()));
return OK;
}
-Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) {
+Error GLTFDocument::_encode_accessors(Ref<GLTFState> p_state) {
Array accessors;
- for (GLTFAccessorIndex i = 0; i < state->accessors.size(); i++) {
+ for (GLTFAccessorIndex i = 0; i < p_state->accessors.size(); i++) {
Dictionary d;
- Ref<GLTFAccessor> accessor = state->accessors[i];
+ Ref<GLTFAccessor> accessor = p_state->accessors[i];
d["componentType"] = accessor->component_type;
d["count"] = accessor->count;
d["type"] = _get_accessor_type_name(accessor->type);
@@ -924,9 +924,9 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) {
if (!accessors.size()) {
return OK;
}
- state->json["accessors"] = accessors;
- ERR_FAIL_COND_V(!state->json.has("accessors"), ERR_FILE_CORRUPT);
- print_verbose("glTF: Total accessors: " + itos(state->accessors.size()));
+ p_state->json["accessors"] = accessors;
+ ERR_FAIL_COND_V(!p_state->json.has("accessors"), ERR_FILE_CORRUPT);
+ print_verbose("glTF: Total accessors: " + itos(p_state->accessors.size()));
return OK;
}
@@ -985,11 +985,11 @@ GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
ERR_FAIL_V(GLTFType::TYPE_SCALAR);
}
-Error GLTFDocument::_parse_accessors(Ref<GLTFState> state) {
- if (!state->json.has("accessors")) {
+Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("accessors")) {
return OK;
}
- const Array &accessors = state->json["accessors"];
+ const Array &accessors = p_state->json["accessors"];
for (GLTFAccessorIndex i = 0; i < accessors.size(); i++) {
const Dictionary &d = accessors[i];
@@ -1052,10 +1052,10 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> state) {
}
}
- state->accessors.push_back(accessor);
+ p_state->accessors.push_back(accessor);
}
- print_verbose("glTF: Total accessors: " + itos(state->accessors.size()));
+ print_verbose("glTF: Total accessors: " + itos(p_state->accessors.size()));
return OK;
}
@@ -1100,33 +1100,33 @@ String GLTFDocument::_get_type_name(const GLTFType p_component) {
return names[p_component];
}
-Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src, const int count, const GLTFType type, const int component_type, const bool normalized, const int byte_offset, const bool for_vertex, GLTFBufferViewIndex &r_accessor) {
+Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFType p_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor) {
const int component_count_for_type[7] = {
1, 2, 3, 4, 4, 9, 16
};
- const int component_count = component_count_for_type[type];
- const int component_size = _get_component_type_size(component_type);
+ const int component_count = component_count_for_type[p_type];
+ const int component_size = _get_component_type_size(p_component_type);
ERR_FAIL_COND_V(component_size == 0, FAILED);
int skip_every = 0;
int skip_bytes = 0;
//special case of alignments, as described in spec
- switch (component_type) {
+ switch (p_component_type) {
case COMPONENT_TYPE_BYTE:
case COMPONENT_TYPE_UNSIGNED_BYTE: {
- if (type == TYPE_MAT2) {
+ if (p_type == TYPE_MAT2) {
skip_every = 2;
skip_bytes = 2;
}
- if (type == TYPE_MAT3) {
+ if (p_type == TYPE_MAT3) {
skip_every = 3;
skip_bytes = 1;
}
} break;
case COMPONENT_TYPE_SHORT:
case COMPONENT_TYPE_UNSIGNED_SHORT: {
- if (type == TYPE_MAT3) {
+ if (p_type == TYPE_MAT3) {
skip_every = 6;
skip_bytes = 4;
}
@@ -1137,39 +1137,39 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
Ref<GLTFBufferView> bv;
bv.instantiate();
- const uint32_t offset = bv->byte_offset = byte_offset;
- Vector<uint8_t> &gltf_buffer = state->buffers.write[0];
+ const uint32_t offset = bv->byte_offset = p_byte_offset;
+ Vector<uint8_t> &gltf_buffer = p_state->buffers.write[0];
- int stride = _get_component_type_size(component_type);
- if (for_vertex && stride % 4) {
+ int stride = _get_component_type_size(p_component_type);
+ if (p_for_vertex && stride % 4) {
stride += 4 - (stride % 4); //according to spec must be multiple of 4
}
//use to debug
- print_verbose("glTF: encoding type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count));
+ print_verbose("glTF: encoding type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
- print_verbose("glTF: encoding accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length));
+ print_verbose("glTF: encoding accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length));
- const int buffer_end = (stride * (count - 1)) + _get_component_type_size(component_type);
+ const int buffer_end = (stride * (p_count - 1)) + _get_component_type_size(p_component_type);
// TODO define bv->byte_stride
bv->byte_offset = gltf_buffer.size();
- switch (component_type) {
+ switch (p_component_type) {
case COMPONENT_TYPE_BYTE: {
Vector<int8_t> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
- if (normalized) {
+ double d = *p_src;
+ if (p_normalized) {
buffer.write[dst_i] = d * 128.0;
} else {
buffer.write[dst_i] = d;
}
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1180,20 +1180,20 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
} break;
case COMPONENT_TYPE_UNSIGNED_BYTE: {
Vector<uint8_t> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
- if (normalized) {
+ double d = *p_src;
+ if (p_normalized) {
buffer.write[dst_i] = d * 255.0;
} else {
buffer.write[dst_i] = d;
}
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1202,20 +1202,20 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
} break;
case COMPONENT_TYPE_SHORT: {
Vector<int16_t> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
- if (normalized) {
+ double d = *p_src;
+ if (p_normalized) {
buffer.write[dst_i] = d * 32768.0;
} else {
buffer.write[dst_i] = d;
}
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1226,20 +1226,20 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
} break;
case COMPONENT_TYPE_UNSIGNED_SHORT: {
Vector<uint16_t> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
- if (normalized) {
+ double d = *p_src;
+ if (p_normalized) {
buffer.write[dst_i] = d * 65535.0;
} else {
buffer.write[dst_i] = d;
}
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1250,16 +1250,16 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
} break;
case COMPONENT_TYPE_INT: {
Vector<int> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
+ double d = *p_src;
buffer.write[dst_i] = d;
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1270,16 +1270,16 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
} break;
case COMPONENT_TYPE_FLOAT: {
Vector<float> buffer;
- buffer.resize(count * component_count);
+ buffer.resize(p_count * component_count);
int32_t dst_i = 0;
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
for (int j = 0; j < component_count; j++) {
if (skip_every && j > 0 && (j % skip_every) == 0) {
dst_i += skip_bytes;
}
- double d = *src;
+ double d = *p_src;
buffer.write[dst_i] = d;
- src++;
+ p_src++;
dst_i++;
}
}
@@ -1292,53 +1292,53 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src,
ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA);
ERR_FAIL_COND_V((int)(offset + buffer_end) > gltf_buffer.size(), ERR_INVALID_DATA);
- r_accessor = bv->buffer = state->buffer_views.size();
- state->buffer_views.push_back(bv);
+ r_accessor = bv->buffer = p_state->buffer_views.size();
+ p_state->buffer_views.push_back(bv);
return OK;
}
-Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex) {
- const Ref<GLTFBufferView> bv = state->buffer_views[p_buffer_view];
+Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFType p_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) {
+ const Ref<GLTFBufferView> bv = p_state->buffer_views[p_buffer_view];
- int stride = element_size;
+ int stride = p_element_size;
if (bv->byte_stride != -1) {
stride = bv->byte_stride;
}
- if (for_vertex && stride % 4) {
+ if (p_for_vertex && stride % 4) {
stride += 4 - (stride % 4); //according to spec must be multiple of 4
}
- ERR_FAIL_INDEX_V(bv->buffer, state->buffers.size(), ERR_PARSE_ERROR);
+ ERR_FAIL_INDEX_V(bv->buffer, p_state->buffers.size(), ERR_PARSE_ERROR);
- const uint32_t offset = bv->byte_offset + byte_offset;
- Vector<uint8_t> buffer = state->buffers[bv->buffer]; //copy on write, so no performance hit
+ const uint32_t offset = bv->byte_offset + p_byte_offset;
+ Vector<uint8_t> buffer = p_state->buffers[bv->buffer]; //copy on write, so no performance hit
const uint8_t *bufptr = buffer.ptr();
//use to debug
- print_verbose("glTF: type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count));
- print_verbose("glTF: accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length));
+ print_verbose("glTF: type " + _get_type_name(p_type) + " component type: " + _get_component_type_name(p_component_type) + " stride: " + itos(stride) + " amount " + itos(p_count));
+ print_verbose("glTF: accessor offset " + itos(p_byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length));
- const int buffer_end = (stride * (count - 1)) + element_size;
+ const int buffer_end = (stride * (p_count - 1)) + p_element_size;
ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_PARSE_ERROR);
ERR_FAIL_COND_V((int)(offset + buffer_end) > buffer.size(), ERR_PARSE_ERROR);
//fill everything as doubles
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < p_count; i++) {
const uint8_t *src = &bufptr[offset + i * stride];
- for (int j = 0; j < component_count; j++) {
- if (skip_every && j > 0 && (j % skip_every) == 0) {
- src += skip_bytes;
+ for (int j = 0; j < p_component_count; j++) {
+ if (p_skip_every && j > 0 && (j % p_skip_every) == 0) {
+ src += p_skip_bytes;
}
double d = 0;
- switch (component_type) {
+ switch (p_component_type) {
case COMPONENT_TYPE_BYTE: {
int8_t b = int8_t(*src);
- if (normalized) {
+ if (p_normalized) {
d = (double(b) / 128.0);
} else {
d = double(b);
@@ -1346,7 +1346,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const
} break;
case COMPONENT_TYPE_UNSIGNED_BYTE: {
uint8_t b = *src;
- if (normalized) {
+ if (p_normalized) {
d = (double(b) / 255.0);
} else {
d = double(b);
@@ -1354,7 +1354,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const
} break;
case COMPONENT_TYPE_SHORT: {
int16_t s = *(int16_t *)src;
- if (normalized) {
+ if (p_normalized) {
d = (double(s) / 32768.0);
} else {
d = double(s);
@@ -1362,7 +1362,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const
} break;
case COMPONENT_TYPE_UNSIGNED_SHORT: {
uint16_t s = *(uint16_t *)src;
- if (normalized) {
+ if (p_normalized) {
d = (double(s) / 65535.0);
} else {
d = double(s);
@@ -1376,16 +1376,16 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const
} break;
}
- *dst++ = d;
- src += component_size;
+ *p_dst++ = d;
+ src += p_component_size;
}
}
return OK;
}
-int GLTFDocument::_get_component_type_size(const int component_type) {
- switch (component_type) {
+int GLTFDocument::_get_component_type_size(const int p_component_type) {
+ switch (p_component_type) {
case COMPONENT_TYPE_BYTE:
case COMPONENT_TYPE_UNSIGNED_BYTE:
return 1;
@@ -1405,13 +1405,13 @@ int GLTFDocument::_get_component_type_size(const int component_type) {
return 0;
}
-Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
//spec, for reference:
//https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
- ERR_FAIL_INDEX_V(p_accessor, state->accessors.size(), Vector<double>());
+ ERR_FAIL_INDEX_V(p_accessor, p_state->accessors.size(), Vector<double>());
- const Ref<GLTFAccessor> a = state->accessors[p_accessor];
+ const Ref<GLTFAccessor> a = p_state->accessors[p_accessor];
const int component_count_for_type[7] = {
1, 2, 3, 4, 4, 9, 16
@@ -1456,9 +1456,9 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAc
double *dst = dst_buffer.ptrw();
if (a->buffer_view >= 0) {
- ERR_FAIL_INDEX_V(a->buffer_view, state->buffer_views.size(), Vector<double>());
+ ERR_FAIL_INDEX_V(a->buffer_view, p_state->buffer_views.size(), Vector<double>());
- const Error err = _decode_buffer_view(state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex);
+ const Error err = _decode_buffer_view(p_state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex);
if (err != OK) {
return Vector<double>();
}
@@ -1475,14 +1475,14 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAc
indices.resize(a->sparse_count);
const int indices_component_size = _get_component_type_size(a->sparse_indices_component_type);
- Error err = _decode_buffer_view(state, indices.ptrw(), a->sparse_indices_buffer_view, 0, 0, indices_component_size, a->sparse_count, TYPE_SCALAR, 1, a->sparse_indices_component_type, indices_component_size, false, a->sparse_indices_byte_offset, false);
+ Error err = _decode_buffer_view(p_state, indices.ptrw(), a->sparse_indices_buffer_view, 0, 0, indices_component_size, a->sparse_count, TYPE_SCALAR, 1, a->sparse_indices_component_type, indices_component_size, false, a->sparse_indices_byte_offset, false);
if (err != OK) {
return Vector<double>();
}
Vector<double> data;
data.resize(component_count * a->sparse_count);
- err = _decode_buffer_view(state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex);
+ err = _decode_buffer_view(p_state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex);
if (err != OK) {
return Vector<double>();
}
@@ -1499,7 +1499,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAc
return dst_buffer;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, const Vector<int32_t> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state, const Vector<int32_t> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1532,7 +1532,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, c
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_INT;
@@ -1543,17 +1543,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, c
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<int> ret;
if (attribs.size() == 0) {
@@ -1571,8 +1571,8 @@ Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> state, const G
return ret;
}
-Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<float> ret;
if (attribs.size() == 0) {
@@ -1590,7 +1590,7 @@ Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> state, con
return ret;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, const Vector<Vector2> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> p_state, const Vector<Vector2> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1616,7 +1616,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, c
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC2;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1627,16 +1627,16 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, c
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> p_state, const Vector<Color> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1665,7 +1665,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1676,31 +1676,31 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state,
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-void GLTFDocument::_calc_accessor_min_max(int i, const int element_count, Vector<double> &type_max, Vector<double> attribs, Vector<double> &type_min) {
- if (i == 0) {
- for (int32_t type_i = 0; type_i < element_count; type_i++) {
- type_max.write[type_i] = attribs[(i * element_count) + type_i];
- type_min.write[type_i] = attribs[(i * element_count) + type_i];
+void GLTFDocument::_calc_accessor_min_max(int p_i, const int p_element_count, Vector<double> &p_type_max, Vector<double> p_attribs, Vector<double> &p_type_min) {
+ if (p_i == 0) {
+ for (int32_t type_i = 0; type_i < p_element_count; type_i++) {
+ p_type_max.write[type_i] = p_attribs[(p_i * p_element_count) + type_i];
+ p_type_min.write[type_i] = p_attribs[(p_i * p_element_count) + type_i];
}
}
- for (int32_t type_i = 0; type_i < element_count; type_i++) {
- type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]);
- type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]);
- type_max.write[type_i] = _filter_number(type_max.write[type_i]);
- type_min.write[type_i] = _filter_number(type_min.write[type_i]);
+ for (int32_t type_i = 0; type_i < p_element_count; type_i++) {
+ p_type_max.write[type_i] = MAX(p_attribs[(p_i * p_element_count) + type_i], p_type_max[type_i]);
+ p_type_min.write[type_i] = MIN(p_attribs[(p_i * p_element_count) + type_i], p_type_min[type_i]);
+ p_type_max.write[type_i] = _filter_number(p_type_max.write[type_i]);
+ p_type_min.write[type_i] = _filter_number(p_type_min.write[type_i]);
}
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> p_state, const Vector<Color> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1730,7 +1730,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1741,16 +1741,16 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> p_state, const Vector<Color> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1777,7 +1777,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
@@ -1788,16 +1788,16 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state,
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> state, const Vector<Quaternion> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p_state, const Vector<Quaternion> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1826,7 +1826,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> s
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1837,17 +1837,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> s
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Vector2> ret;
if (attribs.size() == 0) {
@@ -1866,7 +1866,7 @@ Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> state, con
return ret;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state, const Vector<real_t> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_state, const Vector<real_t> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1891,7 +1891,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1902,16 +1902,16 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state,
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, const Vector<Vector3> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state, const Vector<Vector3> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1937,7 +1937,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, c
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -1948,16 +1948,16 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, c
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state, const Vector<Transform3D> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state, const Vector<Transform3D> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -2005,7 +2005,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state,
Ref<GLTFAccessor> accessor;
accessor.instantiate();
GLTFBufferIndex buffer_view_i;
- int64_t size = state->buffers[0].size();
+ int64_t size = p_state->buffers[0].size();
const GLTFType type = GLTFType::TYPE_MAT4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
@@ -2016,17 +2016,17 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state,
accessor->type = type;
accessor->component_type = component_type;
accessor->byte_offset = 0;
- Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ Error err = _encode_buffer_view(p_state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
if (err != OK) {
return -1;
}
accessor->buffer_view = buffer_view_i;
- state->accessors.push_back(accessor);
- return state->accessors.size() - 1;
+ p_state->accessors.push_back(accessor);
+ return p_state->accessors.size() - 1;
}
-Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Vector3> ret;
if (attribs.size() == 0) {
@@ -2045,15 +2045,15 @@ Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> state, con
return ret;
}
-Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Color> ret;
if (attribs.size() == 0) {
return ret;
}
- const int type = state->accessors[p_accessor]->type;
+ const int type = p_state->accessors[p_accessor]->type;
ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret);
int vec_len = 3;
if (type == TYPE_VEC4) {
@@ -2071,8 +2071,8 @@ Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> state, cons
}
return ret;
}
-Vector<Quaternion> GLTFDocument::_decode_accessor_as_quaternion(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Quaternion> GLTFDocument::_decode_accessor_as_quaternion(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Quaternion> ret;
if (attribs.size() == 0) {
@@ -2090,8 +2090,8 @@ Vector<Quaternion> GLTFDocument::_decode_accessor_as_quaternion(Ref<GLTFState> s
}
return ret;
}
-Vector<Transform2D> GLTFDocument::_decode_accessor_as_xform2d(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Transform2D> GLTFDocument::_decode_accessor_as_xform2d(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Transform2D> ret;
if (attribs.size() == 0) {
@@ -2107,8 +2107,8 @@ Vector<Transform2D> GLTFDocument::_decode_accessor_as_xform2d(Ref<GLTFState> sta
return ret;
}
-Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Basis> ret;
if (attribs.size() == 0) {
@@ -2125,8 +2125,8 @@ Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> state, cons
return ret;
}
-Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, p_for_vertex);
Vector<Transform3D> ret;
if (attribs.size() == 0) {
@@ -2144,15 +2144,15 @@ Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> state
return ret;
}
-Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
+Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
Array meshes;
- for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) {
+ for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < p_state->meshes.size(); gltf_mesh_i++) {
print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i));
- Ref<ImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh();
+ Ref<ImporterMesh> import_mesh = p_state->meshes.write[gltf_mesh_i]->get_mesh();
if (import_mesh.is_null()) {
continue;
}
- Array instance_materials = state->meshes.write[gltf_mesh_i]->get_instance_materials();
+ Array instance_materials = p_state->meshes.write[gltf_mesh_i]->get_instance_materials();
Array primitives;
Dictionary gltf_mesh;
Array target_names;
@@ -2205,7 +2205,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
{
Vector<Vector3> a = array[Mesh::ARRAY_VERTEX];
ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA);
- attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true);
+ attributes["POSITION"] = _encode_accessor_as_vec3(p_state, a, true);
vertex_num = a.size();
}
{
@@ -2222,7 +2222,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
out.a = a[(i * 4) + 3];
attribs.write[i] = out;
}
- attributes["TANGENT"] = _encode_accessor_as_color(state, attribs, true);
+ attributes["TANGENT"] = _encode_accessor_as_color(p_state, attribs, true);
}
}
{
@@ -2234,19 +2234,19 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
for (int i = 0; i < ret_size; i++) {
attribs.write[i] = Vector3(a[i]).normalized();
}
- attributes["NORMAL"] = _encode_accessor_as_vec3(state, attribs, true);
+ attributes["NORMAL"] = _encode_accessor_as_vec3(p_state, attribs, true);
}
}
{
Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV];
if (a.size()) {
- attributes["TEXCOORD_0"] = _encode_accessor_as_vec2(state, a, true);
+ attributes["TEXCOORD_0"] = _encode_accessor_as_vec2(p_state, a, true);
}
}
{
Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV2];
if (a.size()) {
- attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true);
+ attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(p_state, a, true);
}
}
for (int custom_i = 0; custom_i < 3; custom_i++) {
@@ -2275,7 +2275,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
if (!attributes.has(gltf_texcoord_key)) {
Vector<Vector2> empty;
empty.resize(vertex_num);
- attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, empty, true);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, empty, true);
}
}
@@ -2296,25 +2296,25 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
}
}
gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
- attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, first_channel, true);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, first_channel, true);
gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
- attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, second_channel, true);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(p_state, second_channel, true);
}
}
{
Vector<Color> a = array[Mesh::ARRAY_COLOR];
if (a.size()) {
- attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true);
+ attributes["COLOR_0"] = _encode_accessor_as_color(p_state, a, true);
}
}
HashMap<int, int> joint_i_to_bone_i;
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
+ for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); node_i++) {
GLTFSkinIndex skin_i = -1;
- if (state->nodes[node_i]->mesh == gltf_mesh_i) {
- skin_i = state->nodes[node_i]->skin;
+ if (p_state->nodes[node_i]->mesh == gltf_mesh_i) {
+ skin_i = p_state->nodes[node_i]->skin;
}
if (skin_i != -1) {
- joint_i_to_bone_i = state->skins[skin_i]->joint_i_to_bone_i;
+ joint_i_to_bone_i = p_state->skins[skin_i]->joint_i_to_bone_i;
break;
}
}
@@ -2334,7 +2334,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
attribs.write[array_i] = Color(joint_0, joint_1, joint_2, joint_3);
}
}
- attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true);
+ attributes["JOINTS_0"] = _encode_accessor_as_joints(p_state, attribs, true);
} else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) {
Vector<Color> joints_0;
joints_0.resize(vertex_num);
@@ -2355,8 +2355,8 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
joint_1.a = a[vertex_i * weights_8_count + 7];
joints_1.write[vertex_i] = joint_1;
}
- attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joints_0, true);
- attributes["JOINTS_1"] = _encode_accessor_as_joints(state, joints_1, true);
+ attributes["JOINTS_0"] = _encode_accessor_as_joints(p_state, joints_0, true);
+ attributes["JOINTS_1"] = _encode_accessor_as_joints(p_state, joints_1, true);
}
}
{
@@ -2369,7 +2369,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
for (int i = 0; i < vertex_count; i++) {
attribs.write[i] = Color(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]);
}
- attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true);
+ attributes["WEIGHTS_0"] = _encode_accessor_as_weights(p_state, attribs, true);
} else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) {
Vector<Color> weights_0;
weights_0.resize(vertex_num);
@@ -2390,8 +2390,8 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
weight_1.a = a[vertex_i * weights_8_count + 7];
weights_1.write[vertex_i] = weight_1;
}
- attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weights_0, true);
- attributes["WEIGHTS_1"] = _encode_accessor_as_weights(state, weights_1, true);
+ attributes["WEIGHTS_0"] = _encode_accessor_as_weights(p_state, weights_0, true);
+ attributes["WEIGHTS_1"] = _encode_accessor_as_weights(p_state, weights_1, true);
}
}
{
@@ -2404,7 +2404,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]);
}
}
- primitive["indices"] = _encode_accessor_as_ints(state, mesh_indices, true);
+ primitive["indices"] = _encode_accessor_as_ints(p_state, mesh_indices, true);
} else {
if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) {
//generate indices because they need to be swapped for CW/CCW
@@ -2423,7 +2423,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
generated_indices.write[k + 2] = k + 1;
}
}
- primitive["indices"] = _encode_accessor_as_ints(state, generated_indices, true);
+ primitive["indices"] = _encode_accessor_as_ints(p_state, generated_indices, true);
}
}
}
@@ -2448,12 +2448,12 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
}
}
- t["POSITION"] = _encode_accessor_as_vec3(state, varr, true);
+ t["POSITION"] = _encode_accessor_as_vec3(p_state, varr, true);
}
Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
if (narr.size()) {
- t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true);
+ t["NORMAL"] = _encode_accessor_as_vec3(p_state, narr, true);
}
Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
if (tarr.size()) {
@@ -2466,7 +2466,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
vec3.y = tarr[(i * 4) + 1];
vec3.z = tarr[(i * 4) + 2];
}
- t["TANGENT"] = _encode_accessor_as_vec3(state, attribs, true);
+ t["TANGENT"] = _encode_accessor_as_vec3(p_state, attribs, true);
}
targets.push_back(t);
}
@@ -2476,19 +2476,19 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
if (surface_i < instance_materials.size()) {
v = instance_materials.get(surface_i);
}
- Ref<BaseMaterial3D> mat = v;
+ Ref<Material> mat = v;
if (!mat.is_valid()) {
mat = import_mesh->get_surface_material(surface_i);
}
if (mat.is_valid()) {
- HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat);
+ HashMap<Ref<Material>, GLTFMaterialIndex>::Iterator material_cache_i = p_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);
+ GLTFMaterialIndex mat_i = p_state->materials.size();
+ p_state->materials.push_back(mat);
primitive["material"] = mat_i;
- state->material_cache.insert(mat, mat_i);
+ p_state->material_cache.insert(mat, mat_i);
}
}
@@ -2505,8 +2505,8 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
weights.resize(target_names.size());
for (int name_i = 0; name_i < target_names.size(); name_i++) {
real_t weight = 0.0;
- if (name_i < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
- weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[name_i];
+ if (name_i < p_state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
+ weight = p_state->meshes.write[gltf_mesh_i]->get_blend_weights()[name_i];
}
weights[name_i] = weight;
}
@@ -2526,18 +2526,18 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
if (!meshes.size()) {
return OK;
}
- state->json["meshes"] = meshes;
+ p_state->json["meshes"] = meshes;
print_verbose("glTF: Total meshes: " + itos(meshes.size()));
return OK;
}
-Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
- if (!state->json.has("meshes")) {
+Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("meshes")) {
return OK;
}
- Array meshes = state->json["meshes"];
+ Array meshes = p_state->json["meshes"];
for (GLTFMeshIndex i = 0; i < meshes.size(); i++) {
print_verbose("glTF: Parsing mesh: " + itos(i));
Dictionary d = meshes[i];
@@ -2556,7 +2556,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
if (d.has("name") && !String(d["name"]).is_empty()) {
mesh_name = d["name"];
}
- import_mesh->set_name(_gen_unique_name(state, vformat("%s_%s", state->scene_name, mesh_name)));
+ import_mesh->set_name(_gen_unique_name(p_state, vformat("%s_%s", p_state->scene_name, mesh_name)));
for (int j = 0; j < primitives.size(); j++) {
uint32_t flags = 0;
@@ -2583,10 +2583,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
Mesh::PRIMITIVE_TRIANGLES, // 4 TRIANGLES
Mesh::PRIMITIVE_TRIANGLE_STRIP, // 5 TRIANGLE_STRIP
Mesh::PRIMITIVE_TRIANGLES, // 6 TRIANGLE_FAN fan not supported, should be converted
-#ifndef _MSC_VER
-#warning line loop and triangle fan are not supported and need to be converted to lines and triangles
-#endif
-
+ // TODO: Line loop and triangle fan are not supported and need to be converted to lines and triangles.
};
primitive = primitives2[mode];
@@ -2595,21 +2592,21 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR);
int32_t vertex_num = 0;
if (a.has("POSITION")) {
- PackedVector3Array vertices = _decode_accessor_as_vec3(state, a["POSITION"], true);
+ PackedVector3Array vertices = _decode_accessor_as_vec3(p_state, a["POSITION"], true);
array[Mesh::ARRAY_VERTEX] = vertices;
vertex_num = vertices.size();
}
if (a.has("NORMAL")) {
- array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true);
+ array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(p_state, a["NORMAL"], true);
}
if (a.has("TANGENT")) {
- array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(state, a["TANGENT"], true);
+ array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(p_state, a["TANGENT"], true);
}
if (a.has("TEXCOORD_0")) {
- array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(state, a["TEXCOORD_0"], true);
+ array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_0"], true);
}
if (a.has("TEXCOORD_1")) {
- array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true);
+ array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(p_state, a["TEXCOORD_1"], true);
}
for (int custom_i = 0; custom_i < 3; custom_i++) {
Vector<float> cur_custom;
@@ -2620,12 +2617,12 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
int num_channels = 0;
if (a.has(gltf_texcoord_key)) {
- texcoord_first = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true);
+ texcoord_first = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true);
num_channels = 2;
}
gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
if (a.has(gltf_texcoord_key)) {
- texcoord_second = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true);
+ texcoord_second = _decode_accessor_as_vec2(p_state, a[gltf_texcoord_key], true);
num_channels = 4;
}
if (!num_channels) {
@@ -2666,15 +2663,15 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
}
if (a.has("COLOR_0")) {
- array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(state, a["COLOR_0"], true);
+ array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(p_state, a["COLOR_0"], true);
has_vertex_color = true;
}
if (a.has("JOINTS_0") && !a.has("JOINTS_1")) {
- array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
+ array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true);
} else if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
- PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
- PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true);
- ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA);
+ PackedInt32Array joints_0 = _decode_accessor_as_ints(p_state, a["JOINTS_0"], true);
+ PackedInt32Array joints_1 = _decode_accessor_as_ints(p_state, a["JOINTS_1"], true);
+ ERR_FAIL_COND_V(joints_0.size() != joints_1.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
Vector<int> joints;
joints.resize(vertex_num * weight_8_count);
@@ -2691,7 +2688,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
array[Mesh::ARRAY_BONES] = joints;
}
if (a.has("WEIGHTS_0") && !a.has("WEIGHTS_1")) {
- Vector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true);
+ Vector<float> weights = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true);
{ //gltf does not seem to normalize the weights for some reason..
int wc = weights.size();
float *w = weights.ptrw();
@@ -2712,8 +2709,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
array[Mesh::ARRAY_WEIGHTS] = weights;
} else if (a.has("WEIGHTS_0") && a.has("WEIGHTS_1")) {
- Vector<float> weights_0 = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true);
- Vector<float> weights_1 = _decode_accessor_as_floats(state, a["WEIGHTS_1"], true);
+ Vector<float> weights_0 = _decode_accessor_as_floats(p_state, a["WEIGHTS_0"], true);
+ Vector<float> weights_1 = _decode_accessor_as_floats(p_state, a["WEIGHTS_1"], true);
Vector<float> weights;
ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
@@ -2758,7 +2755,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
if (p.has("indices")) {
- Vector<int> indices = _decode_accessor_as_ints(state, p["indices"], false);
+ Vector<int> indices = _decode_accessor_as_ints(p_state, p["indices"], false);
if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
//swap around indices, convert ccw to cw for front face
@@ -2817,8 +2814,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
if (j == 0) {
const Array &target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array();
for (int k = 0; k < targets.size(); k++) {
- const String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k);
- import_mesh->add_blend_shape(name);
+ import_mesh->add_blend_shape(k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k));
}
}
@@ -2833,7 +2829,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
if (t.has("POSITION")) {
- Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true);
+ Vector<Vector3> varr = _decode_accessor_as_vec3(p_state, t["POSITION"], true);
const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
const int size = src_varr.size();
ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
@@ -2855,7 +2851,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
array_copy[Mesh::ARRAY_VERTEX] = varr;
}
if (t.has("NORMAL")) {
- Vector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true);
+ Vector<Vector3> narr = _decode_accessor_as_vec3(p_state, t["NORMAL"], true);
const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL];
int size = src_narr.size();
ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
@@ -2877,7 +2873,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
array_copy[Mesh::ARRAY_NORMAL] = narr;
}
if (t.has("TANGENT")) {
- const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true);
+ const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(p_state, t["TANGENT"], true);
const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT];
ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR);
@@ -2933,16 +2929,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
}
- Ref<BaseMaterial3D> mat;
+ Ref<Material> mat;
String mat_name;
- if (!state->discard_meshes_and_materials) {
+ if (!p_state->discard_meshes_and_materials) {
if (p.has("material")) {
const int material = p["material"];
- ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT);
- Ref<BaseMaterial3D> mat3d = state->materials[material];
+ ERR_FAIL_INDEX_V(material, p_state->materials.size(), ERR_FILE_CORRUPT);
+ Ref<Material> mat3d = p_state->materials[material];
ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT);
- if (has_vertex_color) {
- mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+
+ Ref<BaseMaterial3D> base_material = mat3d;
+ if (has_vertex_color && base_material.is_valid()) {
+ base_material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
mat = mat3d;
@@ -2950,7 +2948,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
Ref<StandardMaterial3D> mat3d;
mat3d.instantiate();
if (has_vertex_color) {
- mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ mat3d->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
mat = mat3d;
}
@@ -2979,22 +2977,22 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
mesh->set_blend_weights(blend_weights);
mesh->set_mesh(import_mesh);
- state->meshes.push_back(mesh);
+ p_state->meshes.push_back(mesh);
}
- print_verbose("glTF: Total meshes: " + itos(state->meshes.size()));
+ print_verbose("glTF: Total meshes: " + itos(p_state->meshes.size()));
return OK;
}
-Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path) {
+Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_path) {
Array images;
- for (int i = 0; i < state->images.size(); i++) {
+ for (int i = 0; i < p_state->images.size(); i++) {
Dictionary d;
- ERR_CONTINUE(state->images[i].is_null());
+ ERR_CONTINUE(p_state->images[i].is_null());
- Ref<Image> image = state->images[i]->get_image();
+ Ref<Image> image = p_state->images[i]->get_image();
ERR_CONTINUE(image.is_null());
if (p_path.to_lower().ends_with("glb") || p_path.is_empty()) {
@@ -3005,8 +3003,8 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path
const GLTFBufferIndex bi = 0;
bv->buffer = bi;
- bv->byte_offset = state->buffers[bi].size();
- ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
+ bv->byte_offset = p_state->buffers[bi].size();
+ ERR_FAIL_INDEX_V(bi, p_state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
Vector<uint8_t> buffer;
Ref<ImageTexture> img_tex = image;
@@ -3017,22 +3015,22 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path
ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG.");
bv->byte_length = buffer.size();
- state->buffers.write[bi].resize(state->buffers[bi].size() + bv->byte_length);
- memcpy(&state->buffers.write[bi].write[bv->byte_offset], buffer.ptr(), buffer.size());
- ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT);
+ p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length);
+ memcpy(&p_state->buffers.write[bi].write[bv->byte_offset], buffer.ptr(), buffer.size());
+ ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > p_state->buffers[bi].size(), ERR_FILE_CORRUPT);
- state->buffer_views.push_back(bv);
- bvi = state->buffer_views.size() - 1;
+ p_state->buffer_views.push_back(bv);
+ bvi = p_state->buffer_views.size() - 1;
d["bufferView"] = bvi;
d["mimeType"] = "image/png";
} else {
ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
- String name = state->images[i]->get_name();
- if (name.is_empty()) {
- name = itos(i);
+ String img_name = p_state->images[i]->get_name();
+ if (img_name.is_empty()) {
+ img_name = itos(i);
}
- name = _gen_unique_name(state, name);
- name = name.pad_zeros(3) + ".png";
+ img_name = _gen_unique_name(p_state, img_name);
+ img_name = img_name.pad_zeros(3) + ".png";
String texture_dir = "textures";
String path = p_path.get_base_dir();
String new_texture_dir = path + "/" + texture_dir;
@@ -3040,31 +3038,31 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path
if (!da->dir_exists(new_texture_dir)) {
da->make_dir(new_texture_dir);
}
- image->save_png(new_texture_dir.plus_file(name));
- d["uri"] = texture_dir.plus_file(name).uri_encode();
+ image->save_png(new_texture_dir.path_join(img_name));
+ d["uri"] = texture_dir.path_join(img_name).uri_encode();
}
images.push_back(d);
}
- print_verbose("Total images: " + itos(state->images.size()));
+ print_verbose("Total images: " + itos(p_state->images.size()));
if (!images.size()) {
return OK;
}
- state->json["images"] = images;
+ p_state->json["images"] = images;
return OK;
}
-Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_path) {
- ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER);
- if (!state->json.has("images")) {
+Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_path) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ if (!p_state->json.has("images")) {
return OK;
}
// Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images
- const Array &images = state->json["images"];
+ const Array &images = p_state->json["images"];
for (int i = 0; i < images.size(); i++) {
const Dictionary &d = images[i];
@@ -3102,7 +3100,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
!uri.begins_with("data:image/png;base64") &&
!uri.begins_with("data:image/jpeg;base64")) {
WARN_PRINT(vformat("glTF: Image index '%d' uses an unsupported URI data type: %s. Skipping it.", i, uri));
- state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
continue;
}
data = _parse_base64_uri(uri);
@@ -3119,31 +3117,32 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
} else { // Relative path to an external image file.
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
uri = uri.uri_decode();
- uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows.
+ uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
// ResourceLoader will rely on the file extension to use the relevant loader.
// The spec says that if mimeType is defined, it should take precedence (e.g.
// there could be a `.png` image which is actually JPEG), but there's no easy
// API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
// the material), so we do this only as fallback.
Ref<Texture2D> texture = ResourceLoader::load(uri);
+ String extension = uri.get_extension().to_lower();
if (texture.is_valid()) {
- state->images.push_back(texture);
+ p_state->images.push_back(texture);
continue;
- } else if (mimetype == "image/png" || mimetype == "image/jpeg") {
+ } else if (mimetype == "image/png" || mimetype == "image/jpeg" || extension == "png" || extension == "jpg" || extension == "jpeg") {
// Fallback to loading as byte array.
// This enables us to support the spec's requirement that we honor mimetype
// regardless of file URI.
- data = FileAccess::get_file_as_array(uri);
+ data = FileAccess::get_file_as_bytes(uri);
if (data.size() == 0) {
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s. Skipping it.", i, mimetype, uri));
- state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
continue;
}
data_ptr = data.ptr();
data_size = data.size();
} else {
WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded from URI: %s. Skipping it.", i, uri));
- state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ p_state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
continue;
}
}
@@ -3154,16 +3153,16 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
const GLTFBufferViewIndex bvi = d["bufferView"];
- ERR_FAIL_INDEX_V(bvi, state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR);
+ ERR_FAIL_INDEX_V(bvi, p_state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR);
- Ref<GLTFBufferView> bv = state->buffer_views[bvi];
+ Ref<GLTFBufferView> bv = p_state->buffer_views[bvi];
const GLTFBufferIndex bi = bv->buffer;
- ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
+ ERR_FAIL_INDEX_V(bi, p_state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
- ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > p_state->buffers[bi].size(), ERR_FILE_CORRUPT);
- data_ptr = &state->buffers[bi][bv->byte_offset];
+ data_ptr = &p_state->buffers[bi][bv->byte_offset];
data_size = bv->byte_length;
}
@@ -3196,41 +3195,46 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
// Now we've done our best, fix your scenes.
if (img.is_null()) {
ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype));
- state->images.push_back(Ref<Texture2D>());
+ p_state->images.push_back(Ref<Texture2D>());
continue;
}
- state->images.push_back(ImageTexture::create_from_image(img));
+ p_state->images.push_back(ImageTexture::create_from_image(img));
}
- print_verbose("glTF: Total images: " + itos(state->images.size()));
+ print_verbose("glTF: Total images: " + itos(p_state->images.size()));
return OK;
}
-Error GLTFDocument::_serialize_textures(Ref<GLTFState> state) {
- if (!state->textures.size()) {
+Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) {
+ if (!p_state->textures.size()) {
return OK;
}
Array textures;
- for (int32_t i = 0; i < state->textures.size(); i++) {
+ for (int32_t i = 0; i < p_state->textures.size(); i++) {
Dictionary d;
- Ref<GLTFTexture> t = state->textures[i];
+ Ref<GLTFTexture> t = p_state->textures[i];
ERR_CONTINUE(t->get_src_image() == -1);
d["source"] = t->get_src_image();
+
+ GLTFTextureSamplerIndex sampler_index = t->get_sampler();
+ if (sampler_index != -1) {
+ d["sampler"] = sampler_index;
+ }
textures.push_back(d);
}
- state->json["textures"] = textures;
+ p_state->json["textures"] = textures;
return OK;
}
-Error GLTFDocument::_parse_textures(Ref<GLTFState> state) {
- if (!state->json.has("textures")) {
+Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("textures")) {
return OK;
}
- const Array &textures = state->json["textures"];
+ const Array &textures = p_state->json["textures"];
for (GLTFTextureIndex i = 0; i < textures.size(); i++) {
const Dictionary &d = textures[i];
@@ -3239,221 +3243,327 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> state) {
Ref<GLTFTexture> t;
t.instantiate();
t->set_src_image(d["source"]);
- state->textures.push_back(t);
+ if (d.has("sampler")) {
+ t->set_sampler(d["sampler"]);
+ } else {
+ t->set_sampler(-1);
+ }
+ p_state->textures.push_back(t);
}
return OK;
}
-GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture) {
+GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> p_state, Ref<Texture2D> p_texture, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats) {
ERR_FAIL_COND_V(p_texture.is_null(), -1);
Ref<GLTFTexture> gltf_texture;
gltf_texture.instantiate();
ERR_FAIL_COND_V(p_texture->get_image().is_null(), -1);
- GLTFImageIndex gltf_src_image_i = state->images.size();
- state->images.push_back(p_texture);
+ GLTFImageIndex gltf_src_image_i = p_state->images.size();
+ p_state->images.push_back(p_texture);
gltf_texture->set_src_image(gltf_src_image_i);
- GLTFTextureIndex gltf_texture_i = state->textures.size();
- state->textures.push_back(gltf_texture);
+ gltf_texture->set_sampler(_set_sampler_for_mode(p_state, p_filter_mode, p_repeats));
+ GLTFTextureIndex gltf_texture_i = p_state->textures.size();
+ p_state->textures.push_back(gltf_texture);
return gltf_texture_i;
}
-Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) {
- ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture2D>());
- const GLTFImageIndex image = state->textures[p_texture]->get_src_image();
+Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture) {
+ ERR_FAIL_INDEX_V(p_texture, p_state->textures.size(), Ref<Texture2D>());
+ const GLTFImageIndex image = p_state->textures[p_texture]->get_src_image();
- ERR_FAIL_INDEX_V(image, state->images.size(), Ref<Texture2D>());
+ ERR_FAIL_INDEX_V(image, p_state->images.size(), Ref<Texture2D>());
- return state->images[image];
+ return p_state->images[image];
}
-Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
- Array materials;
- for (int32_t i = 0; i < state->materials.size(); i++) {
+GLTFTextureSamplerIndex GLTFDocument::_set_sampler_for_mode(Ref<GLTFState> p_state, StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats) {
+ for (int i = 0; i < p_state->texture_samplers.size(); ++i) {
+ if (p_state->texture_samplers[i]->get_filter_mode() == p_filter_mode) {
+ return i;
+ }
+ }
+
+ GLTFTextureSamplerIndex gltf_sampler_i = p_state->texture_samplers.size();
+ Ref<GLTFTextureSampler> gltf_sampler;
+ gltf_sampler.instantiate();
+ gltf_sampler->set_filter_mode(p_filter_mode);
+ gltf_sampler->set_wrap_mode(p_repeats);
+ p_state->texture_samplers.push_back(gltf_sampler);
+ return gltf_sampler_i;
+}
+
+Ref<GLTFTextureSampler> GLTFDocument::_get_sampler_for_texture(Ref<GLTFState> p_state, const GLTFTextureIndex p_texture) {
+ ERR_FAIL_INDEX_V(p_texture, p_state->textures.size(), Ref<Texture2D>());
+ const GLTFTextureSamplerIndex sampler = p_state->textures[p_texture]->get_sampler();
+
+ if (sampler == -1) {
+ return p_state->default_texture_sampler;
+ } else {
+ ERR_FAIL_INDEX_V(sampler, p_state->texture_samplers.size(), Ref<GLTFTextureSampler>());
+
+ return p_state->texture_samplers[sampler];
+ }
+}
+
+Error GLTFDocument::_serialize_texture_samplers(Ref<GLTFState> p_state) {
+ if (!p_state->texture_samplers.size()) {
+ return OK;
+ }
+
+ Array samplers;
+ for (int32_t i = 0; i < p_state->texture_samplers.size(); ++i) {
Dictionary d;
+ Ref<GLTFTextureSampler> s = p_state->texture_samplers[i];
+ d["magFilter"] = s->get_mag_filter();
+ d["minFilter"] = s->get_min_filter();
+ d["wrapS"] = s->get_wrap_s();
+ d["wrapT"] = s->get_wrap_t();
+ samplers.push_back(d);
+ }
+ p_state->json["samplers"] = samplers;
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_texture_samplers(Ref<GLTFState> p_state) {
+ p_state->default_texture_sampler.instantiate();
+ p_state->default_texture_sampler->set_min_filter(GLTFTextureSampler::FilterMode::LINEAR_MIPMAP_LINEAR);
+ p_state->default_texture_sampler->set_mag_filter(GLTFTextureSampler::FilterMode::LINEAR);
+ p_state->default_texture_sampler->set_wrap_s(GLTFTextureSampler::WrapMode::REPEAT);
+ p_state->default_texture_sampler->set_wrap_t(GLTFTextureSampler::WrapMode::REPEAT);
+
+ if (!p_state->json.has("samplers")) {
+ return OK;
+ }
+
+ const Array &samplers = p_state->json["samplers"];
+ for (int i = 0; i < samplers.size(); ++i) {
+ const Dictionary &d = samplers[i];
+
+ Ref<GLTFTextureSampler> sampler;
+ sampler.instantiate();
+
+ if (d.has("minFilter")) {
+ sampler->set_min_filter(d["minFilter"]);
+ } else {
+ sampler->set_min_filter(GLTFTextureSampler::FilterMode::LINEAR_MIPMAP_LINEAR);
+ }
+ if (d.has("magFilter")) {
+ sampler->set_mag_filter(d["magFilter"]);
+ } else {
+ sampler->set_mag_filter(GLTFTextureSampler::FilterMode::LINEAR);
+ }
+
+ if (d.has("wrapS")) {
+ sampler->set_wrap_s(d["wrapS"]);
+ } else {
+ sampler->set_wrap_s(GLTFTextureSampler::WrapMode::DEFAULT);
+ }
+
+ if (d.has("wrapT")) {
+ sampler->set_wrap_t(d["wrapT"]);
+ } else {
+ sampler->set_wrap_t(GLTFTextureSampler::WrapMode::DEFAULT);
+ }
+
+ p_state->texture_samplers.push_back(sampler);
+ }
- Ref<BaseMaterial3D> material = state->materials[i];
+ return OK;
+}
+
+Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) {
+ Array materials;
+ for (int32_t i = 0; i < p_state->materials.size(); i++) {
+ Dictionary d;
+ Ref<Material> material = p_state->materials[i];
if (material.is_null()) {
materials.push_back(d);
continue;
}
if (!material->get_name().is_empty()) {
- d["name"] = _gen_unique_name(state, material->get_name());
+ d["name"] = _gen_unique_name(p_state, material->get_name());
}
+
+ Ref<BaseMaterial3D> base_material = material;
+ if (base_material.is_null()) {
+ materials.push_back(d);
+ continue;
+ }
+
+ Dictionary mr;
{
- Dictionary mr;
- {
- Array arr;
- const Color c = material->get_albedo().srgb_to_linear();
- arr.push_back(c.r);
- arr.push_back(c.g);
- arr.push_back(c.b);
- arr.push_back(c.a);
- mr["baseColorFactor"] = arr;
- }
- {
- Dictionary bct;
- Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
- GLTFTextureIndex gltf_texture_index = -1;
+ Array arr;
+ const Color c = base_material->get_albedo().srgb_to_linear();
+ arr.push_back(c.r);
+ arr.push_back(c.g);
+ arr.push_back(c.b);
+ arr.push_back(c.a);
+ mr["baseColorFactor"] = arr;
+ }
+ {
+ Dictionary bct;
+ Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ GLTFTextureIndex gltf_texture_index = -1;
- if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
- albedo_texture->set_name(material->get_name() + "_albedo");
- gltf_texture_index = _set_texture(state, albedo_texture);
- }
- if (gltf_texture_index != -1) {
- bct["index"] = gltf_texture_index;
- Dictionary extensions = _serialize_texture_transform_uv1(material);
- if (!extensions.is_empty()) {
- bct["extensions"] = extensions;
- state->use_khr_texture_transform = true;
- }
- mr["baseColorTexture"] = bct;
- }
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ albedo_texture->set_name(material->get_name() + "_albedo");
+ gltf_texture_index = _set_texture(p_state, albedo_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
}
-
- mr["metallicFactor"] = material->get_metallic();
- mr["roughnessFactor"] = material->get_roughness();
- bool has_roughness = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid();
- bool has_ao = material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid();
- bool has_metalness = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid();
- if (has_ao || has_roughness || has_metalness) {
- Dictionary mrt;
- Ref<Texture2D> roughness_texture = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS);
- BaseMaterial3D::TextureChannel roughness_channel = material->get_roughness_texture_channel();
- Ref<Texture2D> metallic_texture = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC);
- BaseMaterial3D::TextureChannel metalness_channel = material->get_metallic_texture_channel();
- Ref<Texture2D> ao_texture = material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION);
- BaseMaterial3D::TextureChannel ao_channel = material->get_ao_texture_channel();
- Ref<ImageTexture> orm_texture;
- orm_texture.instantiate();
- Ref<Image> orm_image;
- orm_image.instantiate();
- int32_t height = 0;
- int32_t width = 0;
- Ref<Image> ao_image;
- if (has_ao) {
- height = ao_texture->get_height();
- width = ao_texture->get_width();
- ao_image = ao_texture->get_image();
- Ref<ImageTexture> img_tex = ao_image;
- if (img_tex.is_valid()) {
- ao_image = img_tex->get_image();
- }
- if (ao_image->is_compressed()) {
- ao_image->decompress();
- }
+ if (gltf_texture_index != -1) {
+ bct["index"] = gltf_texture_index;
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ bct["extensions"] = extensions;
+ p_state->use_khr_texture_transform = true;
}
- Ref<Image> roughness_image;
- if (has_roughness) {
- height = roughness_texture->get_height();
- width = roughness_texture->get_width();
- roughness_image = roughness_texture->get_image();
- Ref<ImageTexture> img_tex = roughness_image;
- if (img_tex.is_valid()) {
- roughness_image = img_tex->get_image();
- }
- if (roughness_image->is_compressed()) {
- roughness_image->decompress();
- }
+ mr["baseColorTexture"] = bct;
+ }
+ }
+
+ mr["metallicFactor"] = base_material->get_metallic();
+ mr["roughnessFactor"] = base_material->get_roughness();
+ bool has_roughness = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid();
+ bool has_ao = base_material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid();
+ bool has_metalness = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid();
+ if (has_ao || has_roughness || has_metalness) {
+ Dictionary mrt;
+ Ref<Texture2D> roughness_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS);
+ BaseMaterial3D::TextureChannel roughness_channel = base_material->get_roughness_texture_channel();
+ Ref<Texture2D> metallic_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_METALLIC);
+ BaseMaterial3D::TextureChannel metalness_channel = base_material->get_metallic_texture_channel();
+ Ref<Texture2D> ao_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION);
+ BaseMaterial3D::TextureChannel ao_channel = base_material->get_ao_texture_channel();
+ Ref<ImageTexture> orm_texture;
+ orm_texture.instantiate();
+ Ref<Image> orm_image;
+ orm_image.instantiate();
+ int32_t height = 0;
+ int32_t width = 0;
+ Ref<Image> ao_image;
+ if (has_ao) {
+ height = ao_texture->get_height();
+ width = ao_texture->get_width();
+ ao_image = ao_texture->get_image();
+ Ref<ImageTexture> img_tex = ao_image;
+ if (img_tex.is_valid()) {
+ ao_image = img_tex->get_image();
}
- Ref<Image> metallness_image;
- if (has_metalness) {
- height = metallic_texture->get_height();
- width = metallic_texture->get_width();
- metallness_image = metallic_texture->get_image();
- Ref<ImageTexture> img_tex = metallness_image;
- if (img_tex.is_valid()) {
- metallness_image = img_tex->get_image();
- }
- if (metallness_image->is_compressed()) {
- metallness_image->decompress();
- }
+ if (ao_image->is_compressed()) {
+ ao_image->decompress();
}
- Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
- if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
- height = albedo_texture->get_height();
- width = albedo_texture->get_width();
+ }
+ Ref<Image> roughness_image;
+ if (has_roughness) {
+ height = roughness_texture->get_height();
+ width = roughness_texture->get_width();
+ roughness_image = roughness_texture->get_image();
+ Ref<ImageTexture> img_tex = roughness_image;
+ if (img_tex.is_valid()) {
+ roughness_image = img_tex->get_image();
}
- orm_image->create(width, height, false, Image::FORMAT_RGBA8);
- if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) {
- ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ if (roughness_image->is_compressed()) {
+ roughness_image->decompress();
}
- if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) {
- roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ Ref<Image> metallness_image;
+ if (has_metalness) {
+ height = metallic_texture->get_height();
+ width = metallic_texture->get_width();
+ metallness_image = metallic_texture->get_image();
+ Ref<ImageTexture> img_tex = metallness_image;
+ if (img_tex.is_valid()) {
+ metallness_image = img_tex->get_image();
}
- if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) {
- metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ if (metallness_image->is_compressed()) {
+ metallness_image->decompress();
}
- for (int32_t h = 0; h < height; h++) {
- for (int32_t w = 0; w < width; w++) {
- Color c = Color(1.0f, 1.0f, 1.0f);
- if (has_ao) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) {
- c.r = ao_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) {
- c.r = ao_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) {
- c.r = ao_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) {
- c.r = ao_image->get_pixel(w, h).a;
- }
+ }
+ Ref<Texture2D> albedo_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ height = albedo_texture->get_height();
+ width = albedo_texture->get_width();
+ }
+ orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8);
+ if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) {
+ ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) {
+ roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) {
+ metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ for (int32_t h = 0; h < height; h++) {
+ for (int32_t w = 0; w < width; w++) {
+ Color c = Color(1.0f, 1.0f, 1.0f);
+ if (has_ao) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).a;
}
- if (has_roughness) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) {
- c.g = roughness_image->get_pixel(w, h).a;
- }
+ }
+ if (has_roughness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).a;
}
- if (has_metalness) {
- if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).r;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).g;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).b;
- } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) {
- c.b = metallness_image->get_pixel(w, h).a;
- }
+ }
+ if (has_metalness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).a;
}
- orm_image->set_pixel(w, h, c);
}
+ orm_image->set_pixel(w, h, c);
}
- orm_image->generate_mipmaps();
- 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");
- orm_texture_index = _set_texture(state, orm_texture);
- }
- if (has_ao) {
- Dictionary occt;
- occt["index"] = orm_texture_index;
- d["occlusionTexture"] = occt;
- }
- if (has_roughness || has_metalness) {
- mrt["index"] = orm_texture_index;
- Dictionary extensions = _serialize_texture_transform_uv1(material);
- if (!extensions.is_empty()) {
- mrt["extensions"] = extensions;
- state->use_khr_texture_transform = true;
- }
- mr["metallicRoughnessTexture"] = mrt;
+ }
+ orm_image->generate_mipmaps();
+ 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");
+ orm_texture_index = _set_texture(p_state, orm_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
+ }
+ if (has_ao) {
+ Dictionary occt;
+ occt["index"] = orm_texture_index;
+ d["occlusionTexture"] = occt;
+ }
+ if (has_roughness || has_metalness) {
+ mrt["index"] = orm_texture_index;
+ Dictionary extensions = _serialize_texture_transform_uv1(material);
+ if (!extensions.is_empty()) {
+ mrt["extensions"] = extensions;
+ p_state->use_khr_texture_transform = true;
}
+ mr["metallicRoughnessTexture"] = mrt;
}
- d["pbrMetallicRoughness"] = mr;
}
- if (material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) {
+ d["pbrMetallicRoughness"] = mr;
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) {
Dictionary nt;
Ref<ImageTexture> tex;
tex.instantiate();
{
- Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
+ Ref<Texture2D> normal_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
if (normal_texture.is_valid()) {
// Code for uncompressing RG normal maps
Ref<Image> img = normal_texture->get_image();
@@ -3483,30 +3593,31 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
GLTFTextureIndex gltf_texture_index = -1;
if (tex.is_valid() && tex->get_image().is_valid()) {
tex->set_name(material->get_name() + "_normal");
- gltf_texture_index = _set_texture(state, tex);
+ gltf_texture_index = _set_texture(p_state, tex, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
}
- nt["scale"] = material->get_normal_scale();
+ nt["scale"] = base_material->get_normal_scale();
if (gltf_texture_index != -1) {
nt["index"] = gltf_texture_index;
d["normalTexture"] = nt;
}
}
- if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
- const Color c = material->get_emission().linear_to_srgb();
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+ const Color c = base_material->get_emission().linear_to_srgb();
Array arr;
arr.push_back(c.r);
arr.push_back(c.g);
arr.push_back(c.b);
d["emissiveFactor"] = arr;
}
- if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+
+ if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
Dictionary et;
- Ref<Texture2D> emission_texture = material->get_texture(BaseMaterial3D::TEXTURE_EMISSION);
+ Ref<Texture2D> emission_texture = base_material->get_texture(BaseMaterial3D::TEXTURE_EMISSION);
GLTFTextureIndex gltf_texture_index = -1;
if (emission_texture.is_valid() && emission_texture->get_image().is_valid()) {
emission_texture->set_name(material->get_name() + "_emission");
- gltf_texture_index = _set_texture(state, emission_texture);
+ gltf_texture_index = _set_texture(p_state, emission_texture, base_material->get_texture_filter(), base_material->get_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT));
}
if (gltf_texture_index != -1) {
@@ -3514,33 +3625,36 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
d["emissiveTexture"] = et;
}
}
- const bool ds = material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED;
+
+ const bool ds = base_material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED;
if (ds) {
d["doubleSided"] = ds;
}
- if (material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) {
+
+ if (base_material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) {
d["alphaMode"] = "MASK";
- d["alphaCutoff"] = material->get_alpha_scissor_threshold();
- } else if (material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) {
+ d["alphaCutoff"] = base_material->get_alpha_scissor_threshold();
+ } else if (base_material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) {
d["alphaMode"] = "BLEND";
}
+
materials.push_back(d);
}
if (!materials.size()) {
return OK;
}
- state->json["materials"] = materials;
- print_verbose("Total materials: " + itos(state->materials.size()));
+ p_state->json["materials"] = materials;
+ print_verbose("Total materials: " + itos(p_state->materials.size()));
return OK;
}
-Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
- if (!state->json.has("materials")) {
+Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("materials")) {
return OK;
}
- const Array &materials = state->json["materials"];
+ const Array &materials = p_state->json["materials"];
for (GLTFMaterialIndex i = 0; i < materials.size(); i++) {
const Dictionary &d = materials[i];
@@ -3565,7 +3679,12 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (sgm.has("diffuseTexture")) {
const Dictionary &diffuse_texture_dict = sgm["diffuseTexture"];
if (diffuse_texture_dict.has("index")) {
- Ref<Texture2D> diffuse_texture = _get_texture(state, diffuse_texture_dict["index"]);
+ Ref<GLTFTextureSampler> diffuse_sampler = _get_sampler_for_texture(p_state, diffuse_texture_dict["index"]);
+ if (diffuse_sampler.is_valid()) {
+ material->set_texture_filter(diffuse_sampler->get_filter_mode());
+ material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, diffuse_sampler->get_wrap_mode());
+ }
+ Ref<Texture2D> diffuse_texture = _get_texture(p_state, diffuse_texture_dict["index"]);
if (diffuse_texture.is_valid()) {
spec_gloss->diffuse_img = diffuse_texture->get_image();
material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_texture);
@@ -3593,7 +3712,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (sgm.has("specularGlossinessTexture")) {
const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"];
if (spec_gloss_texture.has("index")) {
- const Ref<Texture2D> orig_texture = _get_texture(state, spec_gloss_texture["index"]);
+ const Ref<Texture2D> orig_texture = _get_texture(p_state, spec_gloss_texture["index"]);
if (orig_texture.is_valid()) {
spec_gloss->spec_gloss_img = orig_texture->get_image();
}
@@ -3613,7 +3732,10 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (mr.has("baseColorTexture")) {
const Dictionary &bct = mr["baseColorTexture"];
if (bct.has("index")) {
- material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"]));
+ Ref<GLTFTextureSampler> bct_sampler = _get_sampler_for_texture(p_state, bct["index"]);
+ material->set_texture_filter(bct_sampler->get_filter_mode());
+ material->set_flag(BaseMaterial3D::FLAG_USE_TEXTURE_REPEAT, bct_sampler->get_wrap_mode());
+ material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(p_state, bct["index"]));
}
if (!mr.has("baseColorFactor")) {
material->set_albedo(Color(1, 1, 1));
@@ -3636,7 +3758,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (mr.has("metallicRoughnessTexture")) {
const Dictionary &bct = mr["metallicRoughnessTexture"];
if (bct.has("index")) {
- const Ref<Texture2D> t = _get_texture(state, bct["index"]);
+ const Ref<Texture2D> t = _get_texture(p_state, bct["index"]);
material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, t);
material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE);
material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, t);
@@ -3654,7 +3776,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (d.has("normalTexture")) {
const Dictionary &bct = d["normalTexture"];
if (bct.has("index")) {
- material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(state, bct["index"]));
+ material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"]));
material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true);
}
if (bct.has("scale")) {
@@ -3664,7 +3786,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (d.has("occlusionTexture")) {
const Dictionary &bct = d["occlusionTexture"];
if (bct.has("index")) {
- material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"]));
+ material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"]));
material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED);
material->set_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION, true);
}
@@ -3682,7 +3804,7 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
if (d.has("emissiveTexture")) {
const Dictionary &bct = d["emissiveTexture"];
if (bct.has("index")) {
- material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(state, bct["index"]));
+ material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"]));
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
material->set_emission(Color(0, 0, 0));
}
@@ -3707,48 +3829,54 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
}
}
}
- state->materials.push_back(material);
+ p_state->materials.push_back(material);
}
- print_verbose("Total materials: " + itos(state->materials.size()));
+ print_verbose("Total materials: " + itos(p_state->materials.size()));
return OK;
}
-void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material) {
- if (d.has("extensions")) {
- const Dictionary &extensions = d["extensions"];
+void GLTFDocument::_set_texture_transform_uv1(const Dictionary &p_dict, Ref<BaseMaterial3D> p_material) {
+ if (p_dict.has("extensions")) {
+ const Dictionary &extensions = p_dict["extensions"];
if (extensions.has("KHR_texture_transform")) {
- const Dictionary &texture_transform = extensions["KHR_texture_transform"];
- const Array &offset_arr = texture_transform["offset"];
- if (offset_arr.size() == 2) {
- const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f);
- material->set_uv1_offset(offset_vector3);
- }
+ if (p_material.is_valid()) {
+ const Dictionary &texture_transform = extensions["KHR_texture_transform"];
+ const Array &offset_arr = texture_transform["offset"];
+ if (offset_arr.size() == 2) {
+ const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f);
+ p_material->set_uv1_offset(offset_vector3);
+ }
- const Array &scale_arr = texture_transform["scale"];
- if (scale_arr.size() == 2) {
- const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f);
- material->set_uv1_scale(scale_vector3);
+ const Array &scale_arr = texture_transform["scale"];
+ if (scale_arr.size() == 2) {
+ const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f);
+ p_material->set_uv1_scale(scale_vector3);
+ }
}
}
}
}
void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Ref<BaseMaterial3D> p_material) {
+ if (r_spec_gloss.is_null()) {
+ return;
+ }
if (r_spec_gloss->spec_gloss_img.is_null()) {
return;
}
if (r_spec_gloss->diffuse_img.is_null()) {
return;
}
- Ref<Image> rm_img;
- rm_img.instantiate();
+ if (p_material.is_null()) {
+ return;
+ }
bool has_roughness = false;
bool has_metal = false;
p_material->set_roughness(1.0f);
p_material->set_metallic(1.0f);
- rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8);
+ Ref<Image> rm_img = Image::create_empty(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8);
r_spec_gloss->spec_gloss_img->decompress();
if (r_spec_gloss->diffuse_img.is_valid()) {
r_spec_gloss->diffuse_img->decompress();
@@ -3815,13 +3943,13 @@ void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor
r_base_color = r_base_color.clamp();
}
-GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vector<GLTFNodeIndex> &subset) {
+GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> p_state, const Vector<GLTFNodeIndex> &p_subset) {
int highest = -1;
GLTFNodeIndex best_node = -1;
- for (int i = 0; i < subset.size(); ++i) {
- const GLTFNodeIndex node_i = subset[i];
- const Ref<GLTFNode> node = state->nodes[node_i];
+ for (int i = 0; i < p_subset.size(); ++i) {
+ const GLTFNodeIndex node_i = p_subset[i];
+ const Ref<GLTFNode> node = p_state->nodes[node_i];
if (highest == -1 || node->height < highest) {
highest = node->height;
@@ -3832,38 +3960,38 @@ GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vecto
return best_node;
}
-bool GLTFDocument::_capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin, const GLTFNodeIndex node_index) {
+bool GLTFDocument::_capture_nodes_in_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin, const GLTFNodeIndex p_node_index) {
bool found_joint = false;
- for (int i = 0; i < state->nodes[node_index]->children.size(); ++i) {
- found_joint |= _capture_nodes_in_skin(state, skin, state->nodes[node_index]->children[i]);
+ for (int i = 0; i < p_state->nodes[p_node_index]->children.size(); ++i) {
+ found_joint |= _capture_nodes_in_skin(p_state, p_skin, p_state->nodes[p_node_index]->children[i]);
}
if (found_joint) {
// Mark it if we happen to find another skins joint...
- if (state->nodes[node_index]->joint && skin->joints.find(node_index) < 0) {
- skin->joints.push_back(node_index);
- } else if (skin->non_joints.find(node_index) < 0) {
- skin->non_joints.push_back(node_index);
+ if (p_state->nodes[p_node_index]->joint && p_skin->joints.find(p_node_index) < 0) {
+ p_skin->joints.push_back(p_node_index);
+ } else if (p_skin->non_joints.find(p_node_index) < 0) {
+ p_skin->non_joints.push_back(p_node_index);
}
}
- if (skin->joints.find(node_index) > 0) {
+ if (p_skin->joints.find(p_node_index) > 0) {
return true;
}
return false;
}
-void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
+void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin) {
DisjointSet<GLTFNodeIndex> disjoint_set;
- for (int i = 0; i < skin->joints.size(); ++i) {
- const GLTFNodeIndex node_index = skin->joints[i];
- const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ for (int i = 0; i < p_skin->joints.size(); ++i) {
+ const GLTFNodeIndex node_index = p_skin->joints[i];
+ const GLTFNodeIndex parent = p_state->nodes[node_index]->parent;
disjoint_set.insert(node_index);
- if (skin->joints.find(parent) >= 0) {
+ if (p_skin->joints.find(parent) >= 0) {
disjoint_set.create_union(parent, node_index);
}
}
@@ -3881,8 +4009,8 @@ void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref
for (int i = 0; i < roots.size(); ++i) {
const GLTFNodeIndex root = roots[i];
- if (maxHeight == -1 || state->nodes[root]->height < maxHeight) {
- maxHeight = state->nodes[root]->height;
+ if (maxHeight == -1 || p_state->nodes[root]->height < maxHeight) {
+ maxHeight = p_state->nodes[root]->height;
}
}
@@ -3890,13 +4018,13 @@ void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref
// This sucks, but 99% of all game engines (not just Godot) would have this same issue.
for (int i = 0; i < roots.size(); ++i) {
GLTFNodeIndex current_node = roots[i];
- while (state->nodes[current_node]->height > maxHeight) {
- GLTFNodeIndex parent = state->nodes[current_node]->parent;
+ while (p_state->nodes[current_node]->height > maxHeight) {
+ GLTFNodeIndex parent = p_state->nodes[current_node]->parent;
- if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) {
- skin->joints.push_back(parent);
- } else if (skin->non_joints.find(parent) < 0) {
- skin->non_joints.push_back(parent);
+ if (p_state->nodes[parent]->joint && p_skin->joints.find(parent) < 0) {
+ p_skin->joints.push_back(parent);
+ } else if (p_skin->non_joints.find(parent) < 0) {
+ p_skin->non_joints.push_back(parent);
}
current_node = parent;
@@ -3911,21 +4039,21 @@ void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref
do {
all_same = true;
- const GLTFNodeIndex first_parent = state->nodes[roots[0]]->parent;
+ const GLTFNodeIndex first_parent = p_state->nodes[roots[0]]->parent;
for (int i = 1; i < roots.size(); ++i) {
- all_same &= (first_parent == state->nodes[roots[i]]->parent);
+ all_same &= (first_parent == p_state->nodes[roots[i]]->parent);
}
if (!all_same) {
for (int i = 0; i < roots.size(); ++i) {
const GLTFNodeIndex current_node = roots[i];
- const GLTFNodeIndex parent = state->nodes[current_node]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[current_node]->parent;
- if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) {
- skin->joints.push_back(parent);
- } else if (skin->non_joints.find(parent) < 0) {
- skin->non_joints.push_back(parent);
+ if (p_state->nodes[parent]->joint && p_skin->joints.find(parent) < 0) {
+ p_skin->joints.push_back(parent);
+ } else if (p_skin->non_joints.find(parent) < 0) {
+ p_skin->non_joints.push_back(parent);
}
roots.write[i] = parent;
@@ -3935,19 +4063,19 @@ void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref
} while (!all_same);
}
-Error GLTFDocument::_expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
- _capture_nodes_for_multirooted_skin(state, skin);
+Error GLTFDocument::_expand_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin) {
+ _capture_nodes_for_multirooted_skin(p_state, p_skin);
// Grab all nodes that lay in between skin joints/nodes
DisjointSet<GLTFNodeIndex> disjoint_set;
Vector<GLTFNodeIndex> all_skin_nodes;
- all_skin_nodes.append_array(skin->joints);
- all_skin_nodes.append_array(skin->non_joints);
+ all_skin_nodes.append_array(p_skin->joints);
+ all_skin_nodes.append_array(p_skin->non_joints);
for (int i = 0; i < all_skin_nodes.size(); ++i) {
const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[node_index]->parent;
disjoint_set.insert(node_index);
if (all_skin_nodes.find(parent) >= 0) {
@@ -3964,7 +4092,7 @@ Error GLTFDocument::_expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
Vector<GLTFNodeIndex> set;
disjoint_set.get_members(set, out_owners[i]);
- const GLTFNodeIndex root = _find_highest_node(state, set);
+ const GLTFNodeIndex root = _find_highest_node(p_state, set);
ERR_FAIL_COND_V(root < 0, FAILED);
out_roots.push_back(root);
}
@@ -3972,15 +4100,15 @@ Error GLTFDocument::_expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
out_roots.sort();
for (int i = 0; i < out_roots.size(); ++i) {
- _capture_nodes_in_skin(state, skin, out_roots[i]);
+ _capture_nodes_in_skin(p_state, p_skin, out_roots[i]);
}
- skin->roots = out_roots;
+ p_skin->roots = out_roots;
return OK;
}
-Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
+Error GLTFDocument::_verify_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin) {
// This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is)
// In case additional interpolating logic is added to the skins, this will help ensure that you
// do not cause it to self implode into a fiery blaze
@@ -3992,12 +4120,12 @@ Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
DisjointSet<GLTFNodeIndex> disjoint_set;
Vector<GLTFNodeIndex> all_skin_nodes;
- all_skin_nodes.append_array(skin->joints);
- all_skin_nodes.append_array(skin->non_joints);
+ all_skin_nodes.append_array(p_skin->joints);
+ all_skin_nodes.append_array(p_skin->non_joints);
for (int i = 0; i < all_skin_nodes.size(); ++i) {
const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[node_index]->parent;
disjoint_set.insert(node_index);
if (all_skin_nodes.find(parent) >= 0) {
@@ -4014,7 +4142,7 @@ Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
Vector<GLTFNodeIndex> set;
disjoint_set.get_members(set, out_owners[i]);
- const GLTFNodeIndex root = _find_highest_node(state, set);
+ const GLTFNodeIndex root = _find_highest_node(p_state, set);
ERR_FAIL_COND_V(root < 0, FAILED);
out_roots.push_back(root);
}
@@ -4024,9 +4152,9 @@ Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
ERR_FAIL_COND_V(out_roots.size() == 0, FAILED);
// Make sure the roots are the exact same (they better be)
- ERR_FAIL_COND_V(out_roots.size() != skin->roots.size(), FAILED);
+ ERR_FAIL_COND_V(out_roots.size() != p_skin->roots.size(), FAILED);
for (int i = 0; i < out_roots.size(); ++i) {
- ERR_FAIL_COND_V(out_roots[i] != skin->roots[i], FAILED);
+ ERR_FAIL_COND_V(out_roots[i] != p_skin->roots[i], FAILED);
}
// Single rooted skin? Perfectly ok!
@@ -4035,9 +4163,9 @@ Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
}
// Make sure all parents of a multi-rooted skin are the SAME
- const GLTFNodeIndex parent = state->nodes[out_roots[0]]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[out_roots[0]]->parent;
for (int i = 1; i < out_roots.size(); ++i) {
- if (state->nodes[out_roots[i]]->parent != parent) {
+ if (p_state->nodes[out_roots[i]]->parent != parent) {
return FAILED;
}
}
@@ -4045,12 +4173,12 @@ Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
return OK;
}
-Error GLTFDocument::_parse_skins(Ref<GLTFState> state) {
- if (!state->json.has("skins")) {
+Error GLTFDocument::_parse_skins(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("skins")) {
return OK;
}
- const Array &skins = state->json["skins"];
+ const Array &skins = p_state->json["skins"];
// Create the base skins, and mark nodes that are joints
for (int i = 0; i < skins.size(); i++) {
@@ -4064,18 +4192,18 @@ Error GLTFDocument::_parse_skins(Ref<GLTFState> state) {
const Array &joints = d["joints"];
if (d.has("inverseBindMatrices")) {
- skin->inverse_binds = _decode_accessor_as_xform(state, d["inverseBindMatrices"], false);
+ skin->inverse_binds = _decode_accessor_as_xform(p_state, d["inverseBindMatrices"], false);
ERR_FAIL_COND_V(skin->inverse_binds.size() != joints.size(), ERR_PARSE_ERROR);
}
for (int j = 0; j < joints.size(); j++) {
const GLTFNodeIndex node = joints[j];
- ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR);
+ ERR_FAIL_INDEX_V(node, p_state->nodes.size(), ERR_PARSE_ERROR);
skin->joints.push_back(node);
skin->joints_original.push_back(node);
- state->nodes.write[node]->joint = true;
+ p_state->nodes.write[node]->joint = true;
}
if (d.has("name") && !String(d["name"]).is_empty()) {
@@ -4088,32 +4216,32 @@ Error GLTFDocument::_parse_skins(Ref<GLTFState> state) {
skin->skin_root = d["skeleton"];
}
- state->skins.push_back(skin);
+ p_state->skins.push_back(skin);
}
- for (GLTFSkinIndex i = 0; i < state->skins.size(); ++i) {
- Ref<GLTFSkin> skin = state->skins.write[i];
+ for (GLTFSkinIndex i = 0; i < p_state->skins.size(); ++i) {
+ Ref<GLTFSkin> skin = p_state->skins.write[i];
// Expand the skin to capture all the extra non-joints that lie in between the actual joints,
// and expand the hierarchy to ensure multi-rooted trees lie on the same height level
- ERR_FAIL_COND_V(_expand_skin(state, skin), ERR_PARSE_ERROR);
- ERR_FAIL_COND_V(_verify_skin(state, skin), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(_expand_skin(p_state, skin), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(_verify_skin(p_state, skin), ERR_PARSE_ERROR);
}
- print_verbose("glTF: Total skins: " + itos(state->skins.size()));
+ print_verbose("glTF: Total skins: " + itos(p_state->skins.size()));
return OK;
}
-Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
+Error GLTFDocument::_determine_skeletons(Ref<GLTFState> p_state) {
// Using a disjoint set, we are going to potentially combine all skins that are actually branches
// of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton.
// This is another unclear issue caused by the current glTF specification.
DisjointSet<GLTFNodeIndex> skeleton_sets;
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- const Ref<GLTFSkin> skin = state->skins[skin_i];
+ for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) {
+ const Ref<GLTFSkin> skin = p_state->skins[skin_i];
Vector<GLTFNodeIndex> all_skin_nodes;
all_skin_nodes.append_array(skin->joints);
@@ -4121,7 +4249,7 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
for (int i = 0; i < all_skin_nodes.size(); ++i) {
const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[node_index]->parent;
skeleton_sets.insert(node_index);
if (all_skin_nodes.find(parent) >= 0) {
@@ -4145,7 +4273,7 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
for (int i = 0; i < groups_representatives.size(); ++i) {
Vector<GLTFNodeIndex> group;
skeleton_sets.get_members(group, groups_representatives[i]);
- highest_group_members.push_back(_find_highest_node(state, group));
+ highest_group_members.push_back(_find_highest_node(p_state, group));
groups.push_back(group);
}
@@ -4157,13 +4285,13 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
const GLTFNodeIndex node_j = highest_group_members[j];
// Even if they are siblings under the root! :)
- if (state->nodes[node_i]->parent == state->nodes[node_j]->parent) {
+ if (p_state->nodes[node_i]->parent == p_state->nodes[node_j]->parent) {
skeleton_sets.create_union(node_i, node_j);
}
}
// Attach any parenting going on together (we need to do this n^2 times)
- const GLTFNodeIndex node_i_parent = state->nodes[node_i]->parent;
+ const GLTFNodeIndex node_i_parent = p_state->nodes[node_i]->parent;
if (node_i_parent >= 0) {
for (int j = 0; j < groups.size() && i != j; ++j) {
const Vector<GLTFNodeIndex> &group = groups[j];
@@ -4190,8 +4318,8 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
Vector<GLTFNodeIndex> skeleton_nodes;
skeleton_sets.get_members(skeleton_nodes, skeleton_owner);
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- Ref<GLTFSkin> skin = state->skins.write[skin_i];
+ for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> skin = p_state->skins.write[skin_i];
// If any of the the skeletons nodes exist in a skin, that skin now maps to the skeleton
for (int i = 0; i < skeleton_nodes.size(); ++i) {
@@ -4207,37 +4335,37 @@ Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
for (int i = 0; i < skeleton_nodes.size(); ++i) {
const GLTFNodeIndex node_i = skeleton_nodes[i];
- if (state->nodes[node_i]->joint) {
+ if (p_state->nodes[node_i]->joint) {
skeleton->joints.push_back(node_i);
} else {
non_joints.push_back(node_i);
}
}
- state->skeletons.push_back(skeleton);
+ p_state->skeletons.push_back(skeleton);
- _reparent_non_joint_skeleton_subtrees(state, state->skeletons.write[skel_i], non_joints);
+ _reparent_non_joint_skeleton_subtrees(p_state, p_state->skeletons.write[skel_i], non_joints);
}
- for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) {
- Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i];
+ for (GLTFSkeletonIndex skel_i = 0; skel_i < p_state->skeletons.size(); ++skel_i) {
+ Ref<GLTFSkeleton> skeleton = p_state->skeletons.write[skel_i];
for (int i = 0; i < skeleton->joints.size(); ++i) {
const GLTFNodeIndex node_i = skeleton->joints[i];
- Ref<GLTFNode> node = state->nodes[node_i];
+ Ref<GLTFNode> node = p_state->nodes[node_i];
ERR_FAIL_COND_V(!node->joint, ERR_PARSE_ERROR);
ERR_FAIL_COND_V(node->skeleton >= 0, ERR_PARSE_ERROR);
node->skeleton = skel_i;
}
- ERR_FAIL_COND_V(_determine_skeleton_roots(state, skel_i), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(_determine_skeleton_roots(p_state, skel_i), ERR_PARSE_ERROR);
}
return OK;
}
-Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const Vector<GLTFNodeIndex> &non_joints) {
+Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> p_state, Ref<GLTFSkeleton> p_skeleton, const Vector<GLTFNodeIndex> &p_non_joints) {
DisjointSet<GLTFNodeIndex> subtree_set;
// Populate the disjoint set with ONLY non joints that are in the skeleton hierarchy (non_joints vector)
@@ -4248,13 +4376,13 @@ Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state,
// skinD depicted here explains this issue:
// https://github.com/KhronosGroup/glTF-Asset-Generator/blob/master/Output/Positive/Animation_Skin
- for (int i = 0; i < non_joints.size(); ++i) {
- const GLTFNodeIndex node_i = non_joints[i];
+ for (int i = 0; i < p_non_joints.size(); ++i) {
+ const GLTFNodeIndex node_i = p_non_joints[i];
subtree_set.insert(node_i);
- const GLTFNodeIndex parent_i = state->nodes[node_i]->parent;
- if (parent_i >= 0 && non_joints.find(parent_i) >= 0 && !state->nodes[parent_i]->joint) {
+ const GLTFNodeIndex parent_i = p_state->nodes[node_i]->parent;
+ if (parent_i >= 0 && p_non_joints.find(parent_i) >= 0 && !p_state->nodes[parent_i]->joint) {
subtree_set.create_union(parent_i, node_i);
}
}
@@ -4271,44 +4399,44 @@ Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state,
subtree_set.get_members(subtree_nodes, subtree_root);
for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) {
- Ref<GLTFNode> node = state->nodes[subtree_nodes[subtree_i]];
+ Ref<GLTFNode> node = p_state->nodes[subtree_nodes[subtree_i]];
node->joint = true;
// Add the joint to the skeletons joints
- skeleton->joints.push_back(subtree_nodes[subtree_i]);
+ p_skeleton->joints.push_back(subtree_nodes[subtree_i]);
}
}
return OK;
}
-Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i) {
+Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> p_state, const GLTFSkeletonIndex p_skel_i) {
DisjointSet<GLTFNodeIndex> disjoint_set;
- for (GLTFNodeIndex i = 0; i < state->nodes.size(); ++i) {
- const Ref<GLTFNode> node = state->nodes[i];
+ for (GLTFNodeIndex i = 0; i < p_state->nodes.size(); ++i) {
+ const Ref<GLTFNode> node = p_state->nodes[i];
- if (node->skeleton != skel_i) {
+ if (node->skeleton != p_skel_i) {
continue;
}
disjoint_set.insert(i);
- if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) {
+ if (node->parent >= 0 && p_state->nodes[node->parent]->skeleton == p_skel_i) {
disjoint_set.create_union(node->parent, i);
}
}
- Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i];
+ Ref<GLTFSkeleton> skeleton = p_state->skeletons.write[p_skel_i];
- Vector<GLTFNodeIndex> owners;
- disjoint_set.get_representatives(owners);
+ Vector<GLTFNodeIndex> representatives;
+ disjoint_set.get_representatives(representatives);
Vector<GLTFNodeIndex> roots;
- for (int i = 0; i < owners.size(); ++i) {
+ for (int i = 0; i < representatives.size(); ++i) {
Vector<GLTFNodeIndex> set;
- disjoint_set.get_members(set, owners[i]);
- const GLTFNodeIndex root = _find_highest_node(state, set);
+ disjoint_set.get_members(set, representatives[i]);
+ const GLTFNodeIndex root = _find_highest_node(p_state, set);
ERR_FAIL_COND_V(root < 0, FAILED);
roots.push_back(root);
}
@@ -4324,9 +4452,9 @@ Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSk
}
// Check that the subtrees have the same parent root
- const GLTFNodeIndex parent = state->nodes[roots[0]]->parent;
+ const GLTFNodeIndex parent = p_state->nodes[roots[0]]->parent;
for (int i = 1; i < roots.size(); ++i) {
- if (state->nodes[roots[i]]->parent != parent) {
+ if (p_state->nodes[roots[i]]->parent != parent) {
return FAILED;
}
}
@@ -4334,16 +4462,16 @@ Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSk
return OK;
}
-Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
- for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) {
- Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i];
+Error GLTFDocument::_create_skeletons(Ref<GLTFState> p_state) {
+ for (GLTFSkeletonIndex skel_i = 0; skel_i < p_state->skeletons.size(); ++skel_i) {
+ Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons.write[skel_i];
Skeleton3D *skeleton = memnew(Skeleton3D);
gltf_skeleton->godot_skeleton = skeleton;
- state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i;
+ p_state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i;
// Make a unique name, no gltf node represents this skeleton
- skeleton->set_name(_gen_unique_name(state, "Skeleton3D"));
+ skeleton->set_name(_gen_unique_name(p_state, "Skeleton3D"));
List<GLTFNodeIndex> bones;
@@ -4359,14 +4487,14 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
const GLTFNodeIndex node_i = bones.front()->get();
bones.pop_front();
- Ref<GLTFNode> node = state->nodes[node_i];
+ Ref<GLTFNode> node = p_state->nodes[node_i];
ERR_FAIL_COND_V(node->skeleton != skel_i, FAILED);
{ // Add all child nodes to the stack (deterministically)
Vector<GLTFNodeIndex> child_nodes;
for (int i = 0; i < node->children.size(); ++i) {
const GLTFNodeIndex child_i = node->children[i];
- if (state->nodes[child_i]->skeleton == skel_i) {
+ if (p_state->nodes[child_i]->skeleton == skel_i) {
child_nodes.push_back(child_i);
}
}
@@ -4384,7 +4512,7 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
node->set_name("bone");
}
- node->set_name(_gen_unique_bone_name(state, skel_i, node->get_name()));
+ node->set_name(_gen_unique_bone_name(p_state, skel_i, node->get_name()));
skeleton->add_bone(node->get_name());
skeleton->set_bone_rest(bone_index, node->xform);
@@ -4392,30 +4520,30 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized());
skeleton->set_bone_pose_scale(bone_index, node->scale);
- if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) {
- const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name());
+ if (node->parent >= 0 && p_state->nodes[node->parent]->skeleton == skel_i) {
+ const int bone_parent = skeleton->find_bone(p_state->nodes[node->parent]->get_name());
ERR_FAIL_COND_V(bone_parent < 0, FAILED);
- skeleton->set_bone_parent(bone_index, skeleton->find_bone(state->nodes[node->parent]->get_name()));
+ skeleton->set_bone_parent(bone_index, skeleton->find_bone(p_state->nodes[node->parent]->get_name()));
}
- state->scene_nodes.insert(node_i, skeleton);
+ p_state->scene_nodes.insert(node_i, skeleton);
}
}
- ERR_FAIL_COND_V(_map_skin_joints_indices_to_skeleton_bone_indices(state), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(_map_skin_joints_indices_to_skeleton_bone_indices(p_state), ERR_PARSE_ERROR);
return OK;
}
-Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state) {
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- Ref<GLTFSkin> skin = state->skins.write[skin_i];
+Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> p_state) {
+ for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> skin = p_state->skins.write[skin_i];
- Ref<GLTFSkeleton> skeleton = state->skeletons[skin->skeleton];
+ Ref<GLTFSkeleton> skeleton = p_state->skeletons[skin->skeleton];
for (int joint_index = 0; joint_index < skin->joints_original.size(); ++joint_index) {
const GLTFNodeIndex node_i = skin->joints_original[joint_index];
- const Ref<GLTFNode> node = state->nodes[node_i];
+ const Ref<GLTFNode> node = p_state->nodes[node_i];
const int bone_index = skeleton->godot_skeleton->find_bone(node->get_name());
ERR_FAIL_COND_V(bone_index < 0, FAILED);
@@ -4427,28 +4555,28 @@ Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFSt
return OK;
}
-Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) {
- _remove_duplicate_skins(state);
+Error GLTFDocument::_serialize_skins(Ref<GLTFState> p_state) {
+ _remove_duplicate_skins(p_state);
Array json_skins;
- for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) {
- Ref<GLTFSkin> gltf_skin = state->skins[skin_i];
+ for (int skin_i = 0; skin_i < p_state->skins.size(); skin_i++) {
+ Ref<GLTFSkin> gltf_skin = p_state->skins[skin_i];
Dictionary json_skin;
- json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false);
+ json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(p_state, gltf_skin->inverse_binds, false);
json_skin["joints"] = gltf_skin->get_joints();
json_skin["name"] = gltf_skin->get_name();
json_skins.push_back(json_skin);
}
- if (!state->skins.size()) {
+ if (!p_state->skins.size()) {
return OK;
}
- state->json["skins"] = json_skins;
+ p_state->json["skins"] = json_skins;
return OK;
}
-Error GLTFDocument::_create_skins(Ref<GLTFState> state) {
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- Ref<GLTFSkin> gltf_skin = state->skins.write[skin_i];
+Error GLTFDocument::_create_skins(Ref<GLTFState> p_state) {
+ for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> gltf_skin = p_state->skins.write[skin_i];
Ref<Skin> skin;
skin.instantiate();
@@ -4458,14 +4586,14 @@ Error GLTFDocument::_create_skins(Ref<GLTFState> state) {
for (int joint_i = 0; joint_i < gltf_skin->joints_original.size(); ++joint_i) {
GLTFNodeIndex node = gltf_skin->joints_original[joint_i];
- String bone_name = state->nodes[node]->get_name();
+ String bone_name = p_state->nodes[node]->get_name();
Transform3D xform;
if (has_ibms) {
xform = gltf_skin->inverse_binds[joint_i];
}
- if (state->use_named_skin_binds) {
+ if (p_state->use_named_skin_binds) {
skin->add_named_bind(bone_name, xform);
} else {
int32_t bone_i = gltf_skin->joint_i_to_bone_i[joint_i];
@@ -4477,35 +4605,35 @@ Error GLTFDocument::_create_skins(Ref<GLTFState> state) {
}
// Purge the duplicates!
- _remove_duplicate_skins(state);
+ _remove_duplicate_skins(p_state);
// Create unique names now, after removing duplicates
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- Ref<Skin> skin = state->skins.write[skin_i]->godot_skin;
+ for (GLTFSkinIndex skin_i = 0; skin_i < p_state->skins.size(); ++skin_i) {
+ Ref<Skin> skin = p_state->skins.write[skin_i]->godot_skin;
if (skin->get_name().is_empty()) {
// Make a unique name, no gltf node represents this skin
- skin->set_name(_gen_unique_name(state, "Skin"));
+ skin->set_name(_gen_unique_name(p_state, "Skin"));
}
}
return OK;
}
-bool GLTFDocument::_skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b) {
- if (skin_a->get_bind_count() != skin_b->get_bind_count()) {
+bool GLTFDocument::_skins_are_same(const Ref<Skin> p_skin_a, const Ref<Skin> p_skin_b) {
+ if (p_skin_a->get_bind_count() != p_skin_b->get_bind_count()) {
return false;
}
- for (int i = 0; i < skin_a->get_bind_count(); ++i) {
- if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) {
+ for (int i = 0; i < p_skin_a->get_bind_count(); ++i) {
+ if (p_skin_a->get_bind_bone(i) != p_skin_b->get_bind_bone(i)) {
return false;
}
- if (skin_a->get_bind_name(i) != skin_b->get_bind_name(i)) {
+ if (p_skin_a->get_bind_name(i) != p_skin_b->get_bind_name(i)) {
return false;
}
- Transform3D a_xform = skin_a->get_bind_pose(i);
- Transform3D b_xform = skin_b->get_bind_pose(i);
+ Transform3D a_xform = p_skin_a->get_bind_pose(i);
+ Transform3D b_xform = p_skin_b->get_bind_pose(i);
if (a_xform != b_xform) {
return false;
@@ -4515,109 +4643,67 @@ bool GLTFDocument::_skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_
return true;
}
-void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> state) {
- for (int i = 0; i < state->skins.size(); ++i) {
- for (int j = i + 1; j < state->skins.size(); ++j) {
- const Ref<Skin> skin_i = state->skins[i]->godot_skin;
- const Ref<Skin> skin_j = state->skins[j]->godot_skin;
+void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> p_state) {
+ for (int i = 0; i < p_state->skins.size(); ++i) {
+ for (int j = i + 1; j < p_state->skins.size(); ++j) {
+ const Ref<Skin> skin_i = p_state->skins[i]->godot_skin;
+ const Ref<Skin> skin_j = p_state->skins[j]->godot_skin;
if (_skins_are_same(skin_i, skin_j)) {
// replace it and delete the old
- state->skins.write[j]->godot_skin = skin_i;
+ p_state->skins.write[j]->godot_skin = skin_i;
}
}
}
}
-Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) {
- if (state->lights.is_empty()) {
+Error GLTFDocument::_serialize_lights(Ref<GLTFState> p_state) {
+ if (p_state->lights.is_empty()) {
return OK;
}
Array lights;
- for (GLTFLightIndex i = 0; i < state->lights.size(); i++) {
- Dictionary d;
- Ref<GLTFLight> light = state->lights[i];
- Array color;
- color.resize(3);
- color[0] = light->color.r;
- color[1] = light->color.g;
- color[2] = light->color.b;
- d["color"] = color;
- d["type"] = light->light_type;
- if (light->light_type == "spot") {
- Dictionary s;
- float inner_cone_angle = light->inner_cone_angle;
- s["innerConeAngle"] = inner_cone_angle;
- float outer_cone_angle = light->outer_cone_angle;
- s["outerConeAngle"] = outer_cone_angle;
- d["spot"] = s;
- }
- float intensity = light->intensity;
- d["intensity"] = intensity;
- float range = light->range;
- d["range"] = range;
- lights.push_back(d);
+ for (GLTFLightIndex i = 0; i < p_state->lights.size(); i++) {
+ lights.push_back(p_state->lights[i]->to_dictionary());
}
Dictionary extensions;
- if (state->json.has("extensions")) {
- extensions = state->json["extensions"];
+ if (p_state->json.has("extensions")) {
+ extensions = p_state->json["extensions"];
} else {
- state->json["extensions"] = extensions;
+ p_state->json["extensions"] = extensions;
}
Dictionary lights_punctual;
extensions["KHR_lights_punctual"] = lights_punctual;
lights_punctual["lights"] = lights;
- print_verbose("glTF: Total lights: " + itos(state->lights.size()));
+ print_verbose("glTF: Total lights: " + itos(p_state->lights.size()));
return OK;
}
-Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) {
+Error GLTFDocument::_serialize_cameras(Ref<GLTFState> p_state) {
Array cameras;
- cameras.resize(state->cameras.size());
- for (GLTFCameraIndex i = 0; i < state->cameras.size(); i++) {
- Dictionary d;
+ cameras.resize(p_state->cameras.size());
+ for (GLTFCameraIndex i = 0; i < p_state->cameras.size(); i++) {
+ cameras[i] = p_state->cameras[i]->to_dictionary();
+ }
- Ref<GLTFCamera> camera = state->cameras[i];
-
- if (camera->get_perspective() == false) {
- Dictionary og;
- og["ymag"] = Math::deg2rad(camera->get_fov_size());
- og["xmag"] = Math::deg2rad(camera->get_fov_size());
- og["zfar"] = camera->get_depth_far();
- og["znear"] = camera->get_depth_near();
- d["orthographic"] = og;
- d["type"] = "orthographic";
- } else if (camera->get_perspective()) {
- Dictionary ppt;
- // GLTF spec is in radians, Godot's camera is in degrees.
- ppt["yfov"] = Math::deg2rad(camera->get_fov_size());
- ppt["zfar"] = camera->get_depth_far();
- ppt["znear"] = camera->get_depth_near();
- d["perspective"] = ppt;
- d["type"] = "perspective";
- }
- cameras[i] = d;
- }
-
- if (!state->cameras.size()) {
+ if (!p_state->cameras.size()) {
return OK;
}
- state->json["cameras"] = cameras;
+ p_state->json["cameras"] = cameras;
- print_verbose("glTF: Total cameras: " + itos(state->cameras.size()));
+ print_verbose("glTF: Total cameras: " + itos(p_state->cameras.size()));
return OK;
}
-Error GLTFDocument::_parse_lights(Ref<GLTFState> state) {
- if (!state->json.has("extensions")) {
+Error GLTFDocument::_parse_lights(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("extensions")) {
return OK;
}
- Dictionary extensions = state->json["extensions"];
+ Dictionary extensions = p_state->json["extensions"];
if (!extensions.has("KHR_lights_punctual")) {
return OK;
}
@@ -4629,87 +4715,30 @@ Error GLTFDocument::_parse_lights(Ref<GLTFState> state) {
const Array &lights = lights_punctual["lights"];
for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) {
- const Dictionary &d = lights[light_i];
-
- Ref<GLTFLight> light;
- light.instantiate();
- ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- const String &type = d["type"];
- light->light_type = type;
-
- if (d.has("color")) {
- const Array &arr = d["color"];
- ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
- const Color c = Color(arr[0], arr[1], arr[2]).linear_to_srgb();
- light->color = c;
- }
- if (d.has("intensity")) {
- light->intensity = d["intensity"];
- }
- if (d.has("range")) {
- light->range = d["range"];
- }
- if (type == "spot") {
- const Dictionary &spot = d["spot"];
- light->inner_cone_angle = spot["innerConeAngle"];
- light->outer_cone_angle = spot["outerConeAngle"];
- ERR_CONTINUE_MSG(light->inner_cone_angle >= light->outer_cone_angle, "The inner angle must be smaller than the outer angle.");
- } else if (type != "point" && type != "directional") {
- ERR_CONTINUE_MSG(true, "Light type is unknown.");
+ Ref<GLTFLight> light = GLTFLight::from_dictionary(lights[light_i]);
+ if (light.is_null()) {
+ return Error::ERR_PARSE_ERROR;
}
-
- state->lights.push_back(light);
+ p_state->lights.push_back(light);
}
- print_verbose("glTF: Total lights: " + itos(state->lights.size()));
+ print_verbose("glTF: Total lights: " + itos(p_state->lights.size()));
return OK;
}
-Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) {
- if (!state->json.has("cameras")) {
+Error GLTFDocument::_parse_cameras(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("cameras")) {
return OK;
}
- const Array cameras = state->json["cameras"];
+ const Array cameras = p_state->json["cameras"];
for (GLTFCameraIndex i = 0; i < cameras.size(); i++) {
- const Dictionary &d = cameras[i];
-
- Ref<GLTFCamera> camera;
- camera.instantiate();
- ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- const String &type = d["type"];
- if (type == "orthographic") {
- camera->set_perspective(false);
- if (d.has("orthographic")) {
- const Dictionary &og = d["orthographic"];
- // GLTF spec is in radians, Godot's camera is in degrees.
- camera->set_fov_size(Math::rad2deg(real_t(og["ymag"])));
- camera->set_depth_far(og["zfar"]);
- camera->set_depth_near(og["znear"]);
- } else {
- camera->set_fov_size(10);
- }
- } else if (type == "perspective") {
- camera->set_perspective(true);
- if (d.has("perspective")) {
- const Dictionary &ppt = d["perspective"];
- // GLTF spec is in radians, Godot's camera is in degrees.
- camera->set_fov_size(Math::rad2deg(real_t(ppt["yfov"])));
- camera->set_depth_far(ppt["zfar"]);
- camera->set_depth_near(ppt["znear"]);
- } else {
- camera->set_fov_size(10);
- }
- } else {
- ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera3D should be in 'orthographic' or 'perspective'");
- }
-
- state->cameras.push_back(camera);
+ p_state->cameras.push_back(GLTFCamera::from_dictionary(cameras[i]));
}
- print_verbose("glTF: Total cameras: " + itos(state->cameras.size()));
+ print_verbose("glTF: Total cameras: " + itos(p_state->cameras.size()));
return OK;
}
@@ -4729,24 +4758,24 @@ String GLTFDocument::interpolation_to_string(const GLTFAnimation::Interpolation
return interp;
}
-Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
- if (!state->animation_players.size()) {
+Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
+ if (!p_state->animation_players.size()) {
return OK;
}
- for (int32_t player_i = 0; player_i < state->animation_players.size(); player_i++) {
+ for (int32_t player_i = 0; player_i < p_state->animation_players.size(); player_i++) {
List<StringName> animation_names;
- AnimationPlayer *animation_player = state->animation_players[player_i];
+ AnimationPlayer *animation_player = p_state->animation_players[player_i];
animation_player->get_animation_list(&animation_names);
if (animation_names.size()) {
for (int animation_name_i = 0; animation_name_i < animation_names.size(); animation_name_i++) {
- _convert_animation(state, animation_player, animation_names[animation_name_i]);
+ _convert_animation(p_state, animation_player, animation_names[animation_name_i]);
}
}
}
Array animations;
- for (GLTFAnimationIndex animation_i = 0; animation_i < state->animations.size(); animation_i++) {
+ for (GLTFAnimationIndex animation_i = 0; animation_i < p_state->animations.size(); animation_i++) {
Dictionary d;
- Ref<GLTFAnimation> gltf_animation = state->animations[animation_i];
+ Ref<GLTFAnimation> gltf_animation = p_state->animations[animation_i];
if (!gltf_animation->get_tracks().size()) {
continue;
}
@@ -4766,9 +4795,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
s["interpolation"] = interpolation_to_string(track.position_track.interpolation);
Vector<real_t> times = Variant(track.position_track.times);
- s["input"] = _encode_accessor_as_floats(state, times, false);
+ s["input"] = _encode_accessor_as_floats(p_state, times, false);
Vector<Vector3> values = Variant(track.position_track.values);
- s["output"] = _encode_accessor_as_vec3(state, values, false);
+ s["output"] = _encode_accessor_as_vec3(p_state, values, false);
samplers.push_back(s);
@@ -4786,9 +4815,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation);
Vector<real_t> times = Variant(track.rotation_track.times);
- s["input"] = _encode_accessor_as_floats(state, times, false);
+ s["input"] = _encode_accessor_as_floats(p_state, times, false);
Vector<Quaternion> values = track.rotation_track.values;
- s["output"] = _encode_accessor_as_quaternions(state, values, false);
+ s["output"] = _encode_accessor_as_quaternions(p_state, values, false);
samplers.push_back(s);
@@ -4806,9 +4835,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
s["interpolation"] = interpolation_to_string(track.scale_track.interpolation);
Vector<real_t> times = Variant(track.scale_track.times);
- s["input"] = _encode_accessor_as_floats(state, times, false);
+ s["input"] = _encode_accessor_as_floats(p_state, times, false);
Vector<Vector3> values = Variant(track.scale_track.values);
- s["output"] = _encode_accessor_as_vec3(state, values, false);
+ s["output"] = _encode_accessor_as_vec3(p_state, values, false);
samplers.push_back(s);
@@ -4886,8 +4915,8 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
}
s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation);
- s["input"] = _encode_accessor_as_floats(state, all_track_times, false);
- s["output"] = _encode_accessor_as_floats(state, all_track_values, false);
+ s["input"] = _encode_accessor_as_floats(p_state, all_track_times, false);
+ s["output"] = _encode_accessor_as_floats(p_state, all_track_values, false);
samplers.push_back(s);
@@ -4909,19 +4938,19 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
if (!animations.size()) {
return OK;
}
- state->json["animations"] = animations;
+ p_state->json["animations"] = animations;
- print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'.");
+ print_verbose("glTF: Total animations '" + itos(p_state->animations.size()) + "'.");
return OK;
}
-Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
- if (!state->json.has("animations")) {
+Error GLTFDocument::_parse_animations(Ref<GLTFState> p_state) {
+ if (!p_state->json.has("animations")) {
return OK;
}
- const Array &animations = state->json["animations"];
+ const Array &animations = p_state->json["animations"];
for (GLTFAnimationIndex i = 0; i < animations.size(); i++) {
const Dictionary &d = animations[i];
@@ -4937,11 +4966,12 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
Array samplers = d["samplers"];
if (d.has("name")) {
- const String name = d["name"];
- if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
+ const String anim_name = d["name"];
+ const String anim_name_lower = anim_name.to_lower();
+ if (anim_name_lower.begins_with("loop") || anim_name_lower.ends_with("loop") || anim_name_lower.begins_with("cycle") || anim_name_lower.ends_with("cycle")) {
animation->set_loop(true);
}
- animation->set_name(_gen_unique_animation_name(state, name));
+ animation->set_name(_gen_unique_animation_name(p_state, anim_name));
}
for (int j = 0; j < channels.size(); j++) {
@@ -4962,7 +4992,7 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
GLTFNodeIndex node = t["node"];
String path = t["path"];
- ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR);
+ ERR_FAIL_INDEX_V(node, p_state->nodes.size(), ERR_PARSE_ERROR);
GLTFAnimation::Track *track = nullptr;
@@ -4997,27 +5027,27 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
}
}
- const Vector<float> times = _decode_accessor_as_floats(state, input, false);
+ const Vector<float> times = _decode_accessor_as_floats(p_state, input, false);
if (path == "translation") {
- const Vector<Vector3> positions = _decode_accessor_as_vec3(state, output, false);
+ const Vector<Vector3> positions = _decode_accessor_as_vec3(p_state, output, false);
track->position_track.interpolation = interp;
track->position_track.times = Variant(times); //convert via variant
track->position_track.values = Variant(positions); //convert via variant
} else if (path == "rotation") {
- const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(state, output, false);
+ const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(p_state, output, false);
track->rotation_track.interpolation = interp;
track->rotation_track.times = Variant(times); //convert via variant
track->rotation_track.values = rotations;
} else if (path == "scale") {
- const Vector<Vector3> scales = _decode_accessor_as_vec3(state, output, false);
+ const Vector<Vector3> scales = _decode_accessor_as_vec3(p_state, output, false);
track->scale_track.interpolation = interp;
track->scale_track.times = Variant(times); //convert via variant
track->scale_track.values = Variant(scales); //convert via variant
} else if (path == "weights") {
- const Vector<float> weights = _decode_accessor_as_floats(state, output, false);
+ const Vector<float> weights = _decode_accessor_as_floats(p_state, output, false);
- ERR_FAIL_INDEX_V(state->nodes[node]->mesh, state->meshes.size(), ERR_PARSE_ERROR);
- Ref<GLTFMesh> mesh = state->meshes[state->nodes[node]->mesh];
+ ERR_FAIL_INDEX_V(p_state->nodes[node]->mesh, p_state->meshes.size(), ERR_PARSE_ERROR);
+ Ref<GLTFMesh> mesh = p_state->meshes[p_state->nodes[node]->mesh];
ERR_CONTINUE(!mesh->get_blend_weights().size());
const int wc = mesh->get_blend_weights().size();
@@ -5045,17 +5075,17 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
}
}
- state->animations.push_back(animation);
+ p_state->animations.push_back(animation);
}
- print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'.");
+ print_verbose("glTF: Total animations '" + itos(p_state->animations.size()) + "'.");
return OK;
}
-void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) {
- for (int i = 0; i < state->nodes.size(); i++) {
- Ref<GLTFNode> n = state->nodes[i];
+void GLTFDocument::_assign_scene_names(Ref<GLTFState> p_state) {
+ for (int i = 0; i < p_state->nodes.size(); i++) {
+ Ref<GLTFNode> n = p_state->nodes[i];
// Any joints get unique names generated when the skeleton is made, unique to the skeleton
if (n->skeleton >= 0) {
@@ -5064,21 +5094,21 @@ void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) {
if (n->get_name().is_empty()) {
if (n->mesh >= 0) {
- n->set_name(_gen_unique_name(state, "Mesh"));
+ n->set_name(_gen_unique_name(p_state, "Mesh"));
} else if (n->camera >= 0) {
- n->set_name(_gen_unique_name(state, "Camera3D"));
+ n->set_name(_gen_unique_name(p_state, "Camera3D"));
} else {
- n->set_name(_gen_unique_name(state, "Node"));
+ n->set_name(_gen_unique_name(p_state, "Node"));
}
}
- n->set_name(_gen_unique_name(state, n->get_name()));
+ n->set_name(_gen_unique_name(p_state, n->get_name()));
}
}
-BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
- Ref<GLTFNode> bone_node = state->nodes[bone_index];
+BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> p_state, Skeleton3D *p_skeleton, const GLTFNodeIndex p_node_index, const GLTFNodeIndex p_bone_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
+ Ref<GLTFNode> bone_node = p_state->nodes[p_bone_index];
BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D);
print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name());
@@ -5089,7 +5119,7 @@ BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state,
return bone_attachment;
}
-GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInstance3D *p_mesh_instance) {
+GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> p_state, MeshInstance3D *p_mesh_instance) {
ERR_FAIL_NULL_V(p_mesh_instance, -1);
if (p_mesh_instance->get_mesh().is_null()) {
return -1;
@@ -5106,7 +5136,7 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst
Ref<GLTFMesh> gltf_mesh;
gltf_mesh.instantiate();
- Array instance_materials;
+ TypedArray<Material> instance_materials;
for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) {
Ref<Material> mat = current_mesh->get_surface_material(surface_i);
if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) {
@@ -5120,20 +5150,20 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst
gltf_mesh->set_instance_materials(instance_materials);
gltf_mesh->set_mesh(current_mesh);
gltf_mesh->set_blend_weights(blend_weights);
- GLTFMeshIndex mesh_i = state->meshes.size();
- state->meshes.push_back(gltf_mesh);
+ GLTFMeshIndex mesh_i = p_state->meshes.size();
+ p_state->meshes.push_back(gltf_mesh);
return mesh_i;
}
-ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
- ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr);
+ ERR_FAIL_INDEX_V(gltf_node->mesh, p_state->meshes.size(), nullptr);
ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D);
print_verbose("glTF: Creating mesh for: " + gltf_node->get_name());
- Ref<GLTFMesh> mesh = state->meshes.write[gltf_node->mesh];
+ Ref<GLTFMesh> mesh = p_state->meshes.write[gltf_node->mesh];
if (mesh.is_null()) {
return mi;
}
@@ -5145,140 +5175,64 @@ ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> sta
return mi;
}
-Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+Light3D *GLTFDocument::_generate_light(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
- ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr);
+ ERR_FAIL_INDEX_V(gltf_node->light, p_state->lights.size(), nullptr);
print_verbose("glTF: Creating light for: " + gltf_node->get_name());
- Ref<GLTFLight> l = state->lights[gltf_node->light];
-
- float intensity = l->intensity;
- if (intensity > 10) {
- // GLTF spec has the default around 1, but Blender defaults lights to 100.
- // The only sane way to handle this is to check where it came from and
- // handle it accordingly. If it's over 10, it probably came from Blender.
- intensity /= 100;
- }
-
- if (l->light_type == "directional") {
- DirectionalLight3D *light = memnew(DirectionalLight3D);
- light->set_param(Light3D::PARAM_ENERGY, intensity);
- light->set_color(l->color);
- return light;
- }
-
- const float range = CLAMP(l->range, 0, 4096);
- if (l->light_type == "point") {
- OmniLight3D *light = memnew(OmniLight3D);
- 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_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);
-
- // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
- // The points in desmos are not exact, except for (1, infinity).
- float angle_ratio = l->inner_cone_angle / l->outer_cone_angle;
- float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
- light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
- return light;
- }
- return memnew(Node3D);
+ Ref<GLTFLight> l = p_state->lights[gltf_node->light];
+ return l->to_node();
}
-Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
- ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr);
+ ERR_FAIL_INDEX_V(gltf_node->camera, p_state->cameras.size(), nullptr);
- Camera3D *camera = memnew(Camera3D);
print_verbose("glTF: Creating camera for: " + gltf_node->get_name());
- Ref<GLTFCamera> c = state->cameras[gltf_node->camera];
- if (c->get_perspective()) {
- camera->set_perspective(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
- } else {
- camera->set_orthogonal(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
- }
-
- return camera;
+ Ref<GLTFCamera> c = p_state->cameras[gltf_node->camera];
+ return c->to_node();
}
-GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_camera) {
+GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera) {
print_verbose("glTF: Converting camera: " + p_camera->get_name());
- Ref<GLTFCamera> c;
- c.instantiate();
-
- if (p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE) {
- c->set_perspective(true);
- }
- c->set_fov_size(p_camera->get_fov());
- c->set_depth_far(p_camera->get_far());
- c->set_depth_near(p_camera->get_near());
- GLTFCameraIndex camera_index = state->cameras.size();
- state->cameras.push_back(c);
+ Ref<GLTFCamera> c = GLTFCamera::from_node(p_camera);
+ GLTFCameraIndex camera_index = p_state->cameras.size();
+ p_state->cameras.push_back(c);
return camera_index;
}
-GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_light) {
+GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> p_state, Light3D *p_light) {
print_verbose("glTF: Converting light: " + p_light->get_name());
- Ref<GLTFLight> l;
- l.instantiate();
- l->color = p_light->get_color();
- if (cast_to<DirectionalLight3D>(p_light)) {
- l->light_type = "directional";
- DirectionalLight3D *light = cast_to<DirectionalLight3D>(p_light);
- l->intensity = light->get_param(DirectionalLight3D::PARAM_ENERGY);
- l->range = FLT_MAX; // Range for directional lights is infinite in Godot.
- } else if (cast_to<OmniLight3D>(p_light)) {
- l->light_type = "point";
- OmniLight3D *light = cast_to<OmniLight3D>(p_light);
- l->range = light->get_param(OmniLight3D::PARAM_RANGE);
- 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);
- 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).
- float angle_ratio = 1 - (0.2 / (0.1 + light->get_param(SpotLight3D::PARAM_SPOT_ATTENUATION)));
- angle_ratio = MAX(0, angle_ratio);
- l->inner_cone_angle = l->outer_cone_angle * angle_ratio;
- }
-
- GLTFLightIndex light_index = state->lights.size();
- state->lights.push_back(l);
+ Ref<GLTFLight> l = GLTFLight::from_node(p_light);
+
+ GLTFLightIndex light_index = p_state->lights.size();
+ p_state->lights.push_back(l);
return light_index;
}
-void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node) {
+void GLTFDocument::_convert_spatial(Ref<GLTFState> p_state, Node3D *p_spatial, Ref<GLTFNode> p_node) {
Transform3D xform = p_spatial->get_transform();
p_node->scale = xform.basis.get_scale();
p_node->rotation = xform.basis.get_rotation_quaternion();
p_node->position = xform.origin;
}
-Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
Node3D *spatial = memnew(Node3D);
print_verbose("glTF: Converting spatial: " + gltf_node->get_name());
return spatial;
}
-void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
+
+void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
bool retflag = true;
_check_visibility(p_current, retflag);
if (retflag) {
@@ -5286,64 +5240,68 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, co
}
Ref<GLTFNode> gltf_node;
gltf_node.instantiate();
- gltf_node->set_name(_gen_unique_name(state, p_current->get_name()));
+ gltf_node->set_name(_gen_unique_name(p_state, p_current->get_name()));
if (cast_to<Node3D>(p_current)) {
Node3D *spatial = cast_to<Node3D>(p_current);
- _convert_spatial(state, spatial, gltf_node);
+ _convert_spatial(p_state, spatial, gltf_node);
}
if (cast_to<MeshInstance3D>(p_current)) {
MeshInstance3D *mi = cast_to<MeshInstance3D>(p_current);
- _convert_mesh_instance_to_gltf(mi, state, gltf_node);
+ _convert_mesh_instance_to_gltf(mi, p_state, gltf_node);
} else if (cast_to<BoneAttachment3D>(p_current)) {
BoneAttachment3D *bone = cast_to<BoneAttachment3D>(p_current);
- _convert_bone_attachment_to_gltf(bone, state, p_gltf_parent, p_gltf_root, gltf_node);
+ _convert_bone_attachment_to_gltf(bone, p_state, p_gltf_parent, p_gltf_root, gltf_node);
return;
} else if (cast_to<Skeleton3D>(p_current)) {
Skeleton3D *skel = cast_to<Skeleton3D>(p_current);
- _convert_skeleton_to_gltf(skel, state, p_gltf_parent, p_gltf_root, gltf_node);
+ _convert_skeleton_to_gltf(skel, p_state, p_gltf_parent, p_gltf_root, gltf_node);
// We ignore the Godot Engine node that is the skeleton.
return;
} else if (cast_to<MultiMeshInstance3D>(p_current)) {
MultiMeshInstance3D *multi = cast_to<MultiMeshInstance3D>(p_current);
- _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state);
+ _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, p_state);
#ifdef MODULE_CSG_ENABLED
} else if (cast_to<CSGShape3D>(p_current)) {
CSGShape3D *shape = cast_to<CSGShape3D>(p_current);
if (shape->get_parent() && shape->is_root_shape()) {
- _convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, state);
+ _convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, p_state);
}
#endif // MODULE_CSG_ENABLED
#ifdef MODULE_GRIDMAP_ENABLED
} else if (cast_to<GridMap>(p_current)) {
GridMap *gridmap = Object::cast_to<GridMap>(p_current);
- _convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, state);
+ _convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, p_state);
#endif // MODULE_GRIDMAP_ENABLED
} else if (cast_to<Camera3D>(p_current)) {
Camera3D *camera = Object::cast_to<Camera3D>(p_current);
- _convert_camera_to_gltf(camera, state, gltf_node);
+ _convert_camera_to_gltf(camera, p_state, gltf_node);
} else if (cast_to<Light3D>(p_current)) {
Light3D *light = Object::cast_to<Light3D>(p_current);
- _convert_light_to_gltf(light, state, gltf_node);
+ _convert_light_to_gltf(light, p_state, gltf_node);
} else if (cast_to<AnimationPlayer>(p_current)) {
AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current);
- _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
+ _convert_animation_player_to_gltf(animation_player, p_state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
+ }
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ ext->convert_scene_node(p_state, gltf_node, p_current);
}
- GLTFNodeIndex current_node_i = state->nodes.size();
+ GLTFNodeIndex current_node_i = p_state->nodes.size();
GLTFNodeIndex gltf_root = p_gltf_root;
if (gltf_root == -1) {
gltf_root = current_node_i;
Array scenes;
scenes.push_back(gltf_root);
- state->json["scene"] = scenes;
+ p_state->json["scene"] = scenes;
}
- _create_gltf_node(state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node);
+ _create_gltf_node(p_state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node);
for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) {
- _convert_scene_node(state, p_current->get_child(node_i), current_node_i, gltf_root);
+ _convert_scene_node(p_state, p_current->get_child(node_i), current_node_i, gltf_root);
}
}
#ifdef MODULE_CSG_ENABLED
-void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state) {
CSGShape3D *csg = p_current;
csg->call("_update_shape");
Array meshes = csg->get_meshes();
@@ -5375,34 +5333,34 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd
Ref<GLTFMesh> gltf_mesh;
gltf_mesh.instantiate();
gltf_mesh->set_mesh(mesh);
- GLTFMeshIndex mesh_i = state->meshes.size();
- state->meshes.push_back(gltf_mesh);
- gltf_node->mesh = mesh_i;
- gltf_node->xform = csg->get_meshes()[0];
- gltf_node->set_name(_gen_unique_name(state, csg->get_name()));
+ GLTFMeshIndex mesh_i = p_state->meshes.size();
+ p_state->meshes.push_back(gltf_mesh);
+ p_gltf_node->mesh = mesh_i;
+ p_gltf_node->xform = csg->get_meshes()[0];
+ p_gltf_node->set_name(_gen_unique_name(p_state, csg->get_name()));
}
#endif // MODULE_CSG_ENABLED
-void GLTFDocument::_create_gltf_node(Ref<GLTFState> state, Node *p_scene_parent, GLTFNodeIndex current_node_i,
- GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref<GLTFNode> gltf_node) {
- state->scene_nodes.insert(current_node_i, p_scene_parent);
- state->nodes.push_back(gltf_node);
- ERR_FAIL_COND(current_node_i == p_parent_node_index);
- state->nodes.write[current_node_i]->parent = p_parent_node_index;
+void GLTFDocument::_create_gltf_node(Ref<GLTFState> p_state, Node *p_scene_parent, GLTFNodeIndex p_current_node_i,
+ GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref<GLTFNode> p_gltf_node) {
+ p_state->scene_nodes.insert(p_current_node_i, p_scene_parent);
+ p_state->nodes.push_back(p_gltf_node);
+ ERR_FAIL_COND(p_current_node_i == p_parent_node_index);
+ p_state->nodes.write[p_current_node_i]->parent = p_parent_node_index;
if (p_parent_node_index == -1) {
return;
}
- state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
+ p_state->nodes.write[p_parent_node_index]->children.push_back(p_current_node_i);
}
-void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
- ERR_FAIL_COND(!animation_player);
- state->animation_players.push_back(animation_player);
- print_verbose(String("glTF: Converting animation player: ") + animation_player->get_name());
+void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *p_animation_player, Ref<GLTFState> p_state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
+ ERR_FAIL_COND(!p_animation_player);
+ p_state->animation_players.push_back(p_animation_player);
+ print_verbose(String("glTF: Converting animation player: ") + p_animation_player->get_name());
}
-void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) {
- retflag = true;
+void GLTFDocument::_check_visibility(Node *p_node, bool &r_retflag) {
+ r_retflag = true;
Node3D *spatial = Object::cast_to<Node3D>(p_node);
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
if (node_2d && !node_2d->is_visible()) {
@@ -5411,51 +5369,51 @@ void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) {
if (spatial && !spatial->is_visible()) {
return;
}
- retflag = false;
+ r_retflag = false;
}
-void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
ERR_FAIL_COND(!camera);
- GLTFCameraIndex camera_index = _convert_camera(state, camera);
+ GLTFCameraIndex camera_index = _convert_camera(p_state, camera);
if (camera_index != -1) {
- gltf_node->camera = camera_index;
+ p_gltf_node->camera = camera_index;
}
}
-void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
ERR_FAIL_COND(!light);
- GLTFLightIndex light_index = _convert_light(state, light);
+ GLTFLightIndex light_index = _convert_light(p_state, light);
if (light_index != -1) {
- gltf_node->light = light_index;
+ p_gltf_node->light = light_index;
}
}
#ifdef MODULE_GRIDMAP_ENABLED
-void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state) {
Array cells = p_grid_map->get_used_cells();
for (int32_t k = 0; k < cells.size(); k++) {
GLTFNode *new_gltf_node = memnew(GLTFNode);
- gltf_node->children.push_back(state->nodes.size());
- state->nodes.push_back(new_gltf_node);
+ p_gltf_node->children.push_back(p_state->nodes.size());
+ p_state->nodes.push_back(new_gltf_node);
Vector3 cell_location = cells[k];
int32_t cell = p_grid_map->get_cell_item(
Vector3(cell_location.x, cell_location.y, cell_location.z));
Transform3D cell_xform;
- cell_xform.basis.set_orthogonal_index(
+ cell_xform.basis = p_grid_map->get_basis_with_orthogonal_index(
p_grid_map->get_cell_item_orientation(
Vector3(cell_location.x, cell_location.y, cell_location.z)));
cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(),
p_grid_map->get_cell_scale(),
p_grid_map->get_cell_scale()));
- cell_xform.set_origin(p_grid_map->map_to_world(
+ cell_xform.set_origin(p_grid_map->map_to_local(
Vector3(cell_location.x, cell_location.y, cell_location.z)));
Ref<GLTFMesh> gltf_mesh;
gltf_mesh.instantiate();
gltf_mesh->set_mesh(_mesh_to_importer_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell)));
- new_gltf_node->mesh = state->meshes.size();
- state->meshes.push_back(gltf_mesh);
+ new_gltf_node->mesh = p_state->meshes.size();
+ p_state->meshes.push_back(gltf_mesh);
new_gltf_node->xform = cell_xform * p_grid_map->get_transform();
- new_gltf_node->set_name(_gen_unique_name(state, p_grid_map->get_mesh_library()->get_item_name(cell)));
+ new_gltf_node->set_name(_gen_unique_name(p_state, p_grid_map->get_mesh_library()->get_item_name(cell)));
}
}
#endif // MODULE_GRIDMAP_ENABLED
@@ -5464,7 +5422,7 @@ void GLTFDocument::_convert_multi_mesh_instance_to_gltf(
MultiMeshInstance3D *p_multi_mesh_instance,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
- Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+ Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state) {
ERR_FAIL_COND(!p_multi_mesh_instance);
Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh();
if (multi_mesh.is_null()) {
@@ -5500,8 +5458,8 @@ void GLTFDocument::_convert_multi_mesh_instance_to_gltf(
blend_arrays, mesh->surface_get_lods(surface_i), mat, material_name, mesh->surface_get_format(surface_i));
}
gltf_mesh->set_mesh(importer_mesh);
- GLTFMeshIndex mesh_index = state->meshes.size();
- state->meshes.push_back(gltf_mesh);
+ GLTFMeshIndex mesh_index = p_state->meshes.size();
+ p_state->meshes.push_back(gltf_mesh);
for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
instance_i++) {
Transform3D transform;
@@ -5523,22 +5481,22 @@ void GLTFDocument::_convert_multi_mesh_instance_to_gltf(
new_gltf_node.instantiate();
new_gltf_node->mesh = mesh_index;
new_gltf_node->xform = transform;
- new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name()));
- gltf_node->children.push_back(state->nodes.size());
- state->nodes.push_back(new_gltf_node);
+ new_gltf_node->set_name(_gen_unique_name(p_state, p_multi_mesh_instance->get_name()));
+ p_gltf_node->children.push_back(p_state->nodes.size());
+ p_state->nodes.push_back(new_gltf_node);
}
}
-void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFState> p_state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> p_gltf_node) {
Skeleton3D *skeleton = p_skeleton3d;
Ref<GLTFSkeleton> gltf_skeleton;
gltf_skeleton.instantiate();
- // GLTFSkeleton is only used to hold internal state data. It will not be written to the document.
+ // GLTFSkeleton is only used to hold internal p_state data. It will not be written to the document.
//
gltf_skeleton->godot_skeleton = skeleton;
- GLTFSkeletonIndex skeleton_i = state->skeletons.size();
- state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skeleton_i;
- state->skeletons.push_back(gltf_skeleton);
+ GLTFSkeletonIndex skeleton_i = p_state->skeletons.size();
+ p_state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skeleton_i;
+ p_state->skeletons.push_back(gltf_skeleton);
BoneId bone_count = skeleton->get_bone_count();
for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) {
@@ -5546,15 +5504,15 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFS
joint_node.instantiate();
// Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node
// names to be unique regardless of whether or not they are used as joints.
- joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i)));
+ joint_node->set_name(_gen_unique_name(p_state, skeleton->get_bone_name(bone_i)));
Transform3D xform = skeleton->get_bone_pose(bone_i);
joint_node->scale = xform.basis.get_scale();
joint_node->rotation = xform.basis.get_rotation_quaternion();
joint_node->position = xform.origin;
joint_node->joint = true;
- GLTFNodeIndex current_node_i = state->nodes.size();
- state->scene_nodes.insert(current_node_i, skeleton);
- state->nodes.push_back(joint_node);
+ GLTFNodeIndex current_node_i = p_state->nodes.size();
+ p_state->scene_nodes.insert(current_node_i, skeleton);
+ p_state->nodes.push_back(joint_node);
gltf_skeleton->joints.push_back(current_node_i);
if (skeleton->get_bone_parent(bone_i) == -1) {
@@ -5567,23 +5525,23 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFS
BoneId parent_bone_id = skeleton->get_bone_parent(bone_i);
if (parent_bone_id == -1) {
if (p_parent_node_index != -1) {
- state->nodes.write[current_node_i]->parent = p_parent_node_index;
- state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
+ p_state->nodes.write[current_node_i]->parent = p_parent_node_index;
+ p_state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
}
} else {
GLTFNodeIndex parent_node_i = gltf_skeleton->godot_bone_node[parent_bone_id];
- state->nodes.write[current_node_i]->parent = parent_node_i;
- state->nodes.write[parent_node_i]->children.push_back(current_node_i);
+ p_state->nodes.write[current_node_i]->parent = parent_node_i;
+ p_state->nodes.write[parent_node_i]->children.push_back(current_node_i);
}
}
// Remove placeholder skeleton3d node by not creating the gltf node
// Skins are per mesh
for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) {
- _convert_scene_node(state, skeleton->get_child(node_i), p_parent_node_index, p_root_node_index);
+ _convert_scene_node(p_state, skeleton->get_child(node_i), p_parent_node_index, p_root_node_index);
}
}
-void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment, Ref<GLTFState> p_state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> p_gltf_node) {
Skeleton3D *skeleton;
// Note that relative transforms to external skeletons and pose overrides are not supported.
if (p_bone_attachment->get_use_external_skeleton()) {
@@ -5592,8 +5550,8 @@ void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_att
skeleton = cast_to<Skeleton3D>(p_bone_attachment->get_parent());
}
GLTFSkeletonIndex skel_gltf_i = -1;
- if (skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) {
- skel_gltf_i = state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()];
+ if (skeleton != nullptr && p_state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) {
+ skel_gltf_i = p_state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()];
}
int bone_idx = -1;
if (skeleton != nullptr) {
@@ -5604,28 +5562,28 @@ void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_att
}
GLTFNodeIndex par_node_index = p_parent_node_index;
if (skeleton != nullptr && bone_idx != -1 && skel_gltf_i != -1) {
- Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_gltf_i];
+ Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons.write[skel_gltf_i];
gltf_skeleton->bone_attachments.push_back(p_bone_attachment);
par_node_index = gltf_skeleton->joints[bone_idx];
}
for (int node_i = 0; node_i < p_bone_attachment->get_child_count(); node_i++) {
- _convert_scene_node(state, p_bone_attachment->get_child(node_i), par_node_index, p_root_node_index);
+ _convert_scene_node(p_state, p_bone_attachment->get_child(node_i), par_node_index, p_root_node_index);
}
}
-void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance3D *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
- GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf(state, p_scene_parent);
+void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance3D *p_scene_parent, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node) {
+ GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf(p_state, p_scene_parent);
if (gltf_mesh_index != -1) {
- gltf_node->mesh = gltf_mesh_index;
+ p_gltf_node->mesh = gltf_mesh_index;
}
}
-void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[node_index];
if (gltf_node->skeleton >= 0) {
- _generate_skeleton_bone_node(state, scene_parent, scene_root, node_index);
+ _generate_skeleton_bone_node(p_state, scene_parent, scene_root, node_index);
return;
}
@@ -5639,76 +5597,87 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
// skinned meshes must not be placed in a bone attachment.
if (non_bone_parented_to_skeleton && gltf_node->skin < 0) {
// Bone Attachment - Parent Case
- BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, node_index, gltf_node->parent);
scene_parent->add_child(bone_attachment, true);
bone_attachment->set_owner(scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
- bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+ bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D"));
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
scene_parent = bone_attachment;
}
- if (gltf_node->mesh >= 0) {
- current_node = _generate_mesh_instance(state, node_index);
- } else if (gltf_node->camera >= 0) {
- current_node = _generate_camera(state, node_index);
- } else if (gltf_node->light >= 0) {
- current_node = _generate_light(state, node_index);
+ // Check if any GLTFDocumentExtension classes want to generate a node for us.
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ current_node = ext->generate_scene_node(p_state, gltf_node, scene_parent);
+ if (current_node) {
+ break;
+ }
}
-
- // We still have not managed to make a node.
+ // If none of our GLTFDocumentExtension classes generated us a node, we generate one.
if (!current_node) {
- current_node = _generate_spatial(state, node_index);
+ if (gltf_node->mesh >= 0) {
+ current_node = _generate_mesh_instance(p_state, node_index);
+ } else if (gltf_node->camera >= 0) {
+ current_node = _generate_camera(p_state, node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(p_state, node_index);
+ } else {
+ current_node = _generate_spatial(p_state, node_index);
+ }
}
+ // Add the node we generated and set the owner to the scene root.
scene_parent->add_child(current_node, true);
if (current_node != scene_root) {
- current_node->set_owner(scene_root);
+ Array args;
+ args.append(scene_root);
+ current_node->propagate_call(StringName("set_owner"), args);
}
current_node->set_transform(gltf_node->xform);
current_node->set_name(gltf_node->get_name());
- state->scene_nodes.insert(node_index, current_node);
+ p_state->scene_nodes.insert(node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
- _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
+ _generate_scene_node(p_state, current_node, scene_root, gltf_node->children[i]);
}
}
-void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> gltf_node = state->nodes[node_index];
+void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_scene_parent, Node3D *p_scene_root, const GLTFNodeIndex p_node_index) {
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_node_index];
Node3D *current_node = nullptr;
- Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
+ Skeleton3D *skeleton = p_state->skeletons[gltf_node->skeleton]->godot_skeleton;
// In this case, this node is already a bone in skeleton.
const bool is_skinned_mesh = (gltf_node->skin >= 0 && gltf_node->mesh >= 0);
const bool requires_extra_node = (gltf_node->mesh >= 0 || gltf_node->camera >= 0 || gltf_node->light >= 0);
- Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
+ Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(p_scene_parent);
if (active_skeleton != skeleton) {
if (active_skeleton) {
// Bone Attachment - Direct Parented Skeleton Case
- BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, gltf_node->parent);
- scene_parent->add_child(bone_attachment, true);
- bone_attachment->set_owner(scene_root);
+ p_scene_parent->add_child(bone_attachment, true);
+ bone_attachment->set_owner(p_scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
- bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+ bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D"));
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
- scene_parent = bone_attachment;
- WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", node_index));
+ p_scene_parent = bone_attachment;
+ WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", p_node_index));
}
// Add it to the scene if it has not already been added
if (skeleton->get_parent() == nullptr) {
- scene_parent->add_child(skeleton, true);
- skeleton->set_owner(scene_root);
+ p_scene_parent->add_child(skeleton, true);
+ skeleton->set_owner(p_scene_root);
}
}
@@ -5719,40 +5688,53 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
// skinned meshes must not be placed in a bone attachment.
if (!is_skinned_mesh) {
// Bone Attachment - Same Node Case
- BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index);
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(p_state, active_skeleton, p_node_index, p_node_index);
- scene_parent->add_child(bone_attachment, true);
- bone_attachment->set_owner(scene_root);
+ p_scene_parent->add_child(bone_attachment, true);
+ bone_attachment->set_owner(p_scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
- bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+ bone_attachment->set_name(_gen_unique_name(p_state, "BoneAttachment3D"));
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
- scene_parent = bone_attachment;
+ p_scene_parent = bone_attachment;
}
-
- // We still have not managed to make a node
- if (gltf_node->mesh >= 0) {
- current_node = _generate_mesh_instance(state, node_index);
- } else if (gltf_node->camera >= 0) {
- current_node = _generate_camera(state, node_index);
- } else if (gltf_node->light >= 0) {
- current_node = _generate_light(state, node_index);
+ // Check if any GLTFDocumentExtension classes want to generate a node for us.
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ current_node = ext->generate_scene_node(p_state, gltf_node, p_scene_parent);
+ if (current_node) {
+ break;
+ }
+ }
+ // If none of our GLTFDocumentExtension classes generated us a node, we generate one.
+ if (!current_node) {
+ if (gltf_node->mesh >= 0) {
+ current_node = _generate_mesh_instance(p_state, p_node_index);
+ } else if (gltf_node->camera >= 0) {
+ current_node = _generate_camera(p_state, p_node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(p_state, p_node_index);
+ } else {
+ current_node = _generate_spatial(p_state, p_node_index);
+ }
}
-
- scene_parent->add_child(current_node, true);
- if (current_node != scene_root) {
- current_node->set_owner(scene_root);
+ // Add the node we generated and set the owner to the scene root.
+ p_scene_parent->add_child(current_node, true);
+ if (current_node != p_scene_root) {
+ Array args;
+ args.append(p_scene_root);
+ current_node->propagate_call(StringName("set_owner"), args);
}
// Do not set transform here. Transform is already applied to our bone.
current_node->set_name(gltf_node->get_name());
}
- state->scene_nodes.insert(node_index, current_node);
+ p_state->scene_nodes.insert(p_node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
- _generate_scene_node(state, active_skeleton, scene_root, gltf_node->children[i]);
+ _generate_scene_node(p_state, active_skeleton, p_scene_root, gltf_node->children[i]);
}
}
@@ -5877,24 +5859,25 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T
ERR_FAIL_V(p_values[0]);
}
-void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) {
- Ref<GLTFAnimation> anim = state->animations[index];
+void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming) {
+ Ref<GLTFAnimation> anim = p_state->animations[p_index];
- String name = anim->get_name();
- if (name.is_empty()) {
+ String anim_name = anim->get_name();
+ if (anim_name.is_empty()) {
// No node represent these, and they are not in the hierarchy, so just make a unique name
- name = _gen_unique_name(state, "Animation");
+ anim_name = _gen_unique_name(p_state, "Animation");
}
Ref<Animation> animation;
animation.instantiate();
- animation->set_name(name);
+ animation->set_name(anim_name);
if (anim->get_loop()) {
animation->set_loop_mode(Animation::LOOP_LINEAR);
}
- float length = 0.0;
+ double anim_start = p_trimming ? INFINITY : 0.0;
+ double anim_end = 0.0;
for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) {
const GLTFAnimation::Track &track = track_i.value;
@@ -5905,38 +5888,59 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
GLTFNodeIndex node_index = track_i.key;
- const Ref<GLTFNode> gltf_node = state->nodes[track_i.key];
+ const Ref<GLTFNode> gltf_node = p_state->nodes[track_i.key];
- Node *root = ap->get_parent();
+ Node *root = p_animation_player->get_parent();
ERR_FAIL_COND(root == nullptr);
- HashMap<GLTFNodeIndex, Node *>::Iterator node_element = state->scene_nodes.find(node_index);
+ HashMap<GLTFNodeIndex, Node *>::Iterator node_element = p_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;
+ const Skeleton3D *sk = p_state->skeletons[gltf_node->skeleton]->godot_skeleton;
ERR_FAIL_COND(sk == nullptr);
- const String path = ap->get_parent()->get_path_to(sk);
+ const String path = p_animation_player->get_parent()->get_path_to(sk);
const String bone = gltf_node->get_name();
transform_node_path = path + ":" + bone;
} else {
transform_node_path = node_path;
}
- for (int i = 0; i < track.rotation_track.times.size(); i++) {
- length = MAX(length, track.rotation_track.times[i]);
- }
- for (int i = 0; i < track.position_track.times.size(); i++) {
- length = MAX(length, track.position_track.times[i]);
- }
- for (int i = 0; i < track.scale_track.times.size(); i++) {
- length = MAX(length, track.scale_track.times[i]);
- }
-
- for (int i = 0; i < track.weight_tracks.size(); i++) {
- for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
- length = MAX(length, track.weight_tracks[i].times[j]);
+ if (p_trimming) {
+ for (int i = 0; i < track.rotation_track.times.size(); i++) {
+ anim_start = MIN(anim_start, track.rotation_track.times[i]);
+ anim_end = MAX(anim_end, track.rotation_track.times[i]);
+ }
+ for (int i = 0; i < track.position_track.times.size(); i++) {
+ anim_start = MIN(anim_start, track.position_track.times[i]);
+ anim_end = MAX(anim_end, track.position_track.times[i]);
+ }
+ for (int i = 0; i < track.scale_track.times.size(); i++) {
+ anim_start = MIN(anim_start, track.scale_track.times[i]);
+ anim_end = MAX(anim_end, track.scale_track.times[i]);
+ }
+ for (int i = 0; i < track.weight_tracks.size(); i++) {
+ for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
+ anim_start = MIN(anim_start, track.weight_tracks[i].times[j]);
+ anim_end = MAX(anim_end, track.weight_tracks[i].times[j]);
+ }
+ }
+ } else {
+ // If you don't use trimming and the first key time is not at 0.0, fake keys will be inserted.
+ for (int i = 0; i < track.rotation_track.times.size(); i++) {
+ anim_end = MAX(anim_end, track.rotation_track.times[i]);
+ }
+ for (int i = 0; i < track.position_track.times.size(); i++) {
+ anim_end = MAX(anim_end, track.position_track.times[i]);
+ }
+ for (int i = 0; i < track.scale_track.times.size(); i++) {
+ anim_end = MAX(anim_end, track.scale_track.times[i]);
+ }
+ for (int i = 0; i < track.weight_tracks.size(); i++) {
+ for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
+ anim_end = MAX(anim_end, track.weight_tracks[i].times[j]);
+ }
}
}
@@ -5950,7 +5954,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
int scale_idx = -1;
if (track.position_track.values.size()) {
- Vector3 base_pos = state->nodes[track_i.key]->position;
+ Vector3 base_pos = p_state->nodes[track_i.key]->position;
bool not_default = false; //discard the track if all it contains is default values
for (int i = 0; i < track.position_track.times.size(); i++) {
Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
@@ -5969,7 +5973,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
if (track.rotation_track.values.size()) {
- Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized();
+ Quaternion base_rot = p_state->nodes[track_i.key]->rotation.normalized();
bool not_default = false; //discard the track if all it contains is default values
for (int i = 0; i < track.rotation_track.times.size(); i++) {
Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
@@ -5987,7 +5991,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
if (track.scale_track.values.size()) {
- Vector3 base_scale = state->nodes[track_i.key]->scale;
+ Vector3 base_scale = p_state->nodes[track_i.key]->scale;
bool not_default = false; //discard the track if all it contains is default values
for (int i = 0; i < track.scale_track.times.size(); i++) {
Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
@@ -6005,25 +6009,23 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
- //first determine animation length
-
- const double increment = 1.0 / bake_fps;
- double time = 0.0;
+ const double increment = 1.0 / p_bake_fps;
+ double time = anim_start;
Vector3 base_pos;
Quaternion base_rot;
Vector3 base_scale = Vector3(1, 1, 1);
if (rotation_idx == -1) {
- base_rot = state->nodes[track_i.key]->rotation.normalized();
+ base_rot = p_state->nodes[track_i.key]->rotation.normalized();
}
if (position_idx == -1) {
- base_pos = state->nodes[track_i.key]->position;
+ base_pos = p_state->nodes[track_i.key]->position;
}
if (scale_idx == -1) {
- base_scale = state->nodes[track_i.key]->scale;
+ base_scale = p_state->nodes[track_i.key]->scale;
}
bool last = false;
@@ -6034,33 +6036,33 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (position_idx >= 0) {
pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
- animation->position_track_insert_key(position_idx, time, pos);
+ animation->position_track_insert_key(position_idx, time - anim_start, pos);
}
if (rotation_idx >= 0) {
rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
- animation->rotation_track_insert_key(rotation_idx, time, rot);
+ animation->rotation_track_insert_key(rotation_idx, time - anim_start, rot);
}
if (scale_idx >= 0) {
scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
- animation->scale_track_insert_key(scale_idx, time, scale);
+ animation->scale_track_insert_key(scale_idx, time - anim_start, scale);
}
if (last) {
break;
}
time += increment;
- if (time >= length) {
+ if (time >= anim_end) {
last = true;
- time = length;
+ time = anim_end;
}
}
}
for (int i = 0; i < track.weight_tracks.size(); i++) {
- ERR_CONTINUE(gltf_node->mesh < 0 || gltf_node->mesh >= state->meshes.size());
- Ref<GLTFMesh> mesh = state->meshes[gltf_node->mesh];
+ ERR_CONTINUE(gltf_node->mesh < 0 || gltf_node->mesh >= p_state->meshes.size());
+ Ref<GLTFMesh> mesh = p_state->meshes[gltf_node->mesh];
ERR_CONTINUE(mesh.is_null());
ERR_CONTINUE(mesh->get_mesh().is_null());
ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null());
@@ -6083,45 +6085,45 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
} else {
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / bake_fps;
+ const double increment = 1.0 / p_bake_fps;
double time = 0.0;
bool last = false;
while (true) {
real_t blend = _interpolate_track<real_t>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
- animation->blend_shape_track_insert_key(track_idx, time, blend);
+ animation->blend_shape_track_insert_key(track_idx, time - anim_start, blend);
if (last) {
break;
}
time += increment;
- if (time >= length) {
+ if (time >= anim_end) {
last = true;
- time = length;
+ time = anim_end;
}
}
}
}
}
- animation->set_length(length);
+ animation->set_length(anim_end - anim_start);
Ref<AnimationLibrary> library;
- if (!ap->has_animation_library("")) {
+ if (!p_animation_player->has_animation_library("")) {
library.instantiate();
- ap->add_animation_library("", library);
+ p_animation_player->add_animation_library("", library);
} else {
- library = ap->get_animation_library("");
+ library = p_animation_player->get_animation_library("");
}
- library->add_animation(name, animation);
+ library->add_animation(anim_name, animation);
}
-void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
- for (GLTFNodeIndex mi_node_i = 0; mi_node_i < state->nodes.size(); ++mi_node_i) {
- Ref<GLTFNode> node = state->nodes[mi_node_i];
+void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> p_state) {
+ for (GLTFNodeIndex mi_node_i = 0; mi_node_i < p_state->nodes.size(); ++mi_node_i) {
+ Ref<GLTFNode> node = p_state->nodes[mi_node_i];
if (node->mesh < 0) {
continue;
}
- HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(mi_node_i);
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = p_state->scene_nodes.find(mi_node_i);
if (!mi_element) {
continue;
}
@@ -6152,10 +6154,10 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
if (skel_node != nullptr) {
godot_skeleton = cast_to<Skeleton3D>(skel_node);
}
- if (godot_skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) {
+ if (godot_skeleton != nullptr && p_state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) {
// This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible.
- const GLTFSkeletonIndex skeleton_gltf_i = state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()];
- Ref<GLTFSkeleton> gltf_skeleton = state->skeletons[skeleton_gltf_i];
+ const GLTFSkeletonIndex skeleton_gltf_i = p_state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()];
+ Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons[skeleton_gltf_i];
int bone_cnt = skeleton->get_bone_count();
ERR_FAIL_COND(bone_cnt != gltf_skeleton->joints.size());
@@ -6169,8 +6171,8 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
if (!gltf_skeleton->roots.is_empty()) {
root_gltf_i = gltf_skeleton->roots[0];
}
- if (state->skin_and_skeleton3d_to_gltf_skin.has(gltf_skin_key) && state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key].has(gltf_skel_key)) {
- skin_gltf_i = state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key];
+ if (p_state->skin_and_skeleton3d_to_gltf_skin.has(gltf_skin_key) && p_state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key].has(gltf_skel_key)) {
+ skin_gltf_i = p_state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key];
} else {
if (skin.is_null()) {
// Note that gltf_skin_key should remain null, so these can share a reference.
@@ -6207,9 +6209,9 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
gltf_skin->joint_i_to_bone_i[bind_i] = bone_i;
gltf_skin->joint_i_to_name[bind_i] = bind_name;
}
- skin_gltf_i = state->skins.size();
- state->skins.push_back(gltf_skin);
- state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key] = skin_gltf_i;
+ skin_gltf_i = p_state->skins.size();
+ p_state->skins.push_back(gltf_skin);
+ p_state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key] = skin_gltf_i;
}
node->skin = skin_gltf_i;
node->skeleton = skeleton_gltf_i;
@@ -6217,14 +6219,14 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
}
}
-float GLTFDocument::solve_metallic(float p_dielectric_specular, float diffuse, float specular, float p_one_minus_specular_strength) {
- if (specular <= p_dielectric_specular) {
+float GLTFDocument::solve_metallic(float p_dielectric_specular, float p_diffuse, float p_specular, float p_one_minus_specular_strength) {
+ if (p_specular <= p_dielectric_specular) {
return 0.0f;
}
const float a = p_dielectric_specular;
- const float b = diffuse * p_one_minus_specular_strength / (1.0f - p_dielectric_specular) + specular - 2.0f * p_dielectric_specular;
- const float c = p_dielectric_specular - specular;
+ const float b = p_diffuse * p_one_minus_specular_strength / (1.0f - p_dielectric_specular) + p_specular - 2.0f * p_dielectric_specular;
+ const float c = p_dielectric_specular - p_specular;
const float D = b * b - 4.0f * a * c;
return CLAMP((-b + Math::sqrt(D)) / (2.0f * a), 0.0f, 1.0f);
}
@@ -6248,21 +6250,21 @@ float GLTFDocument::get_max_component(const Color &p_color) {
return MAX(MAX(r, g), b);
}
-void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_root) {
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) {
- Ref<GLTFNode> node = state->nodes[node_i];
+void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root) {
+ for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); ++node_i) {
+ Ref<GLTFNode> node = p_state->nodes[node_i];
if (node->skin >= 0 && node->mesh >= 0) {
const GLTFSkinIndex skin_i = node->skin;
- HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(node_i);
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = p_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->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];
+ const GLTFSkeletonIndex skel_i = p_state->skins.write[node->skin]->skeleton;
+ Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons.write[skel_i];
Skeleton3D *skeleton = gltf_skeleton->godot_skeleton;
ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i));
@@ -6270,14 +6272,14 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
skeleton->add_child(mi, true);
mi->set_owner(skeleton->get_owner());
- mi->set_skin(state->skins.write[skin_i]->godot_skin);
+ mi->set_skin(p_state->skins.write[skin_i]->godot_skin);
mi->set_skeleton_path(mi->get_path_to(skeleton));
mi->set_transform(Transform3D());
}
}
}
-GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i) {
+GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i) {
Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
@@ -6347,7 +6349,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < key_count; key_i++) {
Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
+ p_track.rotation_track.values.write[key_i] = Quaternion::from_euler(rotation_radian);
}
} else if (path.contains(":scale")) {
p_track.scale_track.times = times;
@@ -6423,11 +6425,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
return p_track;
}
-void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, String p_animation_track_name) {
- Ref<Animation> animation = ap->get_animation(p_animation_track_name);
+void GLTFDocument::_convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, String p_animation_track_name) {
+ Ref<Animation> animation = p_animation_player->get_animation(p_animation_track_name);
Ref<GLTFAnimation> gltf_animation;
gltf_animation.instantiate();
- gltf_animation->set_name(_gen_unique_name(state, p_animation_track_name));
+ gltf_animation->set_name(_gen_unique_name(p_state, p_animation_track_name));
for (int32_t track_i = 0; track_i < animation->get_track_count(); track_i++) {
if (!animation->track_is_enabled(track_i)) {
@@ -6437,8 +6439,8 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (String(orig_track_path).contains(":position")) {
const Vector<String> node_suffix = String(orig_track_path).split(":position");
const NodePath path = node_suffix[0];
- const Node *node = ap->get_parent()->get_node_or_null(path);
- for (const KeyValue<GLTFNodeIndex, Node *> &position_scene_node_i : state->scene_nodes) {
+ const Node *node = p_animation_player->get_parent()->get_node_or_null(path);
+ for (const KeyValue<GLTFNodeIndex, Node *> &position_scene_node_i : p_state->scene_nodes) {
if (position_scene_node_i.value == node) {
GLTFNodeIndex node_index = position_scene_node_i.key;
HashMap<int, GLTFAnimation::Track>::Iterator position_track_i = gltf_animation->get_tracks().find(node_index);
@@ -6446,15 +6448,15 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (position_track_i) {
track = position_track_i->value;
}
- track = _convert_animation_track(state, track, animation, track_i, node_index);
+ track = _convert_animation_track(p_state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
} else if (String(orig_track_path).contains(":rotation_degrees")) {
const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees");
const NodePath path = node_suffix[0];
- const Node *node = ap->get_parent()->get_node_or_null(path);
- for (const KeyValue<GLTFNodeIndex, Node *> &rotation_degree_scene_node_i : state->scene_nodes) {
+ const Node *node = p_animation_player->get_parent()->get_node_or_null(path);
+ for (const KeyValue<GLTFNodeIndex, Node *> &rotation_degree_scene_node_i : p_state->scene_nodes) {
if (rotation_degree_scene_node_i.value == node) {
GLTFNodeIndex node_index = rotation_degree_scene_node_i.key;
HashMap<int, GLTFAnimation::Track>::Iterator rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
@@ -6462,15 +6464,15 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (rotation_degree_track_i) {
track = rotation_degree_track_i->value;
}
- track = _convert_animation_track(state, track, animation, track_i, node_index);
+ track = _convert_animation_track(p_state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
} else if (String(orig_track_path).contains(":scale")) {
const Vector<String> node_suffix = String(orig_track_path).split(":scale");
const NodePath path = node_suffix[0];
- const Node *node = ap->get_parent()->get_node_or_null(path);
- for (const KeyValue<GLTFNodeIndex, Node *> &scale_scene_node_i : state->scene_nodes) {
+ const Node *node = p_animation_player->get_parent()->get_node_or_null(path);
+ for (const KeyValue<GLTFNodeIndex, Node *> &scale_scene_node_i : p_state->scene_nodes) {
if (scale_scene_node_i.value == node) {
GLTFNodeIndex node_index = scale_scene_node_i.key;
HashMap<int, GLTFAnimation::Track>::Iterator scale_track_i = gltf_animation->get_tracks().find(node_index);
@@ -6478,18 +6480,18 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (scale_track_i) {
track = scale_track_i->value;
}
- track = _convert_animation_track(state, track, animation, track_i, node_index);
+ track = _convert_animation_track(p_state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
} else if (String(orig_track_path).contains(":transform")) {
const Vector<String> node_suffix = String(orig_track_path).split(":transform");
const NodePath path = node_suffix[0];
- const Node *node = ap->get_parent()->get_node_or_null(path);
- for (const KeyValue<GLTFNodeIndex, Node *> &transform_track_i : state->scene_nodes) {
+ const Node *node = p_animation_player->get_parent()->get_node_or_null(path);
+ for (const KeyValue<GLTFNodeIndex, Node *> &transform_track_i : p_state->scene_nodes) {
if (transform_track_i.value == node) {
GLTFAnimation::Track track;
- track = _convert_animation_track(state, track, animation, track_i, transform_track_i.key);
+ track = _convert_animation_track(p_state, track, animation, track_i, transform_track_i.key);
gltf_animation->get_tracks().insert(transform_track_i.key, track);
}
}
@@ -6497,12 +6499,12 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const Vector<String> node_suffix = String(orig_track_path).split(":");
const NodePath path = node_suffix[0];
const String suffix = node_suffix[1];
- Node *node = ap->get_parent()->get_node_or_null(path);
+ Node *node = p_animation_player->get_parent()->get_node_or_null(path);
MeshInstance3D *mi = cast_to<MeshInstance3D>(node);
Ref<Mesh> mesh = mi->get_mesh();
ERR_CONTINUE(mesh.is_null());
int32_t mesh_index = -1;
- for (const KeyValue<GLTFNodeIndex, Node *> &mesh_track_i : state->scene_nodes) {
+ for (const KeyValue<GLTFNodeIndex, Node *> &mesh_track_i : p_state->scene_nodes) {
if (mesh_track_i.value == node) {
mesh_index = mesh_track_i.key;
}
@@ -6555,15 +6557,15 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const String node = node_suffix[0];
const NodePath node_path = node;
const String suffix = node_suffix[1];
- Node *godot_node = ap->get_parent()->get_node_or_null(node_path);
+ Node *godot_node = p_animation_player->get_parent()->get_node_or_null(node_path);
Skeleton3D *skeleton = nullptr;
GLTFSkeletonIndex skeleton_gltf_i = -1;
- for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
- if (state->skeletons[skeleton_i]->godot_skeleton == cast_to<Skeleton3D>(godot_node)) {
- skeleton = state->skeletons[skeleton_i]->godot_skeleton;
+ for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < p_state->skeletons.size(); skeleton_i++) {
+ if (p_state->skeletons[skeleton_i]->godot_skeleton == cast_to<Skeleton3D>(godot_node)) {
+ skeleton = p_state->skeletons[skeleton_i]->godot_skeleton;
skeleton_gltf_i = skeleton_i;
ERR_CONTINUE(!skeleton);
- Ref<GLTFSkeleton> skeleton_gltf = state->skeletons[skeleton_gltf_i];
+ Ref<GLTFSkeleton> skeleton_gltf = p_state->skeletons[skeleton_gltf_i];
int32_t bone = skeleton->find_bone(suffix);
ERR_CONTINUE(bone == -1);
if (!skeleton_gltf->godot_bone_node.has(bone)) {
@@ -6575,14 +6577,14 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (property_track_i) {
track = property_track_i->value;
}
- track = _convert_animation_track(state, track, animation, track_i, node_i);
+ track = _convert_animation_track(p_state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
}
}
} else if (!String(orig_track_path).contains(":")) {
- ERR_CONTINUE(!ap->get_parent());
- Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path);
- for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
+ ERR_CONTINUE(!p_animation_player->get_parent());
+ Node *godot_node = p_animation_player->get_parent()->get_node_or_null(orig_track_path);
+ for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : p_state->scene_nodes) {
if (scene_node_i.value == godot_node) {
GLTFNodeIndex node_i = scene_node_i.key;
HashMap<int, GLTFAnimation::Track>::Iterator node_track_i = gltf_animation->get_tracks().find(node_i);
@@ -6590,7 +6592,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (node_track_i) {
track = node_track_i->value;
}
- track = _convert_animation_track(state, track, animation, track_i, node_i);
+ track = _convert_animation_track(p_state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
break;
}
@@ -6598,42 +6600,42 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
if (gltf_animation->get_tracks().size()) {
- state->animations.push_back(gltf_animation);
+ p_state->animations.push_back(gltf_animation);
}
}
-Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f, int p_bake_fps) {
+Error GLTFDocument::_parse(Ref<GLTFState> p_state, String p_path, Ref<FileAccess> p_file) {
Error err;
- if (f.is_null()) {
+ if (p_file.is_null()) {
return FAILED;
}
- f->seek(0);
- uint32_t magic = f->get_32();
+ p_file->seek(0);
+ uint32_t magic = p_file->get_32();
if (magic == 0x46546C67) {
//binary file
//text file
- f->seek(0);
- err = _parse_glb(f, state);
+ p_file->seek(0);
+ err = _parse_glb(p_file, p_state);
if (err != OK) {
return err;
}
} else {
- f->seek(0);
- String text = f->get_as_utf8_string();
+ p_file->seek(0);
+ String text = p_file->get_as_utf8_string();
JSON json;
err = json.parse(text);
if (err != OK) {
_err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
}
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- state->json = json.get_data();
+ p_state->json = json.get_data();
}
- if (!state->json.has("asset")) {
+ if (!p_state->json.has("asset")) {
return ERR_PARSE_ERROR;
}
- Dictionary asset = state->json["asset"];
+ Dictionary asset = p_state->json["asset"];
if (!asset.has("version")) {
return ERR_PARSE_ERROR;
@@ -6641,17 +6643,21 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess>
String version = asset["version"];
- state->major_version = version.get_slice(".", 0).to_int();
- state->minor_version = version.get_slice(".", 1).to_int();
+ p_state->major_version = version.get_slice(".", 0).to_int();
+ p_state->minor_version = version.get_slice(".", 1).to_int();
- for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
- Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ document_extensions.clear();
+ for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->import_preflight(state);
- ERR_FAIL_COND_V(err != OK, err);
+ err = ext->import_preflight(p_state, p_state->json["extensionsUsed"]);
+ if (err == OK) {
+ document_extensions.push_back(ext);
+ }
}
- err = _parse_gltf_state(state, p_path, p_bake_fps);
+
+ err = _parse_gltf_state(p_state, p_path);
ERR_FAIL_COND_V(err != OK, err);
+
return OK;
}
@@ -6682,47 +6688,43 @@ Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) {
}
Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) {
- if (p_material.is_valid()) {
- 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 Dictionary();
+ ERR_FAIL_NULL_V(p_material, Dictionary());
+ 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));
}
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();
+ ERR_FAIL_NULL_V(p_material, Dictionary());
+ 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));
}
-Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
+Error GLTFDocument::_serialize_version(Ref<GLTFState> p_state) {
const String version = "2.0";
- state->major_version = version.get_slice(".", 0).to_int();
- state->minor_version = version.get_slice(".", 1).to_int();
+ p_state->major_version = version.get_slice(".", 0).to_int();
+ p_state->minor_version = version.get_slice(".", 1).to_int();
Dictionary asset;
asset["version"] = version;
String hash = String(VERSION_HASH);
asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.is_empty() ? String("unknown") : hash);
- state->json["asset"] = asset;
+ p_state->json["asset"] = asset;
ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED);
- ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED);
+ ERR_FAIL_COND_V(!p_state->json.has("asset"), Error::FAILED);
return OK;
}
-Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
+Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path) {
Error err = FAILED;
if (p_path.to_lower().ends_with("glb")) {
- err = _encode_buffer_glb(state, p_path);
+ err = _encode_buffer_glb(p_state, p_path);
ERR_FAIL_COND_V(err != OK, err);
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V(f.is_null(), FAILED);
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V(file.is_null(), FAILED);
- String json = Variant(state->json).to_json_string();
+ String json = Variant(p_state->json).to_json_string();
const uint32_t magic = 0x46546C67; // GLTF
const int32_t header_size = 12;
@@ -6733,106 +6735,104 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
const uint32_t text_chunk_type = 0x4E4F534A; //JSON
uint32_t binary_data_length = 0;
- if (state->buffers.size()) {
- binary_data_length = state->buffers[0].size();
+ if (p_state->buffers.size()) {
+ binary_data_length = p_state->buffers[0].size();
}
const uint32_t binary_chunk_length = ((binary_data_length + 3) & (~3));
const uint32_t binary_chunk_type = 0x004E4942; //BIN
- f->create(FileAccess::ACCESS_RESOURCES);
- f->store_32(magic);
- f->store_32(state->major_version); // version
- f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length
- f->store_32(text_chunk_length);
- f->store_32(text_chunk_type);
- f->store_buffer((uint8_t *)&cs[0], cs.length());
+ file->create(FileAccess::ACCESS_RESOURCES);
+ file->store_32(magic);
+ file->store_32(p_state->major_version); // version
+ file->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length
+ file->store_32(text_chunk_length);
+ file->store_32(text_chunk_type);
+ file->store_buffer((uint8_t *)&cs[0], cs.length());
for (uint32_t pad_i = text_data_length; pad_i < text_chunk_length; pad_i++) {
- f->store_8(' ');
+ file->store_8(' ');
}
if (binary_chunk_length) {
- f->store_32(binary_chunk_length);
- f->store_32(binary_chunk_type);
- f->store_buffer(state->buffers[0].ptr(), binary_data_length);
+ file->store_32(binary_chunk_length);
+ file->store_32(binary_chunk_type);
+ file->store_buffer(p_state->buffers[0].ptr(), binary_data_length);
}
for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) {
- f->store_8(0);
+ file->store_8(0);
}
} else {
- err = _encode_buffer_bins(state, p_path);
+ err = _encode_buffer_bins(p_state, p_path);
ERR_FAIL_COND_V(err != OK, err);
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V(f.is_null(), FAILED);
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V(file.is_null(), FAILED);
- f->create(FileAccess::ACCESS_RESOURCES);
- String json = Variant(state->json).to_json_string();
- f->store_string(json);
+ file->create(FileAccess::ACCESS_RESOURCES);
+ String json = Variant(p_state->json).to_json_string();
+ file->store_string(json);
}
return err;
}
void GLTFDocument::_bind_methods() {
- ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "bake_fps", "base_path"),
- &GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(30), DEFVAL(String()));
- ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags", "bake_fps"),
- &GLTFDocument::append_from_buffer, DEFVAL(0), DEFVAL(30));
- ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags", "bake_fps"),
- &GLTFDocument::append_from_scene, DEFVAL(0), DEFVAL(30));
- ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps"),
- &GLTFDocument::generate_scene, DEFVAL(30));
+ ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "base_path"),
+ &GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags"),
+ &GLTFDocument::append_from_buffer, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags"),
+ &GLTFDocument::append_from_scene, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps", "trimming"),
+ &GLTFDocument::generate_scene, DEFVAL(30), DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_buffer", "state"),
&GLTFDocument::generate_buffer);
ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"),
&GLTFDocument::write_to_filesystem);
- ClassDB::bind_method(D_METHOD("set_extensions", "extensions"),
- &GLTFDocument::set_extensions);
- ClassDB::bind_method(D_METHOD("get_extensions"),
- &GLTFDocument::get_extensions);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE,
- vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"),
- PROPERTY_USAGE_DEFAULT),
- "set_extensions", "get_extensions");
+ ClassDB::bind_static_method("GLTFDocument", D_METHOD("register_gltf_document_extension", "extension", "first_priority"),
+ &GLTFDocument::register_gltf_document_extension, DEFVAL(false));
+ ClassDB::bind_static_method("GLTFDocument", D_METHOD("unregister_gltf_document_extension", "extension"),
+ &GLTFDocument::unregister_gltf_document_extension);
}
-void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) {
+void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> p_state) {
// build the hierarchy
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
- for (int j = 0; j < state->nodes[node_i]->children.size(); j++) {
- GLTFNodeIndex child_i = state->nodes[node_i]->children[j];
- ERR_FAIL_INDEX(child_i, state->nodes.size());
- if (state->nodes.write[child_i]->parent != -1) {
+ for (GLTFNodeIndex node_i = 0; node_i < p_state->nodes.size(); node_i++) {
+ for (int j = 0; j < p_state->nodes[node_i]->children.size(); j++) {
+ GLTFNodeIndex child_i = p_state->nodes[node_i]->children[j];
+ ERR_FAIL_INDEX(child_i, p_state->nodes.size());
+ if (p_state->nodes.write[child_i]->parent != -1) {
continue;
}
- state->nodes.write[child_i]->parent = node_i;
+ p_state->nodes.write[child_i]->parent = node_i;
}
}
}
-void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) {
- document_extensions = p_extensions;
+Vector<Ref<GLTFDocumentExtension>> GLTFDocument::all_document_extensions;
+
+void GLTFDocument::register_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension, bool p_first_priority) {
+ if (all_document_extensions.find(p_extension) == -1) {
+ if (p_first_priority) {
+ all_document_extensions.insert(0, p_extension);
+ } else {
+ all_document_extensions.push_back(p_extension);
+ }
+ }
}
-TypedArray<GLTFDocumentExtension> GLTFDocument::get_extensions() const {
- return document_extensions;
+void GLTFDocument::unregister_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension) {
+ all_document_extensions.erase(p_extension);
}
-GLTFDocument::GLTFDocument() {
- bool is_editor = ::Engine::get_singleton()->is_editor_hint();
- if (is_editor) {
- return;
- }
- Ref<GLTFDocumentExtensionConvertImporterMesh> extension_editor;
- extension_editor.instantiate();
- document_extensions.push_back(extension_editor);
+void GLTFDocument::unregister_all_gltf_document_extensions() {
+ all_document_extensions.clear();
}
-PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error *r_err) {
- Error err = _encode_buffer_glb(state, "");
+PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Error *r_err) {
+ Error err = _encode_buffer_glb(p_state, "");
if (r_err) {
*r_err = err;
}
ERR_FAIL_COND_V(err != OK, PackedByteArray());
- String json = Variant(state->json).to_json_string();
+ String json = Variant(p_state->json).to_json_string();
const uint32_t magic = 0x46546C67; // GLTF
const int32_t header_size = 12;
@@ -6846,8 +6846,8 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error
const uint32_t text_chunk_type = 0x4E4F534A; //JSON
int32_t binary_data_length = 0;
- if (state->buffers.size()) {
- binary_data_length = state->buffers[0].size();
+ if (p_state->buffers.size()) {
+ binary_data_length = p_state->buffers[0].size();
}
const int32_t binary_chunk_length = binary_data_length;
const int32_t binary_chunk_type = 0x004E4942; //BIN
@@ -6855,7 +6855,7 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error
Ref<StreamPeerBuffer> buffer;
buffer.instantiate();
buffer->put_32(magic);
- buffer->put_32(state->major_version); // version
+ buffer->put_32(p_state->major_version); // version
buffer->put_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length
buffer->put_32(text_chunk_length);
buffer->put_32(text_chunk_type);
@@ -6863,206 +6863,210 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> state, Error
if (binary_chunk_length) {
buffer->put_32(binary_chunk_length);
buffer->put_32(binary_chunk_type);
- buffer->put_data(state->buffers[0].ptr(), binary_data_length);
+ buffer->put_data(p_state->buffers[0].ptr(), binary_data_length);
}
return buffer->get_data_array();
}
-PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> state) {
- ERR_FAIL_NULL_V(state, PackedByteArray());
- Error err = _serialize(state, "");
+PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, PackedByteArray());
+ Error err = _serialize(p_state, "");
ERR_FAIL_COND_V(err != OK, PackedByteArray());
- PackedByteArray bytes = _serialize_glb_buffer(state, &err);
+ PackedByteArray bytes = _serialize_glb_buffer(p_state, &err);
return bytes;
}
-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);
+Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ Error err = _serialize(p_state, p_path);
if (err != OK) {
return err;
}
- err = _serialize_file(state, p_path);
+ err = _serialize_file(p_state, p_path);
if (err != OK) {
return Error::FAILED;
}
return OK;
}
-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);
+Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming) {
+ ERR_FAIL_NULL_V(p_state, nullptr);
+ ERR_FAIL_INDEX_V(0, p_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);
+ GLTFNodeIndex gltf_root = p_state->root_nodes.write[0];
+ Node *gltf_root_node = p_state->get_scene_node(gltf_root);
Node *root = gltf_root_node->get_parent();
ERR_FAIL_NULL_V(root, nullptr);
- _process_mesh_instances(state, root);
- if (state->animations.size()) {
+ _process_mesh_instances(p_state, root);
+ if (p_state->get_create_animations() && p_state->animations.size()) {
AnimationPlayer *ap = memnew(AnimationPlayer);
root->add_child(ap, true);
ap->set_owner(root);
- for (int i = 0; i < state->animations.size(); i++) {
- _import_animation(state, ap, i, p_bake_fps);
+ for (int i = 0; i < p_state->animations.size(); i++) {
+ _import_animation(p_state, ap, i, p_bake_fps, p_trimming);
}
}
- for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
+ for (KeyValue<GLTFNodeIndex, Node *> E : p_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];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- ERR_CONTINUE(!state->json.has("nodes"));
- Array nodes = state->json["nodes"];
+ ERR_CONTINUE(!p_state->json.has("nodes"));
+ Array nodes = p_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);
+ Ref<GLTFNode> gltf_node = p_state->nodes[E.key];
+ err = ext->import_node(p_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];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->import_post(state, root);
+ err = ext->import_post(p_state, root);
ERR_CONTINUE(err != OK);
}
ERR_FAIL_NULL_V(root, nullptr);
return root;
}
-Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) {
- ERR_FAIL_COND_V(state.is_null(), FAILED);
- 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;
+Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags) {
+ ERR_FAIL_COND_V(p_state.is_null(), FAILED);
+ p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
+ p_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];
+ document_extensions.clear();
+ for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
ERR_CONTINUE(ext.is_null());
- Error err = ext->export_preflight(p_node);
- ERR_FAIL_COND_V(err != OK, FAILED);
+ Error err = ext->export_preflight(p_state, p_node);
+ if (err == OK) {
+ document_extensions.push_back(ext);
+ }
}
- _convert_scene_node(state, p_node, -1, -1);
- if (!state->buffers.size()) {
- state->buffers.push_back(Vector<uint8_t>());
+ _convert_scene_node(p_state, p_node, -1, -1);
+ if (!p_state->buffers.size()) {
+ p_state->buffers.push_back(Vector<uint8_t>());
}
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) {
- ERR_FAIL_COND_V(state.is_null(), FAILED);
+Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags) {
+ ERR_FAIL_COND_V(p_state.is_null(), FAILED);
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
Error err = FAILED;
- 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;
+ p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
+ p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
- state->base_path = p_base_path.get_base_dir();
- err = _parse(state, state->base_path, file_access, p_bake_fps);
+ p_state->base_path = p_base_path.get_base_dir();
+ err = _parse(p_state, p_state->base_path, file_access);
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];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
- err = ext->import_post_parse(state);
+ err = ext->import_post_parse(p_state);
ERR_FAIL_COND_V(err != OK, err);
}
return OK;
}
-Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps) {
+Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_search_path) {
Error err;
/* PARSE EXTENSIONS */
- err = _parse_gltf_extensions(state);
+ err = _parse_gltf_extensions(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE SCENE */
- err = _parse_scenes(state);
+ err = _parse_scenes(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE NODES */
- err = _parse_nodes(state);
+ err = _parse_nodes(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE BUFFERS */
- err = _parse_buffers(state, p_search_path);
+ err = _parse_buffers(p_state, p_search_path);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE BUFFER VIEWS */
- err = _parse_buffer_views(state);
+ err = _parse_buffer_views(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE ACCESSORS */
- err = _parse_accessors(state);
+ err = _parse_accessors(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- if (!state->discard_meshes_and_materials) {
+ if (!p_state->discard_meshes_and_materials) {
/* PARSE IMAGES */
- err = _parse_images(state, p_search_path);
+ err = _parse_images(p_state, p_search_path);
+
+ ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
+
+ /* PARSE TEXTURE SAMPLERS */
+ err = _parse_texture_samplers(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE TEXTURES */
- err = _parse_textures(state);
+ err = _parse_textures(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE TEXTURES */
- err = _parse_materials(state);
+ err = _parse_materials(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
}
/* PARSE SKINS */
- err = _parse_skins(state);
+ err = _parse_skins(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* DETERMINE SKELETONS */
- err = _determine_skeletons(state);
+ err = _determine_skeletons(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* CREATE SKELETONS */
- err = _create_skeletons(state);
+ err = _create_skeletons(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* CREATE SKINS */
- err = _create_skins(state);
+ err = _create_skins(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE MESHES (we have enough info now) */
- err = _parse_meshes(state);
+ err = _parse_meshes(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE LIGHTS */
- err = _parse_lights(state);
+ err = _parse_lights(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE CAMERAS */
- err = _parse_cameras(state);
+ err = _parse_cameras(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* PARSE ANIMATIONS */
- err = _parse_animations(state);
+ err = _parse_animations(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
/* ASSIGN SCENE NAMES */
- _assign_scene_names(state);
+ _assign_scene_names(p_state);
Node3D *root = memnew(Node3D);
- 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]);
+ for (int32_t root_i = 0; root_i < p_state->root_nodes.size(); root_i++) {
+ _generate_scene_node(p_state, root, root, p_state->root_nodes[root_i]);
}
return OK;
}
-Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, int32_t p_bake_fps, String p_base_path) {
+Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, String p_base_path) {
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
if (r_state == Ref<GLTFState>()) {
r_state.instantiate();
@@ -7071,18 +7075,17 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
Error err;
- Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN);
- ERR_FAIL_NULL_V(f, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_NULL_V(file, ERR_FILE_CANT_OPEN);
String base_path = p_base_path;
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 = _parse(r_state, base_path, file);
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];
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
err = ext->import_post_parse(r_state);
ERR_FAIL_COND_V(err != OK, err);
@@ -7090,14 +7093,33 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
return OK;
}
-Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) {
- ERR_FAIL_NULL_V(state, ERR_PARSE_ERROR);
- if (state->json.has("extensionsRequired") && state->json["extensionsRequired"].get_type() == Variant::ARRAY) {
- Array extensions_required = state->json["extensionsRequired"];
- if (extensions_required.find("KHR_draco_mesh_compression") != -1) {
- ERR_PRINT("glTF2 extension KHR_draco_mesh_compression is not supported.");
- return ERR_UNAVAILABLE;
+Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_PARSE_ERROR);
+ if (p_state->json.has("extensionsUsed")) {
+ Vector<String> ext_array = p_state->json["extensionsUsed"];
+ p_state->extensions_used = ext_array;
+ }
+ if (p_state->json.has("extensionsRequired")) {
+ Vector<String> ext_array = p_state->json["extensionsRequired"];
+ p_state->extensions_required = ext_array;
+ }
+ HashSet<String> supported_extensions;
+ supported_extensions.insert("KHR_lights_punctual");
+ supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
+ supported_extensions.insert("KHR_texture_transform");
+ for (Ref<GLTFDocumentExtension> ext : document_extensions) {
+ ERR_CONTINUE(ext.is_null());
+ Vector<String> ext_supported_extensions = ext->get_supported_extensions();
+ for (int i = 0; i < ext_supported_extensions.size(); ++i) {
+ supported_extensions.insert(ext_supported_extensions[i]);
}
}
- return OK;
+ Error ret = Error::OK;
+ for (int i = 0; i < p_state->extensions_required.size(); i++) {
+ if (!supported_extensions.has(p_state->extensions_required[i])) {
+ ERR_PRINT("GLTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?");
+ ret = ERR_UNAVAILABLE;
+ }
+ }
+ return ret;
}
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 36a2f94a4e..164c63c53c 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -1,56 +1,49 @@
-/*************************************************************************/
-/* gltf_document.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_document.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_DOCUMENT_H
#define GLTF_DOCUMENT_H
-#include "gltf_defines.h"
-#include "structures/gltf_animation.h"
-
-#include "scene/3d/bone_attachment_3d.h"
-#include "scene/3d/importer_mesh_instance_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/animation/animation_player.h"
-#include "scene/resources/material.h"
+#include "extensions/gltf_document_extension.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
class GLTFDocument : public Resource {
GDCLASS(GLTFDocument, Resource);
- TypedArray<GLTFDocumentExtension> document_extensions;
+ static Vector<Ref<GLTFDocumentExtension>> all_document_extensions;
+ Vector<Ref<GLTFDocumentExtension>> document_extensions;
private:
const float BAKE_FPS = 30.0f;
public:
- GLTFDocument();
const int32_t JOINT_GROUP_SIZE = 4;
enum {
@@ -76,196 +69,204 @@ protected:
static void _bind_methods();
public:
- void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions);
- TypedArray<GLTFDocumentExtension> get_extensions() const;
+ static void register_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension, bool p_first_priority = false);
+ static void unregister_gltf_document_extension(Ref<GLTFDocumentExtension> p_extension);
+ static void unregister_all_gltf_document_extensions();
private:
- void _build_parent_hierachy(Ref<GLTFState> state);
+ void _build_parent_hierachy(Ref<GLTFState> p_state);
double _filter_number(double p_float);
String _get_component_type_name(const uint32_t p_component);
- int _get_component_type_size(const int component_type);
- Error _parse_scenes(Ref<GLTFState> state);
- Error _parse_nodes(Ref<GLTFState> state);
+ int _get_component_type_size(const int p_component_type);
+ Error _parse_scenes(Ref<GLTFState> p_state);
+ Error _parse_nodes(Ref<GLTFState> p_state);
String _get_type_name(const GLTFType p_component);
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);
- String _sanitize_bone_name(const String &name);
- String _gen_unique_bone_name(Ref<GLTFState> state,
- const GLTFSkeletonIndex skel_i,
+ String _gen_unique_name(Ref<GLTFState> p_state, const String &p_name);
+ String _sanitize_animation_name(const String &p_name);
+ String _gen_unique_animation_name(Ref<GLTFState> p_state, const String &p_name);
+ String _sanitize_bone_name(const String &p_name);
+ String _gen_unique_bone_name(Ref<GLTFState> p_state,
+ const GLTFSkeletonIndex p_skel_i,
const String &p_name);
- GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture);
- Ref<Texture2D> _get_texture(Ref<GLTFState> state,
+ GLTFTextureIndex _set_texture(Ref<GLTFState> p_state, Ref<Texture2D> p_texture,
+ StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats);
+ Ref<Texture2D> _get_texture(Ref<GLTFState> p_state,
+ const GLTFTextureIndex p_texture);
+ GLTFTextureSamplerIndex _set_sampler_for_mode(Ref<GLTFState> p_state,
+ StandardMaterial3D::TextureFilter p_filter_mode, bool p_repeats);
+ Ref<GLTFTextureSampler> _get_sampler_for_texture(Ref<GLTFState> p_state,
const GLTFTextureIndex p_texture);
- Error _parse_json(const String &p_path, Ref<GLTFState> state);
- Error _parse_glb(Ref<FileAccess> f, Ref<GLTFState> state);
- void _compute_node_heights(Ref<GLTFState> state);
- Error _parse_buffers(Ref<GLTFState> state, const String &p_base_path);
- Error _parse_buffer_views(Ref<GLTFState> state);
+ Error _parse_json(const String &p_path, Ref<GLTFState> p_state);
+ Error _parse_glb(Ref<FileAccess> p_file, Ref<GLTFState> p_state);
+ void _compute_node_heights(Ref<GLTFState> p_state);
+ Error _parse_buffers(Ref<GLTFState> p_state, const String &p_base_path);
+ Error _parse_buffer_views(Ref<GLTFState> p_state);
GLTFType _get_type_from_str(const String &p_string);
- Error _parse_accessors(Ref<GLTFState> state);
- Error _decode_buffer_view(Ref<GLTFState> state, double *dst,
+ Error _parse_accessors(Ref<GLTFState> p_state);
+ Error _decode_buffer_view(Ref<GLTFState> p_state, double *p_dst,
const GLTFBufferViewIndex p_buffer_view,
- const int skip_every, const int skip_bytes,
- const int element_size, const int count,
- const GLTFType type, const int component_count,
- const int component_type, const int component_size,
- const bool normalized, const int byte_offset,
- const bool for_vertex);
- Vector<double> _decode_accessor(Ref<GLTFState> state,
+ const int p_skip_every, const int p_skip_bytes,
+ const int p_element_size, const int p_count,
+ const GLTFType p_type, const int p_component_count,
+ const int p_component_type, const int p_component_size,
+ const bool p_normalized, const int p_byte_offset,
+ const bool p_for_vertex);
+ Vector<double> _decode_accessor(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<float> _decode_accessor_as_floats(Ref<GLTFState> state,
+ Vector<float> _decode_accessor_as_floats(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<int> _decode_accessor_as_ints(Ref<GLTFState> state,
+ Vector<int> _decode_accessor_as_ints(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Vector2> _decode_accessor_as_vec2(Ref<GLTFState> state,
+ Vector<Vector2> _decode_accessor_as_vec2(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Vector3> _decode_accessor_as_vec3(Ref<GLTFState> state,
+ Vector<Vector3> _decode_accessor_as_vec3(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Color> _decode_accessor_as_color(Ref<GLTFState> state,
+ Vector<Color> _decode_accessor_as_color(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Quaternion> _decode_accessor_as_quaternion(Ref<GLTFState> state,
+ Vector<Quaternion> _decode_accessor_as_quaternion(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Transform2D> _decode_accessor_as_xform2d(Ref<GLTFState> state,
+ Vector<Transform2D> _decode_accessor_as_xform2d(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Basis> _decode_accessor_as_basis(Ref<GLTFState> state,
+ Vector<Basis> _decode_accessor_as_basis(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Vector<Transform3D> _decode_accessor_as_xform(Ref<GLTFState> state,
+ Vector<Transform3D> _decode_accessor_as_xform(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
- Error _parse_meshes(Ref<GLTFState> state);
- Error _serialize_textures(Ref<GLTFState> state);
- Error _serialize_images(Ref<GLTFState> state, const String &p_path);
- Error _serialize_lights(Ref<GLTFState> state);
- Error _parse_images(Ref<GLTFState> state, const String &p_base_path);
- Error _parse_textures(Ref<GLTFState> state);
- Error _parse_materials(Ref<GLTFState> state);
- void _set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material);
+ Error _parse_meshes(Ref<GLTFState> p_state);
+ Error _serialize_textures(Ref<GLTFState> p_state);
+ Error _serialize_texture_samplers(Ref<GLTFState> p_state);
+ Error _serialize_images(Ref<GLTFState> p_state, const String &p_path);
+ Error _serialize_lights(Ref<GLTFState> p_state);
+ Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path);
+ Error _parse_textures(Ref<GLTFState> p_state);
+ Error _parse_texture_samplers(Ref<GLTFState> p_state);
+ Error _parse_materials(Ref<GLTFState> p_state);
+ void _set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> p_material);
void spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss,
Ref<BaseMaterial3D> p_material);
static void spec_gloss_to_metal_base_color(const Color &p_specular_factor,
const Color &p_diffuse,
Color &r_base_color,
float &r_metallic);
- GLTFNodeIndex _find_highest_node(Ref<GLTFState> state,
- const Vector<GLTFNodeIndex> &subset);
- bool _capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin,
- const GLTFNodeIndex node_index);
- void _capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
- Error _expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
- Error _verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
- Error _parse_skins(Ref<GLTFState> state);
- Error _determine_skeletons(Ref<GLTFState> state);
+ GLTFNodeIndex _find_highest_node(Ref<GLTFState> p_state,
+ const Vector<GLTFNodeIndex> &p_subset);
+ bool _capture_nodes_in_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin,
+ const GLTFNodeIndex p_node_index);
+ void _capture_nodes_for_multirooted_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin);
+ Error _expand_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin);
+ Error _verify_skin(Ref<GLTFState> p_state, Ref<GLTFSkin> p_skin);
+ Error _parse_skins(Ref<GLTFState> p_state);
+ Error _determine_skeletons(Ref<GLTFState> p_state);
Error _reparent_non_joint_skeleton_subtrees(
- Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
- const Vector<GLTFNodeIndex> &non_joints);
- Error _determine_skeleton_roots(Ref<GLTFState> state,
- const GLTFSkeletonIndex skel_i);
- Error _create_skeletons(Ref<GLTFState> state);
- Error _map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state);
- Error _serialize_skins(Ref<GLTFState> state);
- Error _create_skins(Ref<GLTFState> state);
- bool _skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b);
- void _remove_duplicate_skins(Ref<GLTFState> state);
- Error _serialize_cameras(Ref<GLTFState> state);
- Error _parse_cameras(Ref<GLTFState> state);
- Error _parse_lights(Ref<GLTFState> state);
- Error _parse_animations(Ref<GLTFState> state);
- Error _serialize_animations(Ref<GLTFState> state);
- BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> state,
- Skeleton3D *skeleton,
- const GLTFNodeIndex node_index,
- const GLTFNodeIndex bone_index);
- ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, const GLTFNodeIndex node_index);
- Camera3D *_generate_camera(Ref<GLTFState> state, const GLTFNodeIndex node_index);
- Node3D *_generate_light(Ref<GLTFState> state, const GLTFNodeIndex node_index);
- Node3D *_generate_spatial(Ref<GLTFState> state, const GLTFNodeIndex node_index);
- void _assign_scene_names(Ref<GLTFState> state);
+ Ref<GLTFState> p_state, Ref<GLTFSkeleton> p_skeleton,
+ const Vector<GLTFNodeIndex> &p_non_joints);
+ Error _determine_skeleton_roots(Ref<GLTFState> p_state,
+ const GLTFSkeletonIndex p_skel_i);
+ Error _create_skeletons(Ref<GLTFState> p_state);
+ Error _map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> p_state);
+ Error _serialize_skins(Ref<GLTFState> p_state);
+ Error _create_skins(Ref<GLTFState> p_state);
+ bool _skins_are_same(const Ref<Skin> p_skin_a, const Ref<Skin> p_skin_b);
+ void _remove_duplicate_skins(Ref<GLTFState> p_state);
+ Error _serialize_cameras(Ref<GLTFState> p_state);
+ Error _parse_cameras(Ref<GLTFState> p_state);
+ Error _parse_lights(Ref<GLTFState> p_state);
+ Error _parse_animations(Ref<GLTFState> p_state);
+ Error _serialize_animations(Ref<GLTFState> p_state);
+ BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> p_state,
+ Skeleton3D *p_skeleton,
+ const GLTFNodeIndex p_node_index,
+ const GLTFNodeIndex p_bone_index);
+ ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
+ Camera3D *_generate_camera(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
+ Light3D *_generate_light(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
+ Node3D *_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
+ void _assign_scene_names(Ref<GLTFState> p_state);
template <class T>
T _interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values,
const float p_time,
const GLTFAnimation::Interpolation p_interp);
- GLTFAccessorIndex _encode_accessor_as_quaternions(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_quaternions(Ref<GLTFState> p_state,
const Vector<Quaternion> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_weights(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_weights(Ref<GLTFState> p_state,
const Vector<Color> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_joints(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_joints(Ref<GLTFState> p_state,
const Vector<Color> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_floats(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_floats(Ref<GLTFState> p_state,
const Vector<real_t> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_vec2(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_vec2(Ref<GLTFState> p_state,
const Vector<Vector2> p_attribs,
const bool p_for_vertex);
- void _calc_accessor_vec2_min_max(int i, const int element_count, Vector<double> &type_max, Vector2 attribs, Vector<double> &type_min) {
- if (i == 0) {
- for (int32_t type_i = 0; type_i < element_count; type_i++) {
- type_max.write[type_i] = attribs[(i * element_count) + type_i];
- type_min.write[type_i] = attribs[(i * element_count) + type_i];
+ void _calc_accessor_vec2_min_max(int p_i, const int p_element_count, Vector<double> &p_type_max, Vector2 p_attribs, Vector<double> &p_type_min) {
+ if (p_i == 0) {
+ for (int32_t type_i = 0; type_i < p_element_count; type_i++) {
+ p_type_max.write[type_i] = p_attribs[(p_i * p_element_count) + type_i];
+ p_type_min.write[type_i] = p_attribs[(p_i * p_element_count) + type_i];
}
}
- for (int32_t type_i = 0; type_i < element_count; type_i++) {
- type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]);
- type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]);
- type_max.write[type_i] = _filter_number(type_max.write[type_i]);
- type_min.write[type_i] = _filter_number(type_min.write[type_i]);
+ for (int32_t type_i = 0; type_i < p_element_count; type_i++) {
+ p_type_max.write[type_i] = MAX(p_attribs[(p_i * p_element_count) + type_i], p_type_max[type_i]);
+ p_type_min.write[type_i] = MIN(p_attribs[(p_i * p_element_count) + type_i], p_type_min[type_i]);
+ p_type_max.write[type_i] = _filter_number(p_type_max.write[type_i]);
+ p_type_min.write[type_i] = _filter_number(p_type_min.write[type_i]);
}
}
- GLTFAccessorIndex _encode_accessor_as_vec3(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_vec3(Ref<GLTFState> p_state,
const Vector<Vector3> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_color(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_color(Ref<GLTFState> p_state,
const Vector<Color> p_attribs,
const bool p_for_vertex);
void _calc_accessor_min_max(int p_i, const int p_element_count, Vector<double> &p_type_max, Vector<double> p_attribs, Vector<double> &p_type_min);
- GLTFAccessorIndex _encode_accessor_as_ints(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_ints(Ref<GLTFState> p_state,
const Vector<int32_t> p_attribs,
const bool p_for_vertex);
- GLTFAccessorIndex _encode_accessor_as_xform(Ref<GLTFState> state,
+ GLTFAccessorIndex _encode_accessor_as_xform(Ref<GLTFState> p_state,
const Vector<Transform3D> p_attribs,
const bool p_for_vertex);
- Error _encode_buffer_view(Ref<GLTFState> state, const double *src,
- const int count, const GLTFType type,
- const int component_type, const bool normalized,
- const int byte_offset, const bool for_vertex,
+ Error _encode_buffer_view(Ref<GLTFState> p_state, const double *p_src,
+ const int p_count, const GLTFType p_type,
+ const int p_component_type, const bool p_normalized,
+ const int p_byte_offset, const bool p_for_vertex,
GLTFBufferViewIndex &r_accessor);
- Error _encode_accessors(Ref<GLTFState> state);
- Error _encode_buffer_views(Ref<GLTFState> state);
- Error _serialize_materials(Ref<GLTFState> state);
- Error _serialize_meshes(Ref<GLTFState> state);
- Error _serialize_nodes(Ref<GLTFState> state);
- Error _serialize_scenes(Ref<GLTFState> state);
+ Error _encode_accessors(Ref<GLTFState> p_state);
+ Error _encode_buffer_views(Ref<GLTFState> p_state);
+ Error _serialize_materials(Ref<GLTFState> p_state);
+ Error _serialize_meshes(Ref<GLTFState> p_state);
+ Error _serialize_nodes(Ref<GLTFState> p_state);
+ Error _serialize_scenes(Ref<GLTFState> p_state);
String interpolation_to_string(const GLTFAnimation::Interpolation p_interp);
- GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> state,
+ GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> p_state,
GLTFAnimation::Track p_track,
Ref<Animation> p_animation,
int32_t p_track_i,
GLTFNodeIndex p_node_i);
- Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path);
- Error _encode_buffer_glb(Ref<GLTFState> state, const String &p_path);
- PackedByteArray _serialize_glb_buffer(Ref<GLTFState> state, Error *r_err);
+ Error _encode_buffer_bins(Ref<GLTFState> p_state, const String &p_path);
+ Error _encode_buffer_glb(Ref<GLTFState> p_state, const String &p_path);
+ PackedByteArray _serialize_glb_buffer(Ref<GLTFState> p_state, Error *r_err);
Dictionary _serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material);
Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material);
- Error _serialize_version(Ref<GLTFState> state);
- Error _serialize_file(Ref<GLTFState> state, const String p_path);
- Error _serialize_extensions(Ref<GLTFState> state) const;
+ Error _serialize_version(Ref<GLTFState> p_state);
+ Error _serialize_file(Ref<GLTFState> p_state, const String p_path);
+ Error _serialize_gltf_extensions(Ref<GLTFState> p_state) const;
public:
// https://www.itu.int/rec/R-REC-BT.601
@@ -277,90 +278,90 @@ public:
private:
// https://github.com/microsoft/glTF-SDK/blob/master/GLTFSDK/Source/PBRUtils.cpp#L9
// https://bghgary.github.io/glTF/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
- static float solve_metallic(float p_dielectric_specular, float diffuse,
- float specular,
+ static float solve_metallic(float p_dielectric_specular, float p_diffuse,
+ float p_specular,
float p_one_minus_specular_strength);
static float get_perceived_brightness(const Color p_color);
static float get_max_component(const Color &p_color);
public:
- Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30, String p_base_path = String());
- Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30);
- Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30);
+ Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, String p_base_path = String());
+ Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0);
+ Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0);
public:
- Node *generate_scene(Ref<GLTFState> state, int32_t p_bake_fps = 30.0f);
- PackedByteArray generate_buffer(Ref<GLTFState> state);
- Error write_to_filesystem(Ref<GLTFState> state, const String &p_path);
+ Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false);
+ PackedByteArray generate_buffer(Ref<GLTFState> p_state);
+ Error write_to_filesystem(Ref<GLTFState> p_state, const String &p_path);
public:
- Error _parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps);
- Error _parse_gltf_extensions(Ref<GLTFState> state);
- void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root);
- void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent,
- Node3D *scene_root,
- const GLTFNodeIndex node_index);
- void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index);
- void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
- const GLTFAnimationIndex index, const int bake_fps);
- void _convert_mesh_instances(Ref<GLTFState> state);
- GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera3D *p_camera);
- void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node);
- GLTFLightIndex _convert_light(Ref<GLTFState> state, Light3D *p_light);
- void _convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node);
- void _convert_scene_node(Ref<GLTFState> state, Node *p_current,
+ Error _parse_gltf_state(Ref<GLTFState> p_state, const String &p_search_path);
+ Error _parse_gltf_extensions(Ref<GLTFState> p_state);
+ void _process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene_root);
+ void _generate_scene_node(Ref<GLTFState> p_state, Node *p_scene_parent,
+ Node3D *p_scene_root,
+ const GLTFNodeIndex p_node_index);
+ void _generate_skeleton_bone_node(Ref<GLTFState> p_state, Node *p_scene_parent, Node3D *p_scene_root, const GLTFNodeIndex p_node_index);
+ void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player,
+ const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming);
+ void _convert_mesh_instances(Ref<GLTFState> p_state);
+ GLTFCameraIndex _convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera);
+ void _convert_light_to_gltf(Light3D *p_light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node);
+ GLTFLightIndex _convert_light(Ref<GLTFState> p_state, Light3D *p_light);
+ void _convert_spatial(Ref<GLTFState> p_state, Node3D *p_spatial, Ref<GLTFNode> p_node);
+ void _convert_scene_node(Ref<GLTFState> p_state, Node *p_current,
const GLTFNodeIndex p_gltf_current,
const GLTFNodeIndex p_gltf_root);
#ifdef MODULE_CSG_ENABLED
- void _convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
+ void _convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state);
#endif // MODULE_CSG_ENABLED
- void _create_gltf_node(Ref<GLTFState> state,
+ void _create_gltf_node(Ref<GLTFState> p_state,
Node *p_scene_parent,
- GLTFNodeIndex current_node_i,
+ GLTFNodeIndex p_current_node_i,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_gltf_node,
- Ref<GLTFNode> gltf_node);
+ Ref<GLTFNode> p_gltf_node);
void _convert_animation_player_to_gltf(
- AnimationPlayer *animation_player, Ref<GLTFState> state,
+ AnimationPlayer *p_animation_player, Ref<GLTFState> p_state,
GLTFNodeIndex p_gltf_current,
GLTFNodeIndex p_gltf_root_index,
Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
- void _check_visibility(Node *p_node, bool &retflag);
- void _convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state,
- Ref<GLTFNode> gltf_node);
+ void _check_visibility(Node *p_node, bool &r_retflag);
+ void _convert_camera_to_gltf(Camera3D *p_camera, Ref<GLTFState> p_state,
+ Ref<GLTFNode> p_gltf_node);
#ifdef MODULE_GRIDMAP_ENABLED
void _convert_grid_map_to_gltf(
GridMap *p_grid_map,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
- Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
+ Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state);
#endif // MODULE_GRIDMAP_ENABLED
void _convert_multi_mesh_instance_to_gltf(
MultiMeshInstance3D *p_multi_mesh_instance,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
- Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
+ Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state);
void _convert_skeleton_to_gltf(
- Skeleton3D *p_scene_parent, Ref<GLTFState> state,
+ Skeleton3D *p_scene_parent, Ref<GLTFState> p_state,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
- Ref<GLTFNode> gltf_node);
+ Ref<GLTFNode> p_gltf_node);
void _convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment,
- Ref<GLTFState> state,
+ Ref<GLTFState> p_state,
GLTFNodeIndex p_parent_node_index,
GLTFNodeIndex p_root_node_index,
- Ref<GLTFNode> gltf_node);
+ Ref<GLTFNode> p_gltf_node);
void _convert_mesh_instance_to_gltf(MeshInstance3D *p_mesh_instance,
- Ref<GLTFState> state,
- Ref<GLTFNode> gltf_node);
- GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> state,
+ Ref<GLTFState> p_state,
+ Ref<GLTFNode> p_gltf_node);
+ GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> p_state,
MeshInstance3D *p_mesh_instance);
- void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
+ void _convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player,
String p_animation_track_name);
- Error _serialize(Ref<GLTFState> state, const String &p_path);
- Error _parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f, int p_bake_fps);
+ Error _serialize(Ref<GLTFState> p_state, const String &p_path);
+ Error _parse(Ref<GLTFState> p_state, String p_path, Ref<FileAccess> p_file);
};
#endif // GLTF_DOCUMENT_H
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index a5f7bcf9d6..252453dfc2 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -1,36 +1,39 @@
-/*************************************************************************/
-/* gltf_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_state.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_state.h"
+#include "gltf_template_convert.h"
+
void GLTFState::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_used_extension", "extension_name", "required"), &GLTFState::add_used_extension);
ClassDB::bind_method(D_METHOD("get_json"), &GLTFState::get_json);
ClassDB::bind_method(D_METHOD("set_json", "json"), &GLTFState::set_json);
ClassDB::bind_method(D_METHOD("get_major_version"), &GLTFState::get_major_version);
@@ -63,6 +66,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
ClassDB::bind_method(D_METHOD("set_textures", "textures"), &GLTFState::set_textures);
+ ClassDB::bind_method(D_METHOD("get_texture_samplers"), &GLTFState::get_texture_samplers);
+ ClassDB::bind_method(D_METHOD("set_texture_samplers", "texture_samplers"), &GLTFState::set_texture_samplers);
ClassDB::bind_method(D_METHOD("get_images"), &GLTFState::get_images);
ClassDB::bind_method(D_METHOD("set_images", "images"), &GLTFState::set_images);
ClassDB::bind_method(D_METHOD("get_skins"), &GLTFState::get_skins);
@@ -79,9 +84,13 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skeletons", "skeletons"), &GLTFState::set_skeletons);
ClassDB::bind_method(D_METHOD("get_skeleton_to_node"), &GLTFState::get_skeleton_to_node);
ClassDB::bind_method(D_METHOD("set_skeleton_to_node", "skeleton_to_node"), &GLTFState::set_skeleton_to_node);
+ ClassDB::bind_method(D_METHOD("get_create_animations"), &GLTFState::get_create_animations);
+ ClassDB::bind_method(D_METHOD("set_create_animations", "create_animations"), &GLTFState::set_create_animations);
ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations);
ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations);
ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node);
+ ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data);
+ ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary
ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int
@@ -98,6 +107,7 @@ void GLTFState::_bind_methods() {
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, "texture_samplers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_texture_samplers", "get_texture_samplers"); //Vector<Ref<GLTFTextureSampler>>
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>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector<Ref<GLTFSkin>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "cameras", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_cameras", "get_cameras"); // Vector<Ref<GLTFCamera>>
@@ -106,9 +116,21 @@ void GLTFState::_bind_methods() {
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"); // RBMap<GLTFSkeletonIndex,
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool
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>>
}
+void GLTFState::add_used_extension(const String &p_extension_name, bool p_required) {
+ if (!extensions_used.has(p_extension_name)) {
+ extensions_used.push_back(p_extension_name);
+ }
+ if (p_required) {
+ if (!extensions_required.has(p_extension_name)) {
+ extensions_required.push_back(p_extension_name);
+ }
+ }
+}
+
Dictionary GLTFState::get_json() {
return json;
}
@@ -149,51 +171,51 @@ void GLTFState::set_use_named_skin_binds(bool p_use_named_skin_binds) {
use_named_skin_binds = p_use_named_skin_binds;
}
-Array GLTFState::get_nodes() {
+TypedArray<GLTFNode> GLTFState::get_nodes() {
return GLTFTemplateConvert::to_array(nodes);
}
-void GLTFState::set_nodes(Array p_nodes) {
+void GLTFState::set_nodes(TypedArray<GLTFNode> p_nodes) {
GLTFTemplateConvert::set_from_array(nodes, p_nodes);
}
-Array GLTFState::get_buffers() {
+TypedArray<PackedByteArray> GLTFState::get_buffers() {
return GLTFTemplateConvert::to_array(buffers);
}
-void GLTFState::set_buffers(Array p_buffers) {
+void GLTFState::set_buffers(TypedArray<PackedByteArray> p_buffers) {
GLTFTemplateConvert::set_from_array(buffers, p_buffers);
}
-Array GLTFState::get_buffer_views() {
+TypedArray<GLTFBufferView> GLTFState::get_buffer_views() {
return GLTFTemplateConvert::to_array(buffer_views);
}
-void GLTFState::set_buffer_views(Array p_buffer_views) {
+void GLTFState::set_buffer_views(TypedArray<GLTFBufferView> p_buffer_views) {
GLTFTemplateConvert::set_from_array(buffer_views, p_buffer_views);
}
-Array GLTFState::get_accessors() {
+TypedArray<GLTFAccessor> GLTFState::get_accessors() {
return GLTFTemplateConvert::to_array(accessors);
}
-void GLTFState::set_accessors(Array p_accessors) {
+void GLTFState::set_accessors(TypedArray<GLTFAccessor> p_accessors) {
GLTFTemplateConvert::set_from_array(accessors, p_accessors);
}
-Array GLTFState::get_meshes() {
+TypedArray<GLTFMesh> GLTFState::get_meshes() {
return GLTFTemplateConvert::to_array(meshes);
}
-void GLTFState::set_meshes(Array p_meshes) {
+void GLTFState::set_meshes(TypedArray<GLTFMesh> p_meshes) {
GLTFTemplateConvert::set_from_array(meshes, p_meshes);
}
-Array GLTFState::get_materials() {
+TypedArray<Material> GLTFState::get_materials() {
return GLTFTemplateConvert::to_array(materials);
}
-void GLTFState::set_materials(Array p_materials) {
+void GLTFState::set_materials(TypedArray<Material> p_materials) {
GLTFTemplateConvert::set_from_array(materials, p_materials);
}
@@ -205,75 +227,83 @@ void GLTFState::set_scene_name(String p_scene_name) {
scene_name = p_scene_name;
}
-Array GLTFState::get_root_nodes() {
- return GLTFTemplateConvert::to_array(root_nodes);
+PackedInt32Array GLTFState::get_root_nodes() {
+ return root_nodes;
}
-void GLTFState::set_root_nodes(Array p_root_nodes) {
- GLTFTemplateConvert::set_from_array(root_nodes, p_root_nodes);
+void GLTFState::set_root_nodes(PackedInt32Array p_root_nodes) {
+ root_nodes = p_root_nodes;
}
-Array GLTFState::get_textures() {
+TypedArray<GLTFTexture> GLTFState::get_textures() {
return GLTFTemplateConvert::to_array(textures);
}
-void GLTFState::set_textures(Array p_textures) {
+void GLTFState::set_textures(TypedArray<GLTFTexture> p_textures) {
GLTFTemplateConvert::set_from_array(textures, p_textures);
}
-Array GLTFState::get_images() {
+TypedArray<GLTFTextureSampler> GLTFState::get_texture_samplers() {
+ return GLTFTemplateConvert::to_array(texture_samplers);
+}
+
+void GLTFState::set_texture_samplers(TypedArray<GLTFTextureSampler> p_texture_samplers) {
+ GLTFTemplateConvert::set_from_array(texture_samplers, p_texture_samplers);
+}
+
+TypedArray<Texture2D> GLTFState::get_images() {
return GLTFTemplateConvert::to_array(images);
}
-void GLTFState::set_images(Array p_images) {
+void GLTFState::set_images(TypedArray<Texture2D> p_images) {
GLTFTemplateConvert::set_from_array(images, p_images);
}
-Array GLTFState::get_skins() {
+TypedArray<GLTFSkin> GLTFState::get_skins() {
return GLTFTemplateConvert::to_array(skins);
}
-void GLTFState::set_skins(Array p_skins) {
+void GLTFState::set_skins(TypedArray<GLTFSkin> p_skins) {
GLTFTemplateConvert::set_from_array(skins, p_skins);
}
-Array GLTFState::get_cameras() {
+TypedArray<GLTFCamera> GLTFState::get_cameras() {
return GLTFTemplateConvert::to_array(cameras);
}
-void GLTFState::set_cameras(Array p_cameras) {
+void GLTFState::set_cameras(TypedArray<GLTFCamera> p_cameras) {
GLTFTemplateConvert::set_from_array(cameras, p_cameras);
}
-Array GLTFState::get_lights() {
+TypedArray<GLTFLight> GLTFState::get_lights() {
return GLTFTemplateConvert::to_array(lights);
}
-void GLTFState::set_lights(Array p_lights) {
+void GLTFState::set_lights(TypedArray<GLTFLight> p_lights) {
GLTFTemplateConvert::set_from_array(lights, p_lights);
}
-Array GLTFState::get_unique_names() {
+TypedArray<String> GLTFState::get_unique_names() {
return GLTFTemplateConvert::to_array(unique_names);
}
-void GLTFState::set_unique_names(Array p_unique_names) {
+void GLTFState::set_unique_names(TypedArray<String> p_unique_names) {
GLTFTemplateConvert::set_from_array(unique_names, p_unique_names);
}
-Array GLTFState::get_unique_animation_names() {
+TypedArray<String> GLTFState::get_unique_animation_names() {
return GLTFTemplateConvert::to_array(unique_animation_names);
}
-void GLTFState::set_unique_animation_names(Array p_unique_animation_names) {
+void GLTFState::set_unique_animation_names(TypedArray<String> p_unique_animation_names) {
GLTFTemplateConvert::set_from_array(unique_animation_names, p_unique_animation_names);
}
-Array GLTFState::get_skeletons() {
+TypedArray<GLTFSkeleton> GLTFState::get_skeletons() {
return GLTFTemplateConvert::to_array(skeletons);
}
-void GLTFState::set_skeletons(Array p_skeletons) {
+void GLTFState::set_skeletons(TypedArray<GLTFSkeleton> p_skeletons) {
GLTFTemplateConvert::set_from_array(skeletons, p_skeletons);
}
@@ -285,11 +315,19 @@ void GLTFState::set_skeleton_to_node(Dictionary p_skeleton_to_node) {
GLTFTemplateConvert::set_from_dict(skeleton_to_node, p_skeleton_to_node);
}
-Array GLTFState::get_animations() {
+bool GLTFState::get_create_animations() {
+ return create_animations;
+}
+
+void GLTFState::set_create_animations(bool p_create_animations) {
+ create_animations = p_create_animations;
+}
+
+TypedArray<GLTFAnimation> GLTFState::get_animations() {
return GLTFTemplateConvert::to_array(animations);
}
-void GLTFState::set_animations(Array p_animations) {
+void GLTFState::set_animations(TypedArray<GLTFAnimation> p_animations) {
GLTFTemplateConvert::set_from_array(animations, p_animations);
}
@@ -324,3 +362,11 @@ String GLTFState::get_base_path() {
void GLTFState::set_base_path(String p_base_path) {
base_path = p_base_path;
}
+
+Variant GLTFState::get_additional_data(const StringName &p_extension_name) {
+ return additional_data[p_extension_name];
+}
+
+void GLTFState::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) {
+ additional_data[p_extension_name] = p_additional_data;
+}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index d2a4948f06..488ad038aa 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -1,38 +1,37 @@
-/*************************************************************************/
-/* gltf_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_state.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_STATE_H
#define GLTF_STATE_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"
@@ -42,10 +41,7 @@
#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"
+#include "structures/gltf_texture_sampler.h"
class GLTFState : public Resource {
GDCLASS(GLTFState, Resource);
@@ -61,6 +57,7 @@ class GLTFState : public Resource {
bool use_named_skin_binds = false;
bool use_khr_texture_transform = false;
bool discard_meshes_and_materials = false;
+ bool create_animations = true;
Vector<Ref<GLTFNode>> nodes;
Vector<Vector<uint8_t>> buffers;
@@ -70,13 +67,17 @@ class GLTFState : public Resource {
Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to.
Vector<AnimationPlayer *> animation_players;
- HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
- Vector<Ref<BaseMaterial3D>> materials;
+ HashMap<Ref<Material>, GLTFMaterialIndex> material_cache;
+ Vector<Ref<Material>> materials;
String scene_name;
Vector<int> root_nodes;
Vector<Ref<GLTFTexture>> textures;
+ Vector<Ref<GLTFTextureSampler>> texture_samplers;
+ Ref<GLTFTextureSampler> default_texture_sampler;
Vector<Ref<Texture2D>> images;
+ Vector<String> extensions_used;
+ Vector<String> extensions_required;
Vector<Ref<GLTFSkin>> skins;
Vector<Ref<GLTFCamera>> cameras;
@@ -91,11 +92,14 @@ class GLTFState : public Resource {
HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+ Dictionary additional_data;
protected:
static void _bind_methods();
public:
+ void add_used_extension(const String &p_extension, bool p_required = false);
+
Dictionary get_json();
void set_json(Dictionary p_json);
@@ -114,23 +118,23 @@ public:
bool get_discard_meshes_and_materials();
void set_discard_meshes_and_materials(bool p_discard_meshes_and_materials);
- Array get_nodes();
- void set_nodes(Array p_nodes);
+ TypedArray<GLTFNode> get_nodes();
+ void set_nodes(TypedArray<GLTFNode> p_nodes);
- Array get_buffers();
- void set_buffers(Array p_buffers);
+ TypedArray<PackedByteArray> get_buffers();
+ void set_buffers(TypedArray<PackedByteArray> p_buffers);
- Array get_buffer_views();
- void set_buffer_views(Array p_buffer_views);
+ TypedArray<GLTFBufferView> get_buffer_views();
+ void set_buffer_views(TypedArray<GLTFBufferView> p_buffer_views);
- Array get_accessors();
- void set_accessors(Array p_accessors);
+ TypedArray<GLTFAccessor> get_accessors();
+ void set_accessors(TypedArray<GLTFAccessor> p_accessors);
- Array get_meshes();
- void set_meshes(Array p_meshes);
+ TypedArray<GLTFMesh> get_meshes();
+ void set_meshes(TypedArray<GLTFMesh> p_meshes);
- Array get_materials();
- void set_materials(Array p_materials);
+ TypedArray<Material> get_materials();
+ void set_materials(TypedArray<Material> p_materials);
String get_scene_name();
void set_scene_name(String p_scene_name);
@@ -138,38 +142,44 @@ public:
String get_base_path();
void set_base_path(String p_base_path);
- Array get_root_nodes();
- void set_root_nodes(Array p_root_nodes);
+ PackedInt32Array get_root_nodes();
+ void set_root_nodes(PackedInt32Array p_root_nodes);
+
+ TypedArray<GLTFTexture> get_textures();
+ void set_textures(TypedArray<GLTFTexture> p_textures);
- Array get_textures();
- void set_textures(Array p_textures);
+ TypedArray<GLTFTextureSampler> get_texture_samplers();
+ void set_texture_samplers(TypedArray<GLTFTextureSampler> p_texture_samplers);
- Array get_images();
- void set_images(Array p_images);
+ TypedArray<Texture2D> get_images();
+ void set_images(TypedArray<Texture2D> p_images);
- Array get_skins();
- void set_skins(Array p_skins);
+ TypedArray<GLTFSkin> get_skins();
+ void set_skins(TypedArray<GLTFSkin> p_skins);
- Array get_cameras();
- void set_cameras(Array p_cameras);
+ TypedArray<GLTFCamera> get_cameras();
+ void set_cameras(TypedArray<GLTFCamera> p_cameras);
- Array get_lights();
- void set_lights(Array p_lights);
+ TypedArray<GLTFLight> get_lights();
+ void set_lights(TypedArray<GLTFLight> p_lights);
- Array get_unique_names();
- void set_unique_names(Array p_unique_names);
+ TypedArray<String> get_unique_names();
+ void set_unique_names(TypedArray<String> p_unique_names);
- Array get_unique_animation_names();
- void set_unique_animation_names(Array p_unique_names);
+ TypedArray<String> get_unique_animation_names();
+ void set_unique_animation_names(TypedArray<String> p_unique_names);
- Array get_skeletons();
- void set_skeletons(Array p_skeletons);
+ TypedArray<GLTFSkeleton> get_skeletons();
+ void set_skeletons(TypedArray<GLTFSkeleton> p_skeletons);
Dictionary get_skeleton_to_node();
void set_skeleton_to_node(Dictionary p_skeleton_to_node);
- Array get_animations();
- void set_animations(Array p_animations);
+ bool get_create_animations();
+ void set_create_animations(bool p_create_animations);
+
+ TypedArray<GLTFAnimation> get_animations();
+ void set_animations(TypedArray<GLTFAnimation> p_animations);
Node *get_scene_node(GLTFNodeIndex idx);
@@ -177,20 +187,8 @@ public:
AnimationPlayer *get_animation_player(int idx);
- //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) {
- // this->scene_nodes = p_scene_nodes;
- //}
-
- //void set_animation_players(Vector<AnimationPlayer *> p_animation_players) {
- // this->animation_players = p_animation_players;
- //}
-
- //RBMap<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
- // return this->material_cache;
- //}
- //void set_material_cache(RBMap<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
- // this->material_cache = p_material_cache;
- //}
+ Variant get_additional_data(const StringName &p_extension_name);
+ void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);
};
#endif // GLTF_STATE_H
diff --git a/modules/gltf/gltf_template_convert.h b/modules/gltf/gltf_template_convert.h
index c915d3deb0..8616665f8b 100644
--- a/modules/gltf/gltf_template_convert.h
+++ b/modules/gltf/gltf_template_convert.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_template_convert.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -46,8 +46,8 @@ static Array to_array(const Vector<T> &p_inp) {
}
template <class T>
-static Array to_array(const HashSet<T> &p_inp) {
- Array ret;
+static TypedArray<T> to_array(const HashSet<T> &p_inp) {
+ TypedArray<T> ret;
typename HashSet<T>::Iterator elem = p_inp.begin();
while (elem) {
ret.push_back(*elem);
@@ -65,7 +65,7 @@ static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
}
template <class T>
-static void set_from_array(HashSet<T> &r_out, const Array &p_inp) {
+static void set_from_array(HashSet<T> &r_out, const TypedArray<T> &p_inp) {
r_out.clear();
for (int i = 0; i < p_inp.size(); i++) {
r_out.insert(p_inp[i]);
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index 1e1204aa57..f80e12bbae 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -1,52 +1,38 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
-#ifndef _3D_DISABLED
-
-#include "extensions/gltf_light.h"
+#include "extensions/gltf_document_extension_convert_importer_mesh.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_state.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"
@@ -66,17 +52,24 @@ static void _editor_init() {
bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled");
// Defined here because EditorSettings doesn't exist in `register_gltf_types` yet.
- EDITOR_DEF_RST("filesystem/import/blender/blender3_path", "");
+ String blender3_path = EDITOR_DEF_RST("filesystem/import/blender/blender3_path", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,
"filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR));
if (blend_enabled) {
- Ref<EditorSceneFormatImporterBlend> importer;
- importer.instantiate();
- ResourceImporterScene::add_importer(importer);
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (blender3_path.is_empty()) {
+ WARN_PRINT("Blend file import is enabled in the project settings, but no Blender path is configured in the editor settings. Blend files will not be imported.");
+ } else if (!da->dir_exists(blender3_path)) {
+ WARN_PRINT("Blend file import is enabled, but the Blender path doesn't point to an accessible directory. Blend files will not be imported.");
+ } else {
+ Ref<EditorSceneFormatImporterBlend> importer;
+ importer.instantiate();
+ ResourceImporterScene::add_importer(importer);
- Ref<EditorFileSystemImportFormatSupportQueryBlend> blend_import_query;
- blend_import_query.instantiate();
- EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query);
+ Ref<EditorFileSystemImportFormatSupportQueryBlend> blend_import_query;
+ blend_import_query.instantiate();
+ EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query);
+ }
}
// FBX to glTF importer.
@@ -87,20 +80,22 @@ static void _editor_init() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,
"filesystem/import/fbx/fbx2gltf_path", PROPERTY_HINT_GLOBAL_FILE));
if (fbx_enabled) {
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (fbx2gltf_path.is_empty()) {
- WARN_PRINT("FBX file import is enabled, but no FBX2glTF path is configured. FBX files will not be imported.");
- } else if (!da->file_exists(fbx2gltf_path)) {
- WARN_PRINT("FBX file import is enabled, but the FBX2glTF path doesn't point to a valid FBX2glTF executable. FBX files will not be imported.");
- } else {
- Ref<EditorSceneFormatImporterFBX> importer;
- importer.instantiate();
- ResourceImporterScene::add_importer(importer);
- }
+ Ref<EditorSceneFormatImporterFBX> importer;
+ importer.instantiate();
+ ResourceImporterScene::get_scene_singleton()->add_importer(importer);
+
+ Ref<EditorFileSystemImportFormatSupportQueryFBX> fbx_import_query;
+ fbx_import_query.instantiate();
+ EditorFileSystem::get_singleton()->add_import_format_support_query(fbx_import_query);
}
}
#endif // TOOLS_ENABLED
+#define GLTF_REGISTER_DOCUMENT_EXTENSION(m_doc_ext_class) \
+ Ref<m_doc_ext_class> extension_##m_doc_ext_class; \
+ extension_##m_doc_ext_class.instantiate(); \
+ GLTFDocument::register_gltf_document_extension(extension_##m_doc_ext_class);
+
void initialize_gltf_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
// glTF API available at runtime.
@@ -119,6 +114,12 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(GLTFSpecGloss);
GDREGISTER_CLASS(GLTFState);
GDREGISTER_CLASS(GLTFTexture);
+ GDREGISTER_CLASS(GLTFTextureSampler);
+ // Register GLTFDocumentExtension classes with GLTFDocument.
+ bool is_editor = ::Engine::get_singleton()->is_editor_hint();
+ if (!is_editor) {
+ GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionConvertImporterMesh);
+ }
}
#ifdef TOOLS_ENABLED
@@ -135,6 +136,11 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
+ // Can't (a priori) run external app on these platforms.
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/blender/enabled.web", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.android", false);
+ GLOBAL_DEF_RST("filesystem/import/fbx/enabled.web", false);
ClassDB::set_current_api(prev_api);
EditorNode::add_init_callback(_editor_init);
@@ -147,6 +153,5 @@ void uninitialize_gltf_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
+ GLTFDocument::unregister_all_gltf_document_extensions();
}
-
-#endif // _3D_DISABLED
diff --git a/modules/gltf/register_types.h b/modules/gltf/register_types.h
index 90b9a83c88..c8a736c48a 100644
--- a/modules/gltf/register_types.h
+++ b/modules/gltf/register_types.h
@@ -1,34 +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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_REGISTER_TYPES_H
+#define GLTF_REGISTER_TYPES_H
#include "modules/register_module_types.h"
void initialize_gltf_module(ModuleInitializationLevel p_level);
void uninitialize_gltf_module(ModuleInitializationLevel p_level);
+
+#endif // GLTF_REGISTER_TYPES_H
diff --git a/modules/gltf/structures/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp
index 1b8911fe72..2119a0ee82 100644
--- a/modules/gltf/structures/gltf_accessor.cpp
+++ b/modules/gltf/structures/gltf_accessor.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_accessor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_accessor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_accessor.h"
diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h
index bfb71d57fe..5b4afc79c4 100644
--- a/modules/gltf/structures/gltf_accessor.h
+++ b/modules/gltf/structures/gltf_accessor.h
@@ -1,39 +1,38 @@
-/*************************************************************************/
-/* gltf_accessor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_accessor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACCESSOR_H
#define GLTF_ACCESSOR_H
-#include "core/io/resource.h"
-
#include "../gltf_defines.h"
+#include "core/io/resource.h"
struct GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
diff --git a/modules/gltf/structures/gltf_animation.cpp b/modules/gltf/structures/gltf_animation.cpp
index e598c870ab..fc2e8ee148 100644
--- a/modules/gltf/structures/gltf_animation.cpp
+++ b/modules/gltf/structures/gltf_animation.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_animation.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_animation.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_animation.h"
diff --git a/modules/gltf/structures/gltf_animation.h b/modules/gltf/structures/gltf_animation.h
index 3777f579f6..33dc840eb3 100644
--- a/modules/gltf/structures/gltf_animation.h
+++ b/modules/gltf/structures/gltf_animation.h
@@ -1,37 +1,37 @@
-/*************************************************************************/
-/* gltf_animation.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_animation.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ANIMATION_H
#define GLTF_ANIMATION_H
-#include "core/io/resource.h"
+#include "scene/animation/animation_player.h"
class GLTFAnimation : public Resource {
GDCLASS(GLTFAnimation, Resource);
diff --git a/modules/gltf/structures/gltf_buffer_view.cpp b/modules/gltf/structures/gltf_buffer_view.cpp
index ba19ed8628..7678f23f57 100644
--- a/modules/gltf/structures/gltf_buffer_view.cpp
+++ b/modules/gltf/structures/gltf_buffer_view.cpp
@@ -1,37 +1,35 @@
-/*************************************************************************/
-/* gltf_buffer_view.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_buffer_view.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_buffer_view.h"
-#include "../gltf_document_extension.h"
-
void GLTFBufferView::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer);
diff --git a/modules/gltf/structures/gltf_buffer_view.h b/modules/gltf/structures/gltf_buffer_view.h
index b1f500de25..93626a182b 100644
--- a/modules/gltf/structures/gltf_buffer_view.h
+++ b/modules/gltf/structures/gltf_buffer_view.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_buffer_view.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_buffer_view.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_BUFFER_VIEW_H
#define GLTF_BUFFER_VIEW_H
diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp
index f3ea6a1c4c..630b34c270 100644
--- a/modules/gltf/structures/gltf_camera.cpp
+++ b/modules/gltf/structures/gltf_camera.cpp
@@ -1,47 +1,134 @@
-/*************************************************************************/
-/* gltf_camera.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_camera.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_camera.h"
+#include "scene/3d/camera_3d.h"
+
void GLTFCamera::_bind_methods() {
+ ClassDB::bind_static_method("GLTFCamera", D_METHOD("from_node", "camera_node"), &GLTFCamera::from_node);
+ ClassDB::bind_method(D_METHOD("to_node"), &GLTFCamera::to_node);
+
+ ClassDB::bind_static_method("GLTFCamera", D_METHOD("from_dictionary", "dictionary"), &GLTFCamera::from_dictionary);
+ ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFCamera::to_dictionary);
+
ClassDB::bind_method(D_METHOD("get_perspective"), &GLTFCamera::get_perspective);
ClassDB::bind_method(D_METHOD("set_perspective", "perspective"), &GLTFCamera::set_perspective);
- ClassDB::bind_method(D_METHOD("get_fov_size"), &GLTFCamera::get_fov_size);
- ClassDB::bind_method(D_METHOD("set_fov_size", "fov_size"), &GLTFCamera::set_fov_size);
+ ClassDB::bind_method(D_METHOD("get_fov"), &GLTFCamera::get_fov);
+ ClassDB::bind_method(D_METHOD("set_fov", "fov"), &GLTFCamera::set_fov);
+ ClassDB::bind_method(D_METHOD("get_size_mag"), &GLTFCamera::get_size_mag);
+ ClassDB::bind_method(D_METHOD("set_size_mag", "size_mag"), &GLTFCamera::set_size_mag);
ClassDB::bind_method(D_METHOD("get_depth_far"), &GLTFCamera::get_depth_far);
ClassDB::bind_method(D_METHOD("set_depth_far", "zdepth_far"), &GLTFCamera::set_depth_far);
ClassDB::bind_method(D_METHOD("get_depth_near"), &GLTFCamera::get_depth_near);
ClassDB::bind_method(D_METHOD("set_depth_near", "zdepth_near"), &GLTFCamera::set_depth_near);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); // float
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov"), "set_fov", "get_fov");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_mag"), "set_size_mag", "get_size_mag");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near");
+}
+
+Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
+ Ref<GLTFCamera> c;
+ c.instantiate();
+ ERR_FAIL_COND_V_MSG(!p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null.");
+ c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ c->set_fov(Math::deg_to_rad(p_camera->get_fov()));
+ // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ c->set_size_mag(p_camera->get_size() * 0.5f);
+ c->set_depth_far(p_camera->get_far());
+ c->set_depth_near(p_camera->get_near());
+ return c;
+}
+
+Camera3D *GLTFCamera::to_node() const {
+ Camera3D *camera = memnew(Camera3D);
+ camera->set_projection(perspective ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL);
+ // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ camera->set_fov(Math::rad_to_deg(fov));
+ // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ camera->set_size(size_mag * 2.0f);
+ camera->set_near(depth_near);
+ camera->set_far(depth_far);
+ return camera;
+}
+
+Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) {
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse GLTF camera, missing required field 'type'.");
+ Ref<GLTFCamera> camera;
+ camera.instantiate();
+ const String &type = p_dictionary["type"];
+ if (type == "perspective") {
+ camera->set_perspective(true);
+ if (p_dictionary.has("perspective")) {
+ const Dictionary &persp = p_dictionary["perspective"];
+ camera->set_fov(persp["yfov"]);
+ if (persp.has("zfar")) {
+ camera->set_depth_far(persp["zfar"]);
+ }
+ camera->set_depth_near(persp["znear"]);
+ }
+ } else if (type == "orthographic") {
+ camera->set_perspective(false);
+ if (p_dictionary.has("orthographic")) {
+ const Dictionary &ortho = p_dictionary["orthographic"];
+ camera->set_size_mag(ortho["ymag"]);
+ camera->set_depth_far(ortho["zfar"]);
+ camera->set_depth_near(ortho["znear"]);
+ }
+ } else {
+ ERR_PRINT("Error parsing GLTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic.");
+ }
+ return camera;
+}
+
+Dictionary GLTFCamera::to_dictionary() const {
+ Dictionary d;
+ if (perspective) {
+ Dictionary persp;
+ persp["yfov"] = fov;
+ persp["zfar"] = depth_far;
+ persp["znear"] = depth_near;
+ d["perspective"] = persp;
+ d["type"] = "perspective";
+ } else {
+ Dictionary ortho;
+ ortho["ymag"] = size_mag;
+ ortho["xmag"] = size_mag;
+ ortho["zfar"] = depth_far;
+ ortho["znear"] = depth_near;
+ d["orthographic"] = ortho;
+ d["type"] = "orthographic";
+ }
+ return d;
}
diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h
index b7df741825..2a077a6da0 100644
--- a/modules/gltf/structures/gltf_camera.h
+++ b/modules/gltf/structures/gltf_camera.h
@@ -1,46 +1,54 @@
-/*************************************************************************/
-/* gltf_camera.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_camera.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_CAMERA_H
#define GLTF_CAMERA_H
#include "core/io/resource.h"
+class Camera3D;
+
+// Reference and test file:
+// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md
+
class GLTFCamera : public Resource {
GDCLASS(GLTFCamera, Resource);
private:
+ // GLTF has no default camera values, they should always be specified in
+ // the GLTF file. Here we default to Godot's default camera settings.
bool perspective = true;
- float fov_size = 75.0;
- float depth_far = 4000.0;
- float depth_near = 0.05;
+ real_t fov = Math::deg_to_rad(75.0);
+ real_t size_mag = 0.5;
+ real_t depth_far = 4000.0;
+ real_t depth_near = 0.05;
protected:
static void _bind_methods();
@@ -48,12 +56,20 @@ protected:
public:
bool get_perspective() const { return perspective; }
void set_perspective(bool p_val) { perspective = p_val; }
- float get_fov_size() const { return fov_size; }
- void set_fov_size(float p_val) { fov_size = p_val; }
- float get_depth_far() const { return depth_far; }
- void set_depth_far(float p_val) { depth_far = p_val; }
- float get_depth_near() const { return depth_near; }
- void set_depth_near(float p_val) { depth_near = p_val; }
+ real_t get_fov() const { return fov; }
+ void set_fov(real_t p_val) { fov = p_val; }
+ real_t get_size_mag() const { return size_mag; }
+ void set_size_mag(real_t p_val) { size_mag = p_val; }
+ real_t get_depth_far() const { return depth_far; }
+ void set_depth_far(real_t p_val) { depth_far = p_val; }
+ real_t get_depth_near() const { return depth_near; }
+ void set_depth_near(real_t p_val) { depth_near = p_val; }
+
+ static Ref<GLTFCamera> from_node(const Camera3D *p_camera);
+ Camera3D *to_node() const;
+
+ static Ref<GLTFCamera> from_dictionary(const Dictionary p_dictionary);
+ Dictionary to_dictionary() const;
};
#endif // GLTF_CAMERA_H
diff --git a/modules/gltf/structures/gltf_mesh.cpp b/modules/gltf/structures/gltf_mesh.cpp
index 3add8304b1..2f8f208d57 100644
--- a/modules/gltf/structures/gltf_mesh.cpp
+++ b/modules/gltf/structures/gltf_mesh.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_mesh.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_mesh.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_mesh.h"
@@ -53,11 +53,11 @@ void GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) {
mesh = p_mesh;
}
-Array GLTFMesh::get_instance_materials() {
+TypedArray<Material> GLTFMesh::get_instance_materials() {
return instance_materials;
}
-void GLTFMesh::set_instance_materials(Array p_instance_materials) {
+void GLTFMesh::set_instance_materials(TypedArray<Material> p_instance_materials) {
instance_materials = p_instance_materials;
}
diff --git a/modules/gltf/structures/gltf_mesh.h b/modules/gltf/structures/gltf_mesh.h
index dc26120b48..1e14c1ac7f 100644
--- a/modules/gltf/structures/gltf_mesh.h
+++ b/modules/gltf/structures/gltf_mesh.h
@@ -1,40 +1,38 @@
-/*************************************************************************/
-/* gltf_mesh.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_mesh.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_MESH_H
#define GLTF_MESH_H
-#include "core/io/resource.h"
-#include "scene/3d/importer_mesh_instance_3d.h"
+#include "../gltf_defines.h"
#include "scene/resources/importer_mesh.h"
-#include "scene/resources/mesh.h"
class GLTFMesh : public Resource {
GDCLASS(GLTFMesh, Resource);
@@ -42,7 +40,7 @@ class GLTFMesh : public Resource {
private:
Ref<ImporterMesh> mesh;
Vector<float> blend_weights;
- Array instance_materials;
+ TypedArray<Material> instance_materials;
protected:
static void _bind_methods();
@@ -52,8 +50,8 @@ public:
void set_mesh(Ref<ImporterMesh> p_mesh);
Vector<float> get_blend_weights();
void set_blend_weights(Vector<float> p_blend_weights);
- Array get_instance_materials();
- void set_instance_materials(Array p_instance_materials);
+ TypedArray<Material> get_instance_materials();
+ void set_instance_materials(TypedArray<Material> p_instance_materials);
};
#endif // GLTF_MESH_H
diff --git a/modules/gltf/structures/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp
index 86280603fa..66d1eaad51 100644
--- a/modules/gltf/structures/gltf_node.cpp
+++ b/modules/gltf/structures/gltf_node.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_node.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_node.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_node.h"
@@ -57,6 +57,8 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children);
ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light);
ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);
+ ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data);
+ ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data);
ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex
ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int
@@ -176,3 +178,11 @@ GLTFLightIndex GLTFNode::get_light() {
void GLTFNode::set_light(GLTFLightIndex p_light) {
light = p_light;
}
+
+Variant GLTFNode::get_additional_data(const StringName &p_extension_name) {
+ return additional_data[p_extension_name];
+}
+
+void GLTFNode::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) {
+ additional_data[p_extension_name] = p_additional_data;
+}
diff --git a/modules/gltf/structures/gltf_node.h b/modules/gltf/structures/gltf_node.h
index 1a57ea32e2..d801a4cc2c 100644
--- a/modules/gltf/structures/gltf_node.h
+++ b/modules/gltf/structures/gltf_node.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_node.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_node.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_NODE_H
#define GLTF_NODE_H
@@ -53,6 +53,7 @@ private:
Vector3 scale = Vector3(1, 1, 1);
Vector<int> children;
GLTFLightIndex light = -1;
+ Dictionary additional_data;
protected:
static void _bind_methods();
@@ -96,6 +97,9 @@ public:
GLTFLightIndex get_light();
void set_light(GLTFLightIndex p_light);
+
+ Variant get_additional_data(const StringName &p_extension_name);
+ void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);
};
#endif // GLTF_NODE_H
diff --git a/modules/gltf/structures/gltf_skeleton.cpp b/modules/gltf/structures/gltf_skeleton.cpp
index 90a6b0f50f..d7a7315062 100644
--- a/modules/gltf/structures/gltf_skeleton.cpp
+++ b/modules/gltf/structures/gltf_skeleton.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_skeleton.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_skeleton.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_skeleton.h"
@@ -72,11 +72,11 @@ Skeleton3D *GLTFSkeleton::get_godot_skeleton() {
return godot_skeleton;
}
-Array GLTFSkeleton::get_unique_names() {
+TypedArray<String> GLTFSkeleton::get_unique_names() {
return GLTFTemplateConvert::to_array(unique_names);
}
-void GLTFSkeleton::set_unique_names(Array p_unique_names) {
+void GLTFSkeleton::set_unique_names(TypedArray<String> p_unique_names) {
GLTFTemplateConvert::set_from_array(unique_names, p_unique_names);
}
diff --git a/modules/gltf/structures/gltf_skeleton.h b/modules/gltf/structures/gltf_skeleton.h
index db88623213..044d67d299 100644
--- a/modules/gltf/structures/gltf_skeleton.h
+++ b/modules/gltf/structures/gltf_skeleton.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_skeleton.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_skeleton.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SKELETON_H
#define GLTF_SKELETON_H
@@ -75,8 +75,8 @@ public:
// this->godot_skeleton = p_godot_skeleton;
// }
- Array get_unique_names();
- void set_unique_names(Array p_unique_names);
+ TypedArray<String> get_unique_names();
+ void set_unique_names(TypedArray<String> p_unique_names);
//RBMap<int32_t, GLTFNodeIndex> get_godot_bone_node() {
// return this->godot_bone_node;
diff --git a/modules/gltf/structures/gltf_skin.cpp b/modules/gltf/structures/gltf_skin.cpp
index 2e46ee3be2..fa2e814c94 100644
--- a/modules/gltf/structures/gltf_skin.cpp
+++ b/modules/gltf/structures/gltf_skin.cpp
@@ -1,36 +1,37 @@
-/*************************************************************************/
-/* gltf_skin.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_skin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_skin.h"
#include "../gltf_template_convert.h"
+#include "core/variant/typed_array.h"
#include "scene/resources/skin.h"
void GLTFSkin::_bind_methods() {
@@ -83,11 +84,11 @@ void GLTFSkin::set_joints_original(Vector<GLTFNodeIndex> p_joints_original) {
joints_original = p_joints_original;
}
-Array GLTFSkin::get_inverse_binds() {
+TypedArray<Transform3D> GLTFSkin::get_inverse_binds() {
return GLTFTemplateConvert::to_array(inverse_binds);
}
-void GLTFSkin::set_inverse_binds(Array p_inverse_binds) {
+void GLTFSkin::set_inverse_binds(TypedArray<Transform3D> p_inverse_binds) {
GLTFTemplateConvert::set_from_array(inverse_binds, p_inverse_binds);
}
diff --git a/modules/gltf/structures/gltf_skin.h b/modules/gltf/structures/gltf_skin.h
index 59b6a300ac..c943c42083 100644
--- a/modules/gltf/structures/gltf_skin.h
+++ b/modules/gltf/structures/gltf_skin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_skin.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_skin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SKIN_H
#define GLTF_SKIN_H
@@ -34,6 +34,9 @@
#include "../gltf_defines.h"
#include "core/io/resource.h"
+template <typename T>
+class TypedArray;
+
class GLTFSkin : public Resource {
GDCLASS(GLTFSkin, Resource);
friend class GLTFDocument;
@@ -82,8 +85,8 @@ public:
Vector<GLTFNodeIndex> get_joints_original();
void set_joints_original(Vector<GLTFNodeIndex> p_joints_original);
- Array get_inverse_binds();
- void set_inverse_binds(Array p_inverse_binds);
+ TypedArray<Transform3D> get_inverse_binds();
+ void set_inverse_binds(TypedArray<Transform3D> p_inverse_binds);
Vector<GLTFNodeIndex> get_joints();
void set_joints(Vector<GLTFNodeIndex> p_joints);
diff --git a/modules/gltf/structures/gltf_texture.cpp b/modules/gltf/structures/gltf_texture.cpp
index 2a21cb3df8..67010f3b75 100644
--- a/modules/gltf/structures/gltf_texture.cpp
+++ b/modules/gltf/structures/gltf_texture.cpp
@@ -1,40 +1,43 @@
-/*************************************************************************/
-/* gltf_texture.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_texture.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_texture.h"
void GLTFTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_src_image"), &GLTFTexture::get_src_image);
ClassDB::bind_method(D_METHOD("set_src_image", "src_image"), &GLTFTexture::set_src_image);
+ ClassDB::bind_method(D_METHOD("get_sampler"), &GLTFTexture::get_sampler);
+ ClassDB::bind_method(D_METHOD("set_sampler", "sampler"), &GLTFTexture::set_sampler);
ADD_PROPERTY(PropertyInfo(Variant::INT, "src_image"), "set_src_image", "get_src_image"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sampler"), "set_sampler", "get_sampler"); // int
}
GLTFImageIndex GLTFTexture::get_src_image() const {
@@ -44,3 +47,11 @@ GLTFImageIndex GLTFTexture::get_src_image() const {
void GLTFTexture::set_src_image(GLTFImageIndex val) {
src_image = val;
}
+
+GLTFTextureSamplerIndex GLTFTexture::get_sampler() const {
+ return sampler;
+}
+
+void GLTFTexture::set_sampler(GLTFTextureSamplerIndex val) {
+ sampler = val;
+}
diff --git a/modules/gltf/structures/gltf_texture.h b/modules/gltf/structures/gltf_texture.h
index b1d12dddfa..8def62a9a1 100644
--- a/modules/gltf/structures/gltf_texture.h
+++ b/modules/gltf/structures/gltf_texture.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gltf_texture.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gltf_texture.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_TEXTURE_H
#define GLTF_TEXTURE_H
@@ -39,6 +39,7 @@ class GLTFTexture : public Resource {
private:
GLTFImageIndex src_image = 0;
+ GLTFTextureSamplerIndex sampler = -1;
protected:
static void _bind_methods();
@@ -46,6 +47,8 @@ protected:
public:
GLTFImageIndex get_src_image() const;
void set_src_image(GLTFImageIndex val);
+ GLTFTextureSamplerIndex get_sampler() const;
+ void set_sampler(GLTFTextureSamplerIndex val);
};
#endif // GLTF_TEXTURE_H
diff --git a/modules/gltf/structures/gltf_texture_sampler.cpp b/modules/gltf/structures/gltf_texture_sampler.cpp
new file mode 100644
index 0000000000..8653bd83db
--- /dev/null
+++ b/modules/gltf/structures/gltf_texture_sampler.cpp
@@ -0,0 +1,47 @@
+/**************************************************************************/
+/* gltf_texture_sampler.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_texture_sampler.h"
+
+void GLTFTextureSampler::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_mag_filter"), &GLTFTextureSampler::get_mag_filter);
+ ClassDB::bind_method(D_METHOD("set_mag_filter", "filter_mode"), &GLTFTextureSampler::set_mag_filter);
+ ClassDB::bind_method(D_METHOD("get_min_filter"), &GLTFTextureSampler::get_min_filter);
+ ClassDB::bind_method(D_METHOD("set_min_filter", "filter_mode"), &GLTFTextureSampler::set_min_filter);
+ ClassDB::bind_method(D_METHOD("get_wrap_s"), &GLTFTextureSampler::get_wrap_s);
+ ClassDB::bind_method(D_METHOD("set_wrap_s", "wrap_mode"), &GLTFTextureSampler::set_wrap_s);
+ ClassDB::bind_method(D_METHOD("get_wrap_t"), &GLTFTextureSampler::get_wrap_t);
+ ClassDB::bind_method(D_METHOD("set_wrap_t", "wrap_mode"), &GLTFTextureSampler::set_wrap_t);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mag_filter"), "set_mag_filter", "get_mag_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "min_filter"), "set_min_filter", "get_min_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_s"), "set_wrap_s", "get_wrap_s");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_t"), "set_wrap_t", "get_wrap_t");
+}
diff --git a/modules/gltf/structures/gltf_texture_sampler.h b/modules/gltf/structures/gltf_texture_sampler.h
new file mode 100644
index 0000000000..32102b549f
--- /dev/null
+++ b/modules/gltf/structures/gltf_texture_sampler.h
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* gltf_texture_sampler.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_TEXTURE_SAMPLER_H
+#define GLTF_TEXTURE_SAMPLER_H
+
+#include "scene/resources/material.h"
+
+class GLTFTextureSampler : public Resource {
+ GDCLASS(GLTFTextureSampler, Resource);
+
+public:
+ enum FilterMode {
+ NEAREST = 9728,
+ LINEAR = 9729,
+ NEAREST_MIPMAP_NEAREST = 9984,
+ LINEAR_MIPMAP_NEAREST = 9985,
+ NEAREST_MIPMAP_LINEAR = 9986,
+ LINEAR_MIPMAP_LINEAR = 9987
+ };
+
+ enum WrapMode {
+ CLAMP_TO_EDGE = 33071,
+ MIRRORED_REPEAT = 33648,
+ REPEAT = 10497,
+ DEFAULT = REPEAT
+ };
+
+ int get_mag_filter() const {
+ return mag_filter;
+ }
+
+ void set_mag_filter(const int filter_mode) {
+ mag_filter = (FilterMode)filter_mode;
+ }
+
+ int get_min_filter() const {
+ return min_filter;
+ }
+
+ void set_min_filter(const int filter_mode) {
+ min_filter = (FilterMode)filter_mode;
+ }
+
+ int get_wrap_s() const {
+ return wrap_s;
+ }
+
+ void set_wrap_s(const int wrap_mode) {
+ wrap_s = (WrapMode)wrap_mode;
+ }
+
+ int get_wrap_t() const {
+ return wrap_t;
+ }
+
+ void set_wrap_t(const int wrap_mode) {
+ wrap_s = (WrapMode)wrap_mode;
+ }
+
+ StandardMaterial3D::TextureFilter get_filter_mode() const {
+ using TextureFilter = StandardMaterial3D::TextureFilter;
+
+ switch (min_filter) {
+ case NEAREST:
+ return TextureFilter::TEXTURE_FILTER_NEAREST;
+ case LINEAR:
+ return TextureFilter::TEXTURE_FILTER_LINEAR;
+ case NEAREST_MIPMAP_NEAREST:
+ case NEAREST_MIPMAP_LINEAR:
+ return TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS;
+ case LINEAR_MIPMAP_NEAREST:
+ case LINEAR_MIPMAP_LINEAR:
+ default:
+ return TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS;
+ }
+ }
+
+ void set_filter_mode(StandardMaterial3D::TextureFilter mode) {
+ using TextureFilter = StandardMaterial3D::TextureFilter;
+
+ switch (mode) {
+ case TextureFilter::TEXTURE_FILTER_NEAREST:
+ min_filter = FilterMode::NEAREST;
+ mag_filter = FilterMode::NEAREST;
+ break;
+ case TextureFilter::TEXTURE_FILTER_LINEAR:
+ min_filter = FilterMode::LINEAR;
+ mag_filter = FilterMode::LINEAR;
+ break;
+ case TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
+ case TextureFilter::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC:
+ min_filter = FilterMode::NEAREST_MIPMAP_LINEAR;
+ mag_filter = FilterMode::NEAREST;
+ break;
+ case TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
+ case TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC:
+ default:
+ min_filter = FilterMode::LINEAR_MIPMAP_LINEAR;
+ mag_filter = FilterMode::LINEAR;
+ break;
+ }
+ }
+
+ bool get_wrap_mode() const {
+ // BaseMaterial3D presents wrapping as a boolean property. Either the texture is repeated
+ // in both dimensions, non-mirrored, or it isn't repeated at all. This will cause oddities
+ // when people import models having other wrapping mode combinations.
+ return (wrap_s == WrapMode::REPEAT) && (wrap_t == WrapMode::REPEAT);
+ }
+
+ void set_wrap_mode(bool mat_repeats) {
+ if (mat_repeats) {
+ wrap_s = WrapMode::REPEAT;
+ wrap_t = WrapMode::REPEAT;
+ } else {
+ wrap_s = WrapMode::CLAMP_TO_EDGE;
+ wrap_t = WrapMode::CLAMP_TO_EDGE;
+ }
+ }
+
+protected:
+ static void _bind_methods();
+
+private:
+ FilterMode mag_filter = FilterMode::LINEAR;
+ FilterMode min_filter = FilterMode::LINEAR_MIPMAP_LINEAR;
+ WrapMode wrap_s = WrapMode::REPEAT;
+ WrapMode wrap_t = WrapMode::REPEAT;
+};
+
+VARIANT_ENUM_CAST(GLTFTextureSampler::FilterMode);
+VARIANT_ENUM_CAST(GLTFTextureSampler::WrapMode);
+
+#endif // GLTF_TEXTURE_SAMPLER_H
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub
index 52777235b8..282d772592 100644
--- a/modules/gridmap/SCsub
+++ b/modules/gridmap/SCsub
@@ -5,7 +5,7 @@ Import("env_modules")
env_gridmap = env_modules.Clone()
-# Godot's own source files
+# Godot source files
env_gridmap.add_source_files(env.modules_sources, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_gridmap.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 499f54e3ba..e0fc4e2697 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -25,12 +25,14 @@
<method name="clear_baked_meshes">
<return type="void" />
<description>
+ Clears all baked meshes. See [method make_baked_meshes].
</description>
</method>
<method name="get_bake_mesh_instance">
<return type="RID" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
+ Returns [RID] of a baked mesh with the given [param idx].
</description>
</method>
<method name="get_bake_meshes">
@@ -39,30 +41,44 @@
Returns an array of [ArrayMesh]es and [Transform3D] references of all bake meshes that exist within the current GridMap.
</description>
</method>
+ <method name="get_basis_with_orthogonal_index" qualifiers="const">
+ <return type="Basis" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns one of 24 possible rotations that lie along the vectors (x,y,z) with each component being either -1, 0, or 1. For further details, refer to the Godot source code.
+ </description>
+ </method>
<method name="get_cell_item" qualifiers="const">
<return type="int" />
- <argument index="0" name="position" type="Vector3i" />
+ <param index="0" name="position" type="Vector3i" />
<description>
The [MeshLibrary] item index located at the given grid coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned.
</description>
</method>
+ <method name="get_cell_item_basis" qualifiers="const">
+ <return type="Basis" />
+ <param index="0" name="position" type="Vector3i" />
+ <description>
+ Returns the basis that gives the specified cell its orientation.
+ </description>
+ </method>
<method name="get_cell_item_orientation" qualifiers="const">
<return type="int" />
- <argument index="0" name="position" type="Vector3i" />
+ <param index="0" name="position" type="Vector3i" />
<description>
The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty.
</description>
</method>
<method name="get_collision_layer_value" qualifiers="const">
<return type="bool" />
- <argument index="0" name="layer_number" type="int" />
+ <param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
- <argument index="0" name="layer_number" type="int" />
+ <param index="0" name="layer_number" type="int" />
<description>
Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
@@ -70,96 +86,103 @@
<method name="get_meshes" qualifiers="const">
<return type="Array" />
<description>
- Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
+ Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in local space.
</description>
</method>
- <method name="get_navigation_layer_value" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="layer_number" type="int" />
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the [RID] of the navigation map this GridMap node uses for its cell baked navigation meshes.
+ This function returns always the map set on the GridMap node and not the map on the NavigationServer. If the map is changed directly with the NavigationServer API the GridMap node will not be aware of the map change.
+ </description>
+ </method>
+ <method name="get_orthogonal_index_from_basis" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="basis" type="Basis" />
<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.
+ This function considers a discretization of rotations into 24 points on unit sphere, lying along the vectors (x,y,z) with each component being either -1, 0, or 1, and returns the index (in the range from 0 to 23) of the point best representing the orientation of the object. For further details, refer to the Godot source code.
</description>
</method>
<method name="get_used_cells" qualifiers="const">
- <return type="Array" />
+ <return type="Vector3i[]" />
<description>
Returns an array of [Vector3] with the non-empty cell coordinates in the grid map.
</description>
</method>
<method name="get_used_cells_by_item" qualifiers="const">
- <return type="Array" />
- <argument index="0" name="item" type="int" />
+ <return type="Vector3i[]" />
+ <param index="0" name="item" type="int" />
<description>
Returns an array of all cells with the given item index specified in [code]item[/code].
</description>
</method>
+ <method name="local_to_map" qualifiers="const">
+ <return type="Vector3i" />
+ <param index="0" name="local_position" type="Vector3" />
+ <description>
+ Returns the map coordinates of the cell containing the given [param local_position]. If [param local_position] is in global coordinates, consider using [method Node3D.to_local] before passing it to this method. See also [method map_to_local].
+ </description>
+ </method>
<method name="make_baked_meshes">
<return type="void" />
- <argument index="0" name="gen_lightmap_uv" type="bool" default="false" />
- <argument index="1" name="lightmap_uv_texel_size" type="float" default="0.1" />
+ <param index="0" name="gen_lightmap_uv" type="bool" default="false" />
+ <param index="1" name="lightmap_uv_texel_size" type="float" default="0.1" />
<description>
+ Bakes lightmap data for all meshes in the assigned [MeshLibrary].
</description>
</method>
- <method name="map_to_world" qualifiers="const">
+ <method name="map_to_local" qualifiers="const">
<return type="Vector3" />
- <argument index="0" name="map_position" type="Vector3i" />
+ <param index="0" name="map_position" type="Vector3i" />
<description>
- Returns the position of a grid cell in the GridMap's local coordinate space.
+ Returns the position of a grid cell in the GridMap's local coordinate space. To convert the returned value into global coordinates, use [method Node3D.to_global]. See also [method map_to_local].
</description>
</method>
<method name="resource_changed">
<return type="void" />
- <argument index="0" name="resource" type="Resource" />
+ <param index="0" name="resource" type="Resource" />
<description>
+ Notifies the [GridMap] about changed resource and recreates octant data.
</description>
</method>
<method name="set_cell_item">
<return type="void" />
- <argument index="0" name="position" type="Vector3i" />
- <argument index="1" name="item" type="int" />
- <argument index="2" name="orientation" type="int" default="0" />
+ <param index="0" name="position" type="Vector3i" />
+ <param index="1" name="item" type="int" />
+ <param index="2" name="orientation" type="int" default="0" />
<description>
Sets the mesh index for the cell referenced by its grid coordinates.
A negative item index such as [constant INVALID_CELL_ITEM] will clear the cell.
- Optionally, the item's orientation can be passed. For valid orientation values, see [method Basis.get_orthogonal_index].
+ Optionally, the item's orientation can be passed. For valid orientation values, see [method get_orthogonal_index_from_basis].
</description>
</method>
<method name="set_collision_layer_value">
<return type="void" />
- <argument index="0" name="layer_number" type="int" />
- <argument index="1" name="value" type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
<description>
Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="set_collision_mask_value">
<return type="void" />
- <argument index="0" name="layer_number" type="int" />
- <argument index="1" name="value" type="bool" />
+ <param index="0" name="layer_number" type="int" />
+ <param index="1" name="value" type="bool" />
<description>
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">
+ <method name="set_navigation_map">
<return type="void" />
- <argument index="0" name="layer_number" type="int" />
- <argument index="1" name="value" type="bool" />
+ <param index="0" name="navigation_map" type="RID" />
<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" />
- <description>
- Returns the coordinates of the grid cell containing the given point.
- [code]pos[/code] should be in the GridMap's local coordinate space.
+ Sets the [RID] of the navigation map this GridMap node should use for its cell baked navigation meshes.
</description>
</method>
</methods>
<members>
<member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
- If [code]true[/code], this GridMap bakes a navigation region.
+ If [code]true[/code], this GridMap creates a navigation region for each cell that uses a [member mesh_library] item with a navigation mesh. The created navigation region will use the navigation layers bitmask assigned to the [MeshLibrary]'s item.
</member>
<member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true">
If [code]true[/code], grid items are centered on the X axis.
@@ -188,19 +211,19 @@
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
The physics layers this GridMap detects collisions in. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
+ <member name="collision_priority" type="float" setter="set_collision_priority" getter="get_collision_priority" default="1.0">
+ The priority used to solve colliding when occurring penetration. The higher the priority is, the lower the penetration into the object will be. This can for example be used to prevent the player from breaking through the boundaries of a level.
+ </member>
<member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary].
</member>
- <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
- 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].
</member>
</members>
<signals>
<signal name="cell_size_changed">
- <argument index="0" name="cell_size" type="Vector3" />
+ <param index="0" name="cell_size" type="Vector3" />
<description>
Emitted when [member cell_size] changes.
</description>
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 09f0ff32f0..183190460e 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* grid_map_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* grid_map_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "grid_map_editor_plugin.h"
@@ -37,8 +37,11 @@
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/separator.h"
#include "scene/main/window.h"
void GridMapEditor::_node_removed(Node *p_node) {
@@ -94,91 +97,91 @@ void GridMapEditor::_menu_option(int p_option) {
case MENU_OPTION_CURSOR_ROTATE_Y: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(0, 1, 0), -Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_ROTATE_X: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(1, 0, 0), -Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(1, 0, 0), -Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_ROTATE_Z: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(0, 0, 1), -Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(0, 0, 1), -Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_BACK_ROTATE_Y: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(0, 1, 0), Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(0, 1, 0), Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_BACK_ROTATE_X: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(1, 0, 0), Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(1, 0, 0), Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_BACK_ROTATE_Z: {
Basis r;
if (input_action == INPUT_PASTE) {
- r.set_orthogonal_index(paste_indicator.orientation);
+ r = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
r.rotate(Vector3(0, 0, 1), Math_PI / 2.0);
- paste_indicator.orientation = r.get_orthogonal_index();
+ paste_indicator.orientation = node->get_orthogonal_index_from_basis(r);
_update_paste_indicator();
break;
}
- r.set_orthogonal_index(cursor_rot);
+ r = node->get_basis_with_orthogonal_index(cursor_rot);
r.rotate(Vector3(0, 0, 1), Math_PI / 2.0);
- cursor_rot = r.get_orthogonal_index();
+ cursor_rot = node->get_orthogonal_index_from_basis(r);
_update_cursor_transform();
} break;
case MENU_OPTION_CURSOR_CLEAR_ROTATION: {
@@ -242,7 +245,7 @@ void GridMapEditor::_menu_option(int p_option) {
void GridMapEditor::_update_cursor_transform() {
cursor_transform = Transform3D();
cursor_transform.origin = cursor_origin;
- cursor_transform.basis.set_orthogonal_index(cursor_rot);
+ cursor_transform.basis = node->get_basis_with_orthogonal_index(cursor_rot);
cursor_transform.basis *= node->get_cell_scale();
cursor_transform = node->get_global_transform() * cursor_transform;
@@ -458,6 +461,7 @@ void GridMapEditor::_delete_selection() {
return;
}
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Delete Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -478,6 +482,7 @@ void GridMapEditor::_fill_selection() {
return;
}
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Fill Selection"));
for (int i = selection.begin.x; i <= selection.end.x; i++) {
for (int j = selection.begin.y; j <= selection.end.y; j++) {
@@ -543,7 +548,7 @@ void GridMapEditor::_update_paste_indicator() {
xf.scale(scale);
xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
Basis rot;
- rot.set_orthogonal_index(paste_indicator.orientation);
+ rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
xf.basis = rot * xf.basis;
xf.translate_local((-center * node->get_cell_size()) / scale);
@@ -556,7 +561,7 @@ void GridMapEditor::_update_paste_indicator() {
xf.translate_local(item.grid_offset * node->get_cell_size());
Basis item_rot;
- item_rot.set_orthogonal_index(item.orientation);
+ item_rot = node->get_basis_with_orthogonal_index(item.orientation);
xf.basis = item_rot * xf.basis * node->get_cell_scale();
RenderingServer::get_singleton()->instance_set_transform(item.instance, node->get_global_transform() * xf);
@@ -568,19 +573,20 @@ void GridMapEditor::_do_paste() {
bool reselect = options->get_popup()->is_item_checked(idx);
Basis rot;
- rot.set_orthogonal_index(paste_indicator.orientation);
+ rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation);
Vector3 ofs = paste_indicator.current - paste_indicator.click;
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paste Selection"));
for (const ClipboardItem &item : clipboard_items) {
Vector3 position = rot.xform(item.grid_offset) + paste_indicator.begin + ofs;
Basis orm;
- orm.set_orthogonal_index(item.orientation);
+ orm = node->get_basis_with_orthogonal_index(item.orientation);
orm = rot * orm;
- undo_redo->add_do_method(node, "set_cell_item", position, item.cell_item, orm.get_orthogonal_index());
+ undo_redo->add_do_method(node, "set_cell_item", position, item.cell_item, node->get_orthogonal_index_from_basis(orm));
undo_redo->add_undo_method(node, "set_cell_item", position, node->get_cell_item(position), node->get_cell_item_orientation(position));
}
@@ -602,13 +608,13 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_pressed() || mb->is_shift_pressed())) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_or_control_pressed() || mb->is_shift_pressed())) {
if (mb->is_pressed()) {
floor->set_value(floor->get_value() + mb->get_factor());
}
return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten.
- } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && (mb->is_command_or_control_pressed() || mb->is_shift_pressed())) {
if (mb->is_pressed()) {
floor->set_value(floor->get_value() - mb->get_factor());
}
@@ -616,7 +622,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
if (mb->is_pressed()) {
- Node3DEditorViewport::NavigationScheme nav_scheme = (Node3DEditorViewport::NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
+ Node3DEditorViewport::NavigationScheme nav_scheme = (Node3DEditorViewport::NavigationScheme)EDITOR_GET("editors/3d/navigation/navigation_scheme").operator int();
if ((nav_scheme == Node3DEditorViewport::NAVIGATION_MAYA || nav_scheme == Node3DEditorViewport::NAVIGATION_MODO) && mb->is_alt_pressed()) {
input_action = INPUT_NONE;
} else if (mb->get_button_index() == MouseButton::LEFT) {
@@ -628,7 +634,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else if (mb->is_shift_pressed() && can_edit) {
input_action = INPUT_SELECT;
last_selection = selection;
- } else if (mb->is_command_pressed() && can_edit) {
+ } else if (mb->is_command_or_control_pressed() && can_edit) {
input_action = INPUT_PICK;
} else {
input_action = INPUT_PAINT;
@@ -658,6 +664,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else {
if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paint"));
for (const SetItem &si : set_items) {
undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
@@ -679,6 +686,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Selection"));
undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
@@ -745,7 +753,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
- if (pan_gesture->is_alt_pressed() && (pan_gesture->is_command_pressed() || pan_gesture->is_shift_pressed())) {
+ if (pan_gesture->is_alt_pressed() && (pan_gesture->is_command_or_control_pressed() || pan_gesture->is_shift_pressed())) {
const real_t delta = pan_gesture->get_delta().y * 0.5;
accumulated_floor_delta += delta;
int step = 0;
@@ -806,7 +814,7 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) {
const Ref<InputEventMouseButton> mb = p_ie;
// Zoom in/out using Ctrl + mouse wheel
- if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed()) {
+ if (mb.is_valid() && mb->is_pressed() && mb->is_command_or_control_pressed()) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
size_slider->set_value(size_slider->get_value() + 0.2);
}
@@ -896,10 +904,12 @@ void GridMapEditor::update_palette() {
}
if (selected != -1 && mesh_library_palette->get_item_count() > 0) {
- mesh_library_palette->select(selected);
+ // Make sure that this variable is set correctly.
+ selected_palette = MIN(selected, mesh_library_palette->get_item_count() - 1);
+ mesh_library_palette->select(selected_palette);
}
- last_mesh_library = mesh_library.operator->();
+ last_mesh_library = *mesh_library;
}
void GridMapEditor::edit(GridMap *p_gridmap) {
@@ -1139,8 +1149,6 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor() {
- undo_redo = EditorNode::get_singleton()->get_undo_redo();
-
int mw = EDITOR_DEF("editors/grid_map/palette_min_width", 230);
Control *ec = memnew(Control);
ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE);
@@ -1397,6 +1405,7 @@ GridMapEditor::GridMapEditor() {
}
GridMapEditor::~GridMapEditor() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
_clear_clipboard_data();
for (int i = 0; i < 3; i++) {
@@ -1431,7 +1440,7 @@ GridMapEditor::~GridMapEditor() {
void GridMapEditorPlugin::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
+ switch ((int)EDITOR_GET("editors/grid_map/editor_side")) {
case 0: { // Left.
Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor);
} break;
@@ -1469,7 +1478,7 @@ GridMapEditorPlugin::GridMapEditorPlugin() {
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/grid_map/editor_side", PROPERTY_HINT_ENUM, "Left,Right"));
grid_map_editor = memnew(GridMapEditor);
- switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) {
+ switch ((int)EDITOR_GET("editors/grid_map/editor_side")) {
case 0: { // Left.
Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor);
} break;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 3b29397502..7c7dbacc81 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* grid_map_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* grid_map_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GRID_MAP_EDITOR_PLUGIN_H
#define GRID_MAP_EDITOR_PLUGIN_H
@@ -35,10 +35,13 @@
#include "../grid_map.h"
#include "editor/editor_plugin.h"
+#include "scene/gui/box_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
+class ConfirmationDialog;
+class MenuButton;
class Node3DEditorPlugin;
class GridMapEditor : public VBoxContainer {
@@ -62,7 +65,6 @@ class GridMapEditor : public VBoxContainer {
DISPLAY_LIST
};
- UndoRedo *undo_redo = nullptr;
InputAction input_action = INPUT_NONE;
Panel *panel = nullptr;
MenuButton *options = nullptr;
@@ -240,7 +242,7 @@ protected:
void _notification(int p_what);
public:
- virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
virtual String get_name() const override { return "GridMap"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 7d80cbef7c..3c0bd56e86 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* grid_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* grid_map.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "grid_map.h"
@@ -74,7 +74,7 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) {
bm.mesh = meshes[i];
ERR_CONTINUE(!bm.mesh.is_valid());
bm.instance = RS::get_singleton()->instance_create();
- RS::get_singleton()->get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid());
+ RS::get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid());
RS::get_singleton()->instance_attach_object_instance_id(bm.instance, get_instance_id());
if (is_inside_tree()) {
RS::get_singleton()->instance_set_scenario(bm.instance, get_world_3d()->get_scenario());
@@ -138,7 +138,7 @@ void GridMap::_get_property_list(List<PropertyInfo> *p_list) const {
void GridMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
- _reset_physic_bodies_collision_filters();
+ _update_physics_bodies_collision_properties();
}
uint32_t GridMap::get_collision_layer() const {
@@ -147,7 +147,7 @@ uint32_t GridMap::get_collision_layer() const {
void GridMap::set_collision_mask(uint32_t p_mask) {
collision_mask = p_mask;
- _reset_physic_bodies_collision_filters();
+ _update_physics_bodies_collision_properties();
}
uint32_t GridMap::get_collision_mask() const {
@@ -157,13 +157,13 @@ uint32_t GridMap::get_collision_mask() const {
void GridMap::set_collision_layer_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
- uint32_t collision_layer = get_collision_layer();
+ uint32_t collision_layer_new = get_collision_layer();
if (p_value) {
- collision_layer |= 1 << (p_layer_number - 1);
+ collision_layer_new |= 1 << (p_layer_number - 1);
} else {
- collision_layer &= ~(1 << (p_layer_number - 1));
+ collision_layer_new &= ~(1 << (p_layer_number - 1));
}
- set_collision_layer(collision_layer);
+ set_collision_layer(collision_layer_new);
}
bool GridMap::get_collision_layer_value(int p_layer_number) const {
@@ -184,6 +184,15 @@ void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) {
set_collision_mask(mask);
}
+void GridMap::set_collision_priority(real_t p_priority) {
+ collision_priority = p_priority;
+ _update_physics_bodies_collision_properties();
+}
+
+real_t GridMap::get_collision_priority() const {
+ return collision_priority;
+}
+
void GridMap::set_physics_material(Ref<PhysicsMaterial> p_material) {
physics_material = p_material;
_recreate_octant_data();
@@ -226,31 +235,25 @@ bool GridMap::is_baking_navigation() {
return bake_navigation;
}
-void GridMap::set_navigation_layers(uint32_t p_navigation_layers) {
- navigation_layers = p_navigation_layers;
- _recreate_octant_data();
-}
-
-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));
+void GridMap::set_navigation_map(RID p_navigation_map) {
+ map_override = p_navigation_map;
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ Octant &g = *octant_map[E.key];
+ for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) {
+ if (F.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_map(F.value.region, map_override);
+ }
+ }
}
- 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));
+RID GridMap::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_3d()->get_navigation_map();
+ }
+ return RID();
}
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
@@ -364,6 +367,7 @@ void GridMap::set_cell_item(const Vector3i &p_position, int p_item, int p_rot) {
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(g->static_body, get_instance_id());
PhysicsServer3D::get_singleton()->body_set_collision_layer(g->static_body, collision_layer);
PhysicsServer3D::get_singleton()->body_set_collision_mask(g->static_body, collision_mask);
+ PhysicsServer3D::get_singleton()->body_set_collision_priority(g->static_body, collision_priority);
if (physics_material.is_valid()) {
PhysicsServer3D::get_singleton()->body_set_param(g->static_body, PhysicsServer3D::BODY_PARAM_FRICTION, physics_material->get_friction());
PhysicsServer3D::get_singleton()->body_set_param(g->static_body, PhysicsServer3D::BODY_PARAM_BOUNCE, physics_material->get_bounce());
@@ -428,18 +432,87 @@ int GridMap::get_cell_item_orientation(const Vector3i &p_position) const {
return cell_map[key].rot;
}
-Vector3i GridMap::world_to_map(const Vector3 &p_world_position) const {
+static const Basis _ortho_bases[24] = {
+ Basis(1, 0, 0, 0, 1, 0, 0, 0, 1),
+ Basis(0, -1, 0, 1, 0, 0, 0, 0, 1),
+ Basis(-1, 0, 0, 0, -1, 0, 0, 0, 1),
+ Basis(0, 1, 0, -1, 0, 0, 0, 0, 1),
+ Basis(1, 0, 0, 0, 0, -1, 0, 1, 0),
+ Basis(0, 0, 1, 1, 0, 0, 0, 1, 0),
+ Basis(-1, 0, 0, 0, 0, 1, 0, 1, 0),
+ Basis(0, 0, -1, -1, 0, 0, 0, 1, 0),
+ Basis(1, 0, 0, 0, -1, 0, 0, 0, -1),
+ Basis(0, 1, 0, 1, 0, 0, 0, 0, -1),
+ Basis(-1, 0, 0, 0, 1, 0, 0, 0, -1),
+ Basis(0, -1, 0, -1, 0, 0, 0, 0, -1),
+ Basis(1, 0, 0, 0, 0, 1, 0, -1, 0),
+ Basis(0, 0, -1, 1, 0, 0, 0, -1, 0),
+ Basis(-1, 0, 0, 0, 0, -1, 0, -1, 0),
+ Basis(0, 0, 1, -1, 0, 0, 0, -1, 0),
+ Basis(0, 0, 1, 0, 1, 0, -1, 0, 0),
+ Basis(0, -1, 0, 0, 0, 1, -1, 0, 0),
+ Basis(0, 0, -1, 0, -1, 0, -1, 0, 0),
+ Basis(0, 1, 0, 0, 0, -1, -1, 0, 0),
+ Basis(0, 0, 1, 0, -1, 0, 1, 0, 0),
+ Basis(0, 1, 0, 0, 0, 1, 1, 0, 0),
+ Basis(0, 0, -1, 0, 1, 0, 1, 0, 0),
+ Basis(0, -1, 0, 0, 0, -1, 1, 0, 0)
+};
+
+Basis GridMap::get_cell_item_basis(const Vector3i &p_position) const {
+ int orientation = get_cell_item_orientation(p_position);
+
+ if (orientation == -1) {
+ return Basis();
+ }
+
+ return get_basis_with_orthogonal_index(orientation);
+}
+
+Basis GridMap::get_basis_with_orthogonal_index(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, 24, Basis());
+
+ return _ortho_bases[p_index];
+}
+
+int GridMap::get_orthogonal_index_from_basis(const Basis &p_basis) const {
+ Basis orth = p_basis;
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ real_t v = orth[i][j];
+ if (v > 0.5) {
+ v = 1.0;
+ } else if (v < -0.5) {
+ v = -1.0;
+ } else {
+ v = 0;
+ }
+
+ orth[i][j] = v;
+ }
+ }
+
+ for (int i = 0; i < 24; i++) {
+ if (_ortho_bases[i] == orth) {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+Vector3i GridMap::local_to_map(const Vector3 &p_world_position) const {
Vector3 map_position = (p_world_position / cell_size).floor();
return Vector3i(map_position);
}
-Vector3 GridMap::map_to_world(const Vector3i &p_map_position) const {
+Vector3 GridMap::map_to_local(const Vector3i &p_map_position) const {
Vector3 offset = _get_offset();
- Vector3 world_pos(
+ Vector3 local_position(
p_map_position.x * cell_size.x + offset.x,
p_map_position.y * cell_size.y + offset.y,
p_map_position.z * cell_size.z + offset.z);
- return world_pos;
+ return local_position;
}
void GridMap::_octant_transform(const OctantKey &p_key) {
@@ -452,13 +525,13 @@ void GridMap::_octant_transform(const OctantKey &p_key) {
}
// update transform for NavigationServer regions and navigation debugmesh instances
- for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_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);
+ if (E.value.navigation_mesh_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(E.value.navigation_mesh_debug_instance, get_global_transform() * E.value.xform);
}
}
}
@@ -484,13 +557,13 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
//erase navigation
- for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_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);
+ if (E.value.navigation_mesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navigation_mesh_debug_instance);
}
}
- g.navmesh_ids.clear();
+ g.navigation_cell_ids.clear();
//erase multimeshes
@@ -529,7 +602,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Transform3D xform;
- xform.basis.set_orthogonal_index(c.rot);
+ xform.basis = _ortho_bases[c.rot];
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
if (baked_meshes.size() == 0) {
@@ -558,40 +631,54 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
}
- // add the item's navmesh at given xform to GridMap's Navigation ancestor
- Ref<NavigationMesh> navmesh = mesh_library->get_item_navmesh(c.item);
- if (navmesh.is_valid()) {
- Octant::NavMesh nm;
- nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item);
+ // add the item's navigation_mesh at given xform to GridMap's Navigation ancestor
+ Ref<NavigationMesh> navigation_mesh = mesh_library->get_item_navigation_mesh(c.item);
+ if (navigation_mesh.is_valid()) {
+ Octant::NavigationCell nm;
+ nm.xform = xform * mesh_library->get_item_navigation_mesh_transform(c.item);
+ nm.navigation_layers = mesh_library->get_item_navigation_layers(c.item);
if (bake_navigation) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
- NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh);
+ NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id());
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, nm.navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, navigation_mesh);
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());
+ if (is_inside_tree()) {
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_map(region, map_override);
+ } else {
+ NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
+ }
+ }
nm.region = region;
+#ifdef DEBUG_ENABLED
// 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 (!nm.navigation_mesh_debug_instance.is_valid()) {
+ RID navigation_mesh_debug_rid = navigation_mesh->get_debug_mesh()->get_rid();
+ nm.navigation_mesh_debug_instance = RS::get_singleton()->instance_create();
+ RS::get_singleton()->instance_set_base(nm.navigation_mesh_debug_instance, navigation_mesh_debug_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);
+ RS::get_singleton()->instance_set_scenario(nm.navigation_mesh_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_transform(nm.navigation_mesh_debug_instance, get_global_transform() * nm.xform);
}
}
+#endif // DEBUG_ENABLED
}
-
- g.navmesh_ids[E] = nm;
+ g.navigation_cell_ids[E] = nm;
}
}
+#ifdef DEBUG_ENABLED
+ if (bake_navigation) {
+ _update_octant_navigation_debug_edge_connections_mesh(p_key);
+ }
+#endif // DEBUG_ENABLED
+
//update multimeshes, only if not baked
if (baked_meshes.size() == 0) {
for (const KeyValue<int, List<Pair<Transform3D, IndexKey>>> &E : multimesh_items) {
@@ -648,10 +735,11 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
return false;
}
-void GridMap::_reset_physic_bodies_collision_filters() {
+void GridMap::_update_physics_bodies_collision_properties() {
for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
PhysicsServer3D::get_singleton()->body_set_collision_layer(E.value->static_body, collision_layer);
PhysicsServer3D::get_singleton()->body_set_collision_mask(E.value->static_body, collision_mask);
+ PhysicsServer3D::get_singleton()->body_set_collision_priority(E.value->static_body, collision_priority);
}
}
@@ -672,24 +760,46 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
}
if (bake_navigation && mesh_library.is_valid()) {
- for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) {
+ for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) {
if (cell_map.has(F.key) && F.value.region.is_valid() == false) {
- Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F.key].item);
- if (nm.is_valid()) {
+ Ref<NavigationMesh> navigation_mesh = mesh_library->get_item_navigation_mesh(cell_map[F.key].item);
+ if (navigation_mesh.is_valid()) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
- NavigationServer3D::get_singleton()->region_set_navmesh(region, nm);
+ NavigationServer3D::get_singleton()->region_set_owner_id(region, get_instance_id());
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, F.value.navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_mesh(region, navigation_mesh);
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());
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_map(region, map_override);
+ } else {
+ NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
+ }
F.value.region = region;
}
}
}
+
+#ifdef DEBUG_ENABLED
+ if (bake_navigation) {
+ if (!g.navigation_debug_edge_connections_instance.is_valid()) {
+ g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
+ }
+ if (!g.navigation_debug_edge_connections_mesh.is_valid()) {
+ g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+
+ _update_octant_navigation_debug_edge_connections_mesh(p_key);
+ }
+#endif // DEBUG_ENABLED
}
}
void GridMap::_octant_exit_world(const OctantKey &p_key) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+
ERR_FAIL_COND(!octant_map.has(p_key));
Octant &g = *octant_map[p_key];
PhysicsServer3D::get_singleton()->body_set_state(g.static_body, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
@@ -703,19 +813,35 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
RS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID());
}
- for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) {
+ for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) {
if (F.value.region.is_valid()) {
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();
+ if (F.value.navigation_mesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(F.value.navigation_mesh_debug_instance);
+ F.value.navigation_mesh_debug_instance = RID();
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ if (bake_navigation) {
+ if (g.navigation_debug_edge_connections_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_instance);
+ g.navigation_debug_edge_connections_instance = RID();
+ }
+ if (g.navigation_debug_edge_connections_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid());
}
}
+#endif // DEBUG_ENABLED
}
void GridMap::_octant_clean_up(const OctantKey &p_key) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ ERR_FAIL_NULL(PhysicsServer3D::get_singleton());
+ ERR_FAIL_NULL(NavigationServer3D::get_singleton());
+
ERR_FAIL_COND(!octant_map.has(p_key));
Octant &g = *octant_map[p_key];
@@ -729,15 +855,27 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
PhysicsServer3D::get_singleton()->free(g.static_body);
// Erase navigation
- for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) {
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);
+ if (E.value.navigation_mesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navigation_mesh_debug_instance);
}
}
- g.navmesh_ids.clear();
+ g.navigation_cell_ids.clear();
+
+#ifdef DEBUG_ENABLED
+ if (bake_navigation) {
+ if (g.navigation_debug_edge_connections_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_instance);
+ g.navigation_debug_edge_connections_instance = RID();
+ }
+ if (g.navigation_debug_edge_connections_mesh.is_valid()) {
+ RenderingServer::get_singleton()->free(g.navigation_debug_edge_connections_mesh->get_rid());
+ }
+ }
+#endif // DEBUG_ENABLED
//erase multimeshes
@@ -763,6 +901,14 @@ void GridMap::_notification(int p_what) {
}
} break;
+#ifdef DEBUG_ENABLED
+ case NOTIFICATION_ENTER_TREE: {
+ if (bake_navigation && NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ _update_navigation_debug_edge_connections();
+ }
+ } break;
+#endif // DEBUG_ENABLED
+
case NOTIFICATION_TRANSFORM_CHANGED: {
Transform3D new_xform = get_global_transform();
if (new_xform == last_transform) {
@@ -894,17 +1040,17 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value);
ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value);
+ ClassDB::bind_method(D_METHOD("set_collision_priority", "priority"), &GridMap::set_collision_priority);
+ ClassDB::bind_method(D_METHOD("get_collision_priority"), &GridMap::get_collision_priority);
+
ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &GridMap::set_physics_material);
ClassDB::bind_method(D_METHOD("get_physics_material"), &GridMap::get_physics_material);
ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation);
ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation);
- 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_navigation_map", "navigation_map"), &GridMap::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &GridMap::get_navigation_map);
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);
@@ -921,9 +1067,12 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_item", "position", "item", "orientation"), &GridMap::set_cell_item, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_cell_item", "position"), &GridMap::get_cell_item);
ClassDB::bind_method(D_METHOD("get_cell_item_orientation", "position"), &GridMap::get_cell_item_orientation);
+ ClassDB::bind_method(D_METHOD("get_cell_item_basis", "position"), &GridMap::get_cell_item_basis);
+ ClassDB::bind_method(D_METHOD("get_basis_with_orthogonal_index", "index"), &GridMap::get_basis_with_orthogonal_index);
+ ClassDB::bind_method(D_METHOD("get_orthogonal_index_from_basis", "basis"), &GridMap::get_orthogonal_index_from_basis);
- ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &GridMap::world_to_map);
- ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &GridMap::map_to_world);
+ ClassDB::bind_method(D_METHOD("local_to_map", "local_position"), &GridMap::local_to_map);
+ ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &GridMap::map_to_local);
ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback);
ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed);
@@ -959,9 +1108,9 @@ void GridMap::_bind_methods() {
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_priority"), "set_collision_priority", "get_collision_priority");
ADD_GROUP("Navigation", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
BIND_CONSTANT(INVALID_CELL_ITEM);
@@ -977,23 +1126,23 @@ float GridMap::get_cell_scale() const {
return cell_scale;
}
-Array GridMap::get_used_cells() const {
- Array a;
+TypedArray<Vector3i> GridMap::get_used_cells() const {
+ TypedArray<Vector3i> a;
a.resize(cell_map.size());
int i = 0;
for (const KeyValue<IndexKey, Cell> &E : cell_map) {
- Vector3 p(E.key.x, E.key.y, E.key.z);
+ Vector3i p(E.key.x, E.key.y, E.key.z);
a[i++] = p;
}
return a;
}
-Array GridMap::get_used_cells_by_item(int p_item) const {
- Array a;
+TypedArray<Vector3i> GridMap::get_used_cells_by_item(int p_item) const {
+ TypedArray<Vector3i> a;
for (const KeyValue<IndexKey, Cell> &E : cell_map) {
- if (E.value.item == p_item) {
- Vector3 p(E.key.x, E.key.y, E.key.z);
+ if ((int)E.value.item == p_item) {
+ Vector3i p(E.key.x, E.key.y, E.key.z);
a.push_back(p);
}
}
@@ -1025,7 +1174,7 @@ Array GridMap::get_meshes() const {
Transform3D xform;
- xform.basis.set_orthogonal_index(E.value.rot);
+ xform.basis = _ortho_bases[E.value.rot];
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
@@ -1045,6 +1194,7 @@ Vector3 GridMap::_get_offset() const {
}
void GridMap::clear_baked_meshes() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->free(baked_meshes[i].instance);
}
@@ -1079,7 +1229,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
Transform3D xform;
- xform.basis.set_orthogonal_index(E.value.rot);
+ xform.basis = _ortho_bases[E.value.rot];
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
@@ -1122,7 +1272,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
BakedMesh bm;
bm.mesh = mesh;
bm.instance = RS::get_singleton()->instance_create();
- RS::get_singleton()->get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid());
+ RS::get_singleton()->instance_set_base(bm.instance, bm.mesh->get_rid());
RS::get_singleton()->instance_attach_object_instance_id(bm.instance, get_instance_id());
if (is_inside_tree()) {
RS::get_singleton()->instance_set_scenario(bm.instance, get_world_3d()->get_scenario());
@@ -1159,12 +1309,136 @@ RID GridMap::get_bake_mesh_instance(int p_idx) {
GridMap::GridMap() {
set_notify_transform(true);
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->connect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->connect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
+#endif // DEBUG_ENABLED
+}
+
+#ifdef DEBUG_ENABLED
+void GridMap::_update_navigation_debug_edge_connections() {
+ if (bake_navigation) {
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ _update_octant_navigation_debug_edge_connections_mesh(E.key);
+ }
+ }
}
+void GridMap::_navigation_map_changed(RID p_map) {
+ if (bake_navigation && is_inside_tree() && p_map == get_world_3d()->get_navigation_map()) {
+ _update_navigation_debug_edge_connections();
+ }
+}
+#endif // DEBUG_ENABLED
+
GridMap::~GridMap() {
if (!mesh_library.is_null()) {
mesh_library->unregister_owner(this);
}
clear();
+#ifdef DEBUG_ENABLED
+ NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
+ NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
+#endif // DEBUG_ENABLED
+}
+
+#ifdef DEBUG_ENABLED
+void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const OctantKey &p_key) {
+ ERR_FAIL_COND(!octant_map.has(p_key));
+ Octant &g = *octant_map[p_key];
+
+ if (!NavigationServer3D::get_singleton()->get_debug_enabled()) {
+ if (g.navigation_debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false);
+ }
+ return;
+ }
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (!bake_navigation) {
+ if (g.navigation_debug_edge_connections_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false);
+ }
+ return;
+ }
+
+ if (!g.navigation_debug_edge_connections_instance.is_valid()) {
+ g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
+ }
+
+ if (!g.navigation_debug_edge_connections_mesh.is_valid()) {
+ g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ }
+
+ g.navigation_debug_edge_connections_mesh->clear_surfaces();
+
+ float edge_connection_margin = NavigationServer3D::get_singleton()->map_get_edge_connection_margin(get_world_3d()->get_navigation_map());
+ float half_edge_connection_margin = edge_connection_margin * 0.5;
+
+ Vector<Vector3> vertex_array;
+
+ for (KeyValue<IndexKey, Octant::NavigationCell> &F : g.navigation_cell_ids) {
+ if (cell_map.has(F.key) && F.value.region.is_valid()) {
+ int connections_count = NavigationServer3D::get_singleton()->region_get_connections_count(F.value.region);
+ if (connections_count == 0) {
+ continue;
+ }
+
+ for (int i = 0; i < connections_count; i++) {
+ Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(F.value.region, i);
+ Vector3 connection_pathway_end = NavigationServer3D::get_singleton()->region_get_connection_pathway_end(F.value.region, i);
+
+ Vector3 direction_start_end = connection_pathway_start.direction_to(connection_pathway_end);
+ Vector3 direction_end_start = connection_pathway_end.direction_to(connection_pathway_start);
+
+ Vector3 start_right_dir = direction_start_end.cross(Vector3(0, 1, 0));
+ Vector3 start_left_dir = -start_right_dir;
+
+ Vector3 end_right_dir = direction_end_start.cross(Vector3(0, 1, 0));
+ Vector3 end_left_dir = -end_right_dir;
+
+ Vector3 left_start_pos = connection_pathway_start + (start_left_dir * half_edge_connection_margin);
+ Vector3 right_start_pos = connection_pathway_start + (start_right_dir * half_edge_connection_margin);
+ Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin);
+ Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin);
+
+ vertex_array.push_back(right_end_pos);
+ vertex_array.push_back(left_start_pos);
+ vertex_array.push_back(right_start_pos);
+
+ vertex_array.push_back(left_end_pos);
+ vertex_array.push_back(right_end_pos);
+ vertex_array.push_back(right_start_pos);
+ }
+ }
+ }
+
+ if (vertex_array.size() == 0) {
+ return;
+ }
+
+ Ref<StandardMaterial3D> edge_connections_material = NavigationServer3D::get_singleton()->get_debug_navigation_edge_connections_material();
+
+ Array mesh_array;
+ mesh_array.resize(Mesh::ARRAY_MAX);
+ mesh_array[Mesh::ARRAY_VERTEX] = vertex_array;
+
+ g.navigation_debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array);
+ g.navigation_debug_edge_connections_mesh->surface_set_material(0, edge_connections_material);
+
+ RS::get_singleton()->instance_set_base(g.navigation_debug_edge_connections_instance, g.navigation_debug_edge_connections_mesh->get_rid());
+ RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, is_visible_in_tree());
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(g.navigation_debug_edge_connections_instance, get_world_3d()->get_scenario());
+ }
+
+ bool enable_edge_connections = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_connections();
+ if (!enable_edge_connections) {
+ RS::get_singleton()->instance_set_visible(g.navigation_debug_edge_connections_instance, false);
+ }
}
+#endif // DEBUG_ENABLED
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 078a1d9de5..18c3f90269 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* grid_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* grid_map.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GRID_MAP_H
#define GRID_MAP_H
@@ -95,10 +95,11 @@ class GridMap : public Node3D {
* A GridMap can have multiple Octants.
*/
struct Octant {
- struct NavMesh {
+ struct NavigationCell {
RID region;
Transform3D xform;
- RID navmesh_debug_instance;
+ RID navigation_mesh_debug_instance;
+ uint32_t navigation_layers = 1;
};
struct MultimeshInstance {
@@ -117,10 +118,14 @@ class GridMap : public Node3D {
HashSet<IndexKey> cells;
RID collision_debug;
RID collision_debug_instance;
+#ifdef DEBUG_ENABLED
+ RID navigation_debug_edge_connections_instance;
+ Ref<ArrayMesh> navigation_debug_edge_connections_mesh;
+#endif // DEBUG_ENABLED
bool dirty = false;
RID static_body;
- HashMap<IndexKey, NavMesh> navmesh_ids;
+ HashMap<IndexKey, NavigationCell> navigation_cell_ids;
};
union OctantKey {
@@ -146,8 +151,10 @@ class GridMap : public Node3D {
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
+ real_t collision_priority = 1.0;
Ref<PhysicsMaterial> physics_material;
bool bake_navigation = false;
+ RID map_override;
uint32_t navigation_layers = 1;
Transform3D last_transform;
@@ -180,12 +187,17 @@ class GridMap : public Node3D {
return Vector3(p_key.x, p_key.y, p_key.z) * cell_size * octant_size;
}
- void _reset_physic_bodies_collision_filters();
+ void _update_physics_bodies_collision_properties();
void _octant_enter_world(const OctantKey &p_key);
void _octant_exit_world(const OctantKey &p_key);
bool _octant_update(const OctantKey &p_key);
void _octant_clean_up(const OctantKey &p_key);
void _octant_transform(const OctantKey &p_key);
+#ifdef DEBUG_ENABLED
+ void _update_octant_navigation_debug_edge_connections_mesh(const OctantKey &p_key);
+ void _navigation_map_changed(RID p_map);
+ void _update_navigation_debug_edge_connections();
+#endif // DEBUG_ENABLED
bool awaiting_update = false;
void _queue_octants_dirty();
@@ -230,6 +242,9 @@ public:
void set_collision_mask_value(int p_layer_number, bool p_value);
bool get_collision_mask_value(int p_layer_number) const;
+ void set_collision_priority(real_t p_priority);
+ real_t get_collision_priority() const;
+
void set_physics_material(Ref<PhysicsMaterial> p_material);
Ref<PhysicsMaterial> get_physics_material() const;
@@ -238,11 +253,8 @@ public:
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
- 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_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
Ref<MeshLibrary> get_mesh_library() const;
@@ -263,15 +275,18 @@ public:
void set_cell_item(const Vector3i &p_position, int p_item, int p_rot = 0);
int get_cell_item(const Vector3i &p_position) const;
int get_cell_item_orientation(const Vector3i &p_position) const;
+ Basis get_cell_item_basis(const Vector3i &p_position) const;
+ Basis get_basis_with_orthogonal_index(int p_index) const;
+ int get_orthogonal_index_from_basis(const Basis &p_basis) const;
- Vector3i world_to_map(const Vector3 &p_world_position) const;
- Vector3 map_to_world(const Vector3i &p_map_position) const;
+ Vector3i local_to_map(const Vector3 &p_local_position) const;
+ Vector3 map_to_local(const Vector3i &p_map_position) const;
void set_cell_scale(float p_scale);
float get_cell_scale() const;
- Array get_used_cells() const;
- Array get_used_cells_by_item(int p_item) const;
+ TypedArray<Vector3i> get_used_cells() const;
+ TypedArray<Vector3i> get_used_cells_by_item(int p_item) const;
Array get_meshes() const;
diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp
index 9efd18a265..bc486bbdc2 100644
--- a/modules/gridmap/register_types.cpp
+++ b/modules/gridmap/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 _3D_DISABLED
diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h
index 28f14cd398..095bd74075 100644
--- a/modules/gridmap/register_types.h
+++ b/modules/gridmap/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GRIDMAP_REGISTER_TYPES_H
#define GRIDMAP_REGISTER_TYPES_H
diff --git a/modules/hdr/SCsub b/modules/hdr/SCsub
index a709397c9a..10629bda3c 100644
--- a/modules/hdr/SCsub
+++ b/modules/hdr/SCsub
@@ -5,5 +5,5 @@ Import("env_modules")
env_hdr = env_modules.Clone()
-# Godot's own source files
+# Godot source files
env_hdr.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index eca689e87a..c49c62a08b 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -1,39 +1,39 @@
-/*************************************************************************/
-/* image_loader_hdr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_hdr.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_hdr.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
String header = f->get_token();
ERR_FAIL_COND_V_MSG(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED, "Unsupported header information in HDR: " + header + ".");
@@ -131,7 +131,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f
ptr[1] * exp / 255.0,
ptr[2] * exp / 255.0);
- if (p_force_linear) {
+ if (p_flags & FLAG_FORCE_LINEAR) {
c = c.srgb_to_linear();
}
@@ -140,7 +140,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f
}
}
- p_image->create(width, height, false, Image::FORMAT_RGBE9995, imgdata);
+ p_image->set_data(width, height, false, Image::FORMAT_RGBE9995, imgdata);
return OK;
}
diff --git a/modules/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h
index 16c0816562..9821db059e 100644
--- a/modules/hdr/image_loader_hdr.h
+++ b/modules/hdr/image_loader_hdr.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_hdr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_hdr.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_HDR_H
#define IMAGE_LOADER_HDR_H
@@ -35,7 +35,7 @@
class ImageLoaderHDR : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderHDR();
};
diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp
index b988bf4587..5149a948c7 100644
--- a/modules/hdr/register_types.cpp
+++ b/modules/hdr/register_types.cpp
@@ -1,45 +1,45 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_hdr.h"
-static ImageLoaderHDR *image_loader_hdr = nullptr;
+static Ref<ImageLoaderHDR> image_loader_hdr;
void initialize_hdr_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_hdr = memnew(ImageLoaderHDR);
+ image_loader_hdr.instantiate();
ImageLoader::add_image_format_loader(image_loader_hdr);
}
@@ -48,5 +48,6 @@ void uninitialize_hdr_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_hdr);
+ ImageLoader::remove_image_format_loader(image_loader_hdr);
+ image_loader_hdr.unref();
}
diff --git a/modules/hdr/register_types.h b/modules/hdr/register_types.h
index 0254e43b6c..e3a71c88bd 100644
--- a/modules/hdr/register_types.h
+++ b/modules/hdr/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 HDR_REGISTER_TYPES_H
#define HDR_REGISTER_TYPES_H
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 0e03fa65c8..c6a1ca6630 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -1,40 +1,40 @@
-/*************************************************************************/
-/* image_loader_jpegd.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_jpegd.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_jpegd.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "thirdparty/jpeg-compressor/jpgd.h"
-#include "thirdparty/jpeg-compressor/jpge.h"
+#include <jpgd.h>
+#include <jpge.h>
#include <string.h>
Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
@@ -99,12 +99,12 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
fmt = Image::FORMAT_RGB8;
}
- p_image->create(image_width, image_height, false, fmt, data);
+ p_image->set_data(image_width, image_height, false, fmt, data);
return OK;
}
-Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderJPG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, 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);
@@ -132,10 +132,6 @@ 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;
@@ -157,21 +153,18 @@ public:
}
};
-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>());
+static Error _jpgd_save_to_output_stream(jpge::output_stream *p_output_stream, const Ref<Image> &p_img, float p_quality) {
+ ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), ERR_INVALID_PARAMETER);
Ref<Image> image = p_img;
if (image->get_format() != Image::FORMAT_RGB8) {
- image->convert(Image::FORMAT_ETC2_RGB8);
+ image->convert(Image::FORMAT_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);
+ enc.init(p_output_stream, 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++) {
@@ -180,9 +173,28 @@ static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_q
enc.process_scanline(nullptr);
+ return OK;
+}
+
+static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
+ Vector<uint8_t> output;
+ ImageLoaderJPGOSBuffer ob;
+ ob.buffer = &output;
+ if (_jpgd_save_to_output_stream(&ob, p_img, p_quality) != OK) {
+ return Vector<uint8_t>();
+ }
return output;
}
+static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float 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 JPG at path: '%s'.", p_path));
+ ImageLoaderJPGOSFile ob;
+ ob.f = file;
+ return _jpgd_save_to_output_stream(&ob, p_img, p_quality);
+}
+
ImageLoaderJPG::ImageLoaderJPG() {
Image::_jpg_mem_loader_func = _jpegd_mem_loader_func;
Image::save_jpg_func = _jpgd_save_func;
diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h
index 6d631446e7..678bfe2f6f 100644
--- a/modules/jpg/image_loader_jpegd.h
+++ b/modules/jpg/image_loader_jpegd.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_jpegd.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_jpegd.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_JPEGD_H
#define IMAGE_LOADER_JPEGD_H
@@ -35,7 +35,7 @@
class ImageLoaderJPG : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderJPG();
};
diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp
index b8b48a550f..6400aee43b 100644
--- a/modules/jpg/register_types.cpp
+++ b/modules/jpg/register_types.cpp
@@ -1,45 +1,45 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_jpegd.h"
-static ImageLoaderJPG *image_loader_jpg = nullptr;
+static Ref<ImageLoaderJPG> image_loader_jpg;
void initialize_jpg_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_jpg = memnew(ImageLoaderJPG);
+ image_loader_jpg.instantiate();
ImageLoader::add_image_format_loader(image_loader_jpg);
}
@@ -48,5 +48,6 @@ void uninitialize_jpg_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_jpg);
+ ImageLoader::remove_image_format_loader(image_loader_jpg);
+ image_loader_jpg.unref();
}
diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h
index f0a205f6a8..0d8f207e07 100644
--- a/modules/jpg/register_types.h
+++ b/modules/jpg/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 JPG_REGISTER_TYPES_H
#define JPG_REGISTER_TYPES_H
diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp
index 91774fe1f6..a3c0ce88bf 100644
--- a/modules/jsonrpc/jsonrpc.cpp
+++ b/modules/jsonrpc/jsonrpc.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* jsonrpc.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* jsonrpc.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "jsonrpc.h"
diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h
index 1d9f2771b2..70dda29aed 100644
--- a/modules/jsonrpc/jsonrpc.h
+++ b/modules/jsonrpc/jsonrpc.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* jsonrpc.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* jsonrpc.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 JSONRPC_H
#define JSONRPC_H
diff --git a/modules/jsonrpc/register_types.cpp b/modules/jsonrpc/register_types.cpp
index 6d35d6aeb8..2a0457d5a3 100644
--- a/modules/jsonrpc/register_types.cpp
+++ b/modules/jsonrpc/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/object/class_db.h"
diff --git a/modules/jsonrpc/register_types.h b/modules/jsonrpc/register_types.h
index 83d315a9eb..ef2359f706 100644
--- a/modules/jsonrpc/register_types.h
+++ b/modules/jsonrpc/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 JSONRPC_REGISTER_TYPES_H
#define JSONRPC_REGISTER_TYPES_H
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index 83ac478a97..bcb32d77c3 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* lightmapper_rd.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmapper_rd.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "lightmapper_rd.h"
@@ -62,7 +62,7 @@ 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 = Math::tan(Math::deg2rad(p_angular_distance));
+ l.size = Math::tan(Math::deg_to_rad(p_angular_distance));
l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
@@ -96,7 +96,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.direction[2] = p_direction.z;
l.range = p_range;
l.attenuation = p_attenuation;
- l.cos_spot_angle = Math::cos(Math::deg2rad(p_spot_angle));
+ l.cos_spot_angle = Math::cos(Math::deg_to_rad(p_spot_angle));
l.inv_spot_attenuation = 1.0f / p_spot_attenuation;
l.color[0] = p_color.r;
l.color[1] = p_color.g;
@@ -251,15 +251,11 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
}
for (int i = 0; i < atlas_slices; i++) {
- Ref<Image> albedo;
- albedo.instantiate();
- albedo->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8);
+ Ref<Image> albedo = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8);
albedo->set_as_black();
albedo_images.write[i] = albedo;
- Ref<Image> emission;
- emission.instantiate();
- emission->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH);
+ Ref<Image> emission = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH);
emission->set_as_black();
emission_images.write[i] = emission;
}
@@ -278,7 +274,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
return BAKE_OK;
}
-void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
+void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &p_probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
HashMap<Vertex, uint32_t, VertexHash> vertex_map;
//fill triangles array and vertex array
@@ -403,8 +399,8 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
}
//also consider probe positions for bounds
- for (int i = 0; i < probe_positions.size(); i++) {
- Vector3 pp(probe_positions[i].position[0], probe_positions[i].position[1], probe_positions[i].position[2]);
+ for (int i = 0; i < p_probe_positions.size(); i++) {
+ Vector3 pp(p_probe_positions[i].position[0], p_probe_positions[i].position[1], p_probe_positions[i].position[2]);
bounds.expand_to(pp);
}
bounds.grow_by(0.1); //grow a bit to avoid numerical error
@@ -478,9 +474,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
grid_usage.write[j] = count > 0 ? 255 : 0;
}
- Ref<Image> img;
- img.instantiate();
- img->create(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage);
+ Ref<Image> img = Image::create_from_data(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage);
img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png");
}
#endif
@@ -520,7 +514,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
}
seams_buffer = rd->storage_buffer_create(sb.size(), sb);
- Vector<uint8_t> pb = probe_positions.to_byte_array();
+ Vector<uint8_t> pb = p_probe_positions.to_byte_array();
if (pb.size() == 0) {
pb.resize(sizeof(Probe));
}
@@ -660,9 +654,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
#ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->convert(Image::FORMAT_RGBA8);
img->save_png("res://5_dilated_" + itos(i) + ".png");
}
@@ -670,7 +662,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
return BAKE_OK;
}
-LightmapperRD::BakeError LightmapperRD::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, void *p_bake_userdata) {
+LightmapperRD::BakeError LightmapperRD::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, void *p_bake_userdata, float p_exposure_normalization) {
if (p_step_function) {
p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true);
}
@@ -778,7 +770,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
panorama_tex->convert(Image::FORMAT_RGBAF);
} else {
panorama_tex.instantiate();
- panorama_tex->create(8, 8, false, Image::FORMAT_RGBAF);
+ panorama_tex->initialize_data(8, 8, false, Image::FORMAT_RGBAF);
panorama_tex->fill(Color(0, 0, 0, 1));
}
@@ -953,13 +945,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(position_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s);
img->save_exr("res://1_position_" + itos(i) + ".exr", false);
s = rd->texture_get_data(normal_tex, i);
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ img->set_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://1_normal_" + itos(i) + ".exr", false);
}
#endif
@@ -1165,6 +1155,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1);
+ push_constant.environment_xform[11] = p_exposure_normalization;
+
for (int i = 0; i < atlas_slices; i++) {
push_constant.atlas_slice = i;
rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
@@ -1172,15 +1164,15 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
//no barrier, let them run all together
}
rd->compute_list_end(); //done
+
+ push_constant.environment_xform[11] = 0.0;
}
#ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(light_source_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false);
}
#endif
@@ -1411,14 +1403,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
#if 0
for (int i = 0; i < probe_positions.size(); i++) {
- Ref<Image> img;
- img.instantiate();
- img->create(6, 4, false, Image::FORMAT_RGB8);
+ Ref<Image> img = Image::create_empty(6, 4, false, Image::FORMAT_RGB8);
for (int j = 0; j < 6; j++) {
Vector<uint8_t> s = rd->texture_get_data(lightprobe_tex, i * 6 + j);
- Ref<Image> img2;
- img2.instantiate();
- img2->create(2, 2, false, Image::FORMAT_RGBAF, s);
+ Ref<Image> img2 = Image::create_from_data(2, 2, false, Image::FORMAT_RGBAF, s);
img2->convert(Image::FORMAT_RGB8);
img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2));
}
@@ -1445,9 +1433,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
if (denoiser.is_valid()) {
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
Ref<Image> denoised = denoiser->denoise_image(img);
if (denoised != img) {
@@ -1480,9 +1466,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false);
}
#endif
@@ -1636,9 +1620,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
}
#endif
@@ -1648,9 +1630,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->convert(Image::FORMAT_RGBH); //remove alpha
bake_textures.push_back(img);
}
@@ -1663,9 +1643,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
#ifdef DEBUG_TEXTURES
{
- Ref<Image> img2;
- img2.instantiate();
- img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data);
+ Ref<Image> img2 = Image::create_from_data(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data);
img2->save_exr("res://6_lightprobes.exr", false);
}
#endif
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 88860ad0d4..061c9ba000 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* lightmapper_rd.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmapper_rd.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 LIGHTMAPPER_RD_H
#define LIGHTMAPPER_RD_H
@@ -183,7 +183,7 @@ class LightmapperRD : public Lightmapper {
}
};
- void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size);
+ void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size);
struct RasterPushConstant {
float atlas_size[2] = {};
@@ -241,7 +241,7 @@ public:
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;
+ 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, float p_exposure_normalization = 1.0) override;
int get_bake_texture_count() const override;
Ref<Image> get_bake_texture(int p_index) const override;
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index efa6cd50b4..c2557dfed3 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -434,6 +434,7 @@ void main() {
imageStore(primary_dynamic, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
dynamic_light += static_light * albedo; //send for bounces
+ dynamic_light *= params.env_transform[2][3]; // exposure_normalization
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0));
#ifdef USE_SH_LIGHTMAPS
@@ -444,6 +445,7 @@ void main() {
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]);
#else
+ static_light *= params.env_transform[2][3]; // exposure_normalization
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(static_light, 1.0));
#endif
diff --git a/modules/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp
index 0e0330c1a1..af89aa167c 100644
--- a/modules/lightmapper_rd/register_types.cpp
+++ b/modules/lightmapper_rd/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -57,6 +57,7 @@ void initialize_lightmapper_rd_module(ModuleInitializationLevel p_level) {
GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_probe_ray_count", 512);
GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count", 2048);
GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_probe_pass", 64);
+ GLOBAL_DEF("rendering/lightmapping/primitive_meshes/texel_size", 0.2);
#ifndef _3D_DISABLED
GDREGISTER_CLASS(LightmapperRD);
Lightmapper::create_gpu = create_lightmapper_rd;
diff --git a/modules/lightmapper_rd/register_types.h b/modules/lightmapper_rd/register_types.h
index 9b72ff45d7..01b7f5dd4f 100644
--- a/modules/lightmapper_rd/register_types.h
+++ b/modules/lightmapper_rd/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 LIGHTMAPPER_RD_REGISTER_TYPES_H
#define LIGHTMAPPER_RD_REGISTER_TYPES_H
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index e62581ab40..5b52af3068 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* crypto_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* crypto_mbedtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "crypto_mbedtls.h"
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 5ba7e9cbf6..7422ebad3e 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* crypto_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* crypto_mbedtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CRYPTO_MBEDTLS_H
#define CRYPTO_MBEDTLS_H
@@ -39,7 +39,7 @@
#include <mbedtls/ssl.h>
class CryptoMbedTLS;
-class SSLContextMbedTLS;
+class TLSContextMbedTLS;
class CryptoKeyMbedTLS : public CryptoKey {
private:
mbedtls_pk_context pkey;
@@ -69,7 +69,7 @@ public:
_FORCE_INLINE_ void unlock() { locks--; }
friend class CryptoMbedTLS;
- friend class SSLContextMbedTLS;
+ friend class TLSContextMbedTLS;
};
class X509CertificateMbedTLS : public X509Certificate {
@@ -98,7 +98,7 @@ public:
_FORCE_INLINE_ void unlock() { locks--; }
friend class CryptoMbedTLS;
- friend class SSLContextMbedTLS;
+ friend class TLSContextMbedTLS;
};
class HMACContextMbedTLS : public HMACContext {
diff --git a/modules/mbedtls/dtls_server_mbedtls.cpp b/modules/mbedtls/dtls_server_mbedtls.cpp
index dedf0651a0..c54ab8ef6e 100644
--- a/modules/mbedtls/dtls_server_mbedtls.cpp
+++ b/modules/mbedtls/dtls_server_mbedtls.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* dtls_server_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* dtls_server_mbedtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "dtls_server_mbedtls.h"
#include "packet_peer_mbed_dtls.h"
diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h
index a6626c9f65..e4612d01ef 100644
--- a/modules/mbedtls/dtls_server_mbedtls.h
+++ b/modules/mbedtls/dtls_server_mbedtls.h
@@ -1,38 +1,38 @@
-/*************************************************************************/
-/* dtls_server_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* dtls_server_mbedtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 DTLS_SERVER_MBEDTLS_H
#define DTLS_SERVER_MBEDTLS_H
#include "core/io/dtls_server.h"
-#include "ssl_context_mbedtls.h"
+#include "tls_context_mbedtls.h"
class DTLSServerMbedTLS : public DTLSServer {
private:
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp
index 1296a4587c..16450e151e 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.cpp
+++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp
@@ -1,38 +1,38 @@
-/*************************************************************************/
-/* packet_peer_mbed_dtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* packet_peer_mbed_dtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "packet_peer_mbed_dtls.h"
#include "mbedtls/platform_util.h"
#include "core/io/file_access.h"
-#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tls.h"
int PacketPeerMbedDTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
if (buf == nullptr || len == 0) {
@@ -79,7 +79,7 @@ int PacketPeerMbedDTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
}
void PacketPeerMbedDTLS::_cleanup() {
- ssl_ctx->clear();
+ tls_ctx->clear();
base = Ref<PacketPeer>();
status = STATUS_DISCONNECTED;
}
@@ -91,16 +91,16 @@ int PacketPeerMbedDTLS::_set_cookie() {
uint16_t port = base->get_packet_port();
memcpy(client_id, addr.get_ipv6(), 16);
memcpy(&client_id[16], (uint8_t *)&port, 2);
- return mbedtls_ssl_set_client_transport_id(ssl_ctx->get_context(), client_id, 18);
+ return mbedtls_ssl_set_client_transport_id(tls_ctx->get_context(), client_id, 18);
}
Error PacketPeerMbedDTLS::_do_handshake() {
int ret = 0;
- while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) {
+ while ((ret = mbedtls_ssl_handshake(tls_ctx->get_context())) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (ret != MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) {
ERR_PRINT("TLS handshake error: " + itos(ret));
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
}
_cleanup();
status = STATUS_ERROR;
@@ -118,19 +118,18 @@ Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_vali
ERR_FAIL_COND_V(!p_base.is_valid() || !p_base->is_socket_connected(), ERR_INVALID_PARAMETER);
base = p_base;
- int ret = 0;
int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE;
- Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, authmode, p_ca_certs);
+ Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_DATAGRAM, authmode, p_ca_certs);
ERR_FAIL_COND_V(err != OK, err);
- mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data());
- mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr);
- mbedtls_ssl_set_timer_cb(ssl_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
+ mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data());
+ mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
+ mbedtls_ssl_set_timer_cb(tls_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
status = STATUS_HANDSHAKING;
- if ((ret = _do_handshake()) != OK) {
+ if (_do_handshake() != OK) {
status = STATUS_ERROR_HOSTNAME_MISMATCH;
return FAILED;
}
@@ -139,13 +138,13 @@ Error PacketPeerMbedDTLS::connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_vali
}
Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain, Ref<CookieContextMbedTLS> p_cookies) {
- Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert, p_cookies);
+ Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert, p_cookies);
ERR_FAIL_COND_V(err != OK, err);
base = p_base;
base->set_blocking_mode(false);
- mbedtls_ssl_session_reset(ssl_ctx->get_context());
+ mbedtls_ssl_session_reset(tls_ctx->get_context());
int ret = _set_cookie();
if (ret != 0) {
@@ -153,12 +152,12 @@ Error PacketPeerMbedDTLS::accept_peer(Ref<PacketPeerUDP> p_base, Ref<CryptoKey>
ERR_FAIL_V_MSG(FAILED, "Error setting DTLS client cookie");
}
- mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr);
- mbedtls_ssl_set_timer_cb(ssl_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
+ mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
+ mbedtls_ssl_set_timer_cb(tls_ctx->get_context(), &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
status = STATUS_HANDSHAKING;
- if ((ret = _do_handshake()) != OK) {
+ if (_do_handshake() != OK) {
status = STATUS_ERROR;
return FAILED;
}
@@ -173,11 +172,11 @@ Error PacketPeerMbedDTLS::put_packet(const uint8_t *p_buffer, int p_bytes) {
return OK;
}
- int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_buffer, p_bytes);
+ int ret = mbedtls_ssl_write(tls_ctx->get_context(), p_buffer, p_bytes);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
- ret = 0; // non blocking io
+ // Non blocking io.
} else if (ret <= 0) {
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
_cleanup();
return ERR_CONNECTION_ERROR;
}
@@ -190,7 +189,7 @@ Error PacketPeerMbedDTLS::get_packet(const uint8_t **r_buffer, int &r_bytes) {
r_bytes = 0;
- int ret = mbedtls_ssl_read(ssl_ctx->get_context(), packet_buffer, PACKET_BUFFER_SIZE);
+ int ret = mbedtls_ssl_read(tls_ctx->get_context(), packet_buffer, PACKET_BUFFER_SIZE);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
ret = 0; // non blocking io
} else if (ret <= 0) {
@@ -200,7 +199,7 @@ Error PacketPeerMbedDTLS::get_packet(const uint8_t **r_buffer, int &r_bytes) {
} else {
_cleanup();
status = STATUS_ERROR;
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
}
return ERR_CONNECTION_ERROR;
}
@@ -220,7 +219,7 @@ void PacketPeerMbedDTLS::poll() {
ERR_FAIL_COND(!base.is_valid());
- int ret = mbedtls_ssl_read(ssl_ctx->get_context(), nullptr, 0);
+ int ret = mbedtls_ssl_read(tls_ctx->get_context(), nullptr, 0);
if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
@@ -229,7 +228,7 @@ void PacketPeerMbedDTLS::poll() {
} else {
_cleanup();
status = STATUS_ERROR;
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
}
}
}
@@ -237,7 +236,7 @@ void PacketPeerMbedDTLS::poll() {
int PacketPeerMbedDTLS::get_available_packet_count() const {
ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
- return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl)) > 0 ? 1 : 0;
+ return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls)) > 0 ? 1 : 0;
}
int PacketPeerMbedDTLS::get_max_packet_size() const {
@@ -245,7 +244,7 @@ int PacketPeerMbedDTLS::get_max_packet_size() const {
}
PacketPeerMbedDTLS::PacketPeerMbedDTLS() {
- ssl_ctx.instantiate();
+ tls_ctx.instantiate();
}
PacketPeerMbedDTLS::~PacketPeerMbedDTLS() {
@@ -261,7 +260,7 @@ void PacketPeerMbedDTLS::disconnect_from_peer() {
int ret = 0;
// Send SSL close notification, blocking, but ignore other errors.
do {
- ret = mbedtls_ssl_close_notify(ssl_ctx->get_context());
+ ret = mbedtls_ssl_close_notify(tls_ctx->get_context());
} while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
}
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h
index 5f2f42cd30..744ef81524 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.h
+++ b/modules/mbedtls/packet_peer_mbed_dtls.h
@@ -1,38 +1,38 @@
-/*************************************************************************/
-/* packet_peer_mbed_dtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* packet_peer_mbed_dtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 PACKET_PEER_MBED_DTLS_H
#define PACKET_PEER_MBED_DTLS_H
#include "core/io/packet_peer_dtls.h"
-#include "ssl_context_mbedtls.h"
+#include "tls_context_mbedtls.h"
#include <mbedtls/timing.h>
@@ -56,7 +56,7 @@ private:
void _cleanup();
protected:
- Ref<SSLContextMbedTLS> ssl_ctx;
+ Ref<TLSContextMbedTLS> tls_ctx;
mbedtls_timing_delay_context timer;
Error _do_handshake();
diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp
index 2d4a18b3fc..df5bce05e4 100644
--- a/modules/mbedtls/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -45,7 +45,7 @@ void initialize_mbedtls_module(ModuleInitializationLevel p_level) {
}
CryptoMbedTLS::initialize_crypto();
- StreamPeerMbedTLS::initialize_ssl();
+ StreamPeerMbedTLS::initialize_tls();
PacketPeerMbedDTLS::initialize_dtls();
DTLSServerMbedTLS::initialize();
}
@@ -57,6 +57,6 @@ void uninitialize_mbedtls_module(ModuleInitializationLevel p_level) {
DTLSServerMbedTLS::finalize();
PacketPeerMbedDTLS::finalize_dtls();
- StreamPeerMbedTLS::finalize_ssl();
+ StreamPeerMbedTLS::finalize_tls();
CryptoMbedTLS::finalize_crypto();
}
diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h
index ebe76f44f1..407abd7abd 100644
--- a/modules/mbedtls/register_types.h
+++ b/modules/mbedtls/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MBEDTLS_REGISTER_TYPES_H
#define MBEDTLS_REGISTER_TYPES_H
diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp
index 92590fbcf6..1d17fb9441 100644
--- a/modules/mbedtls/stream_peer_mbedtls.cpp
+++ b/modules/mbedtls/stream_peer_mbedtls.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* stream_peer_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* stream_peer_mbedtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "stream_peer_mbedtls.h"
@@ -74,18 +74,18 @@ int StreamPeerMbedTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
}
void StreamPeerMbedTLS::_cleanup() {
- ssl_ctx->clear();
+ tls_ctx->clear();
base = Ref<StreamPeer>();
status = STATUS_DISCONNECTED;
}
Error StreamPeerMbedTLS::_do_handshake() {
int ret = 0;
- while ((ret = mbedtls_ssl_handshake(ssl_ctx->get_context())) != 0) {
+ while ((ret = mbedtls_ssl_handshake(tls_ctx->get_context())) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
// An error occurred.
ERR_PRINT("TLS handshake error: " + itos(ret));
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
disconnect_from_stream();
status = STATUS_ERROR;
return FAILED;
@@ -108,11 +108,11 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
base = p_base;
int authmode = p_validate_certs ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE;
- Error err = ssl_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs);
+ Error err = tls_ctx->init_client(MBEDTLS_SSL_TRANSPORT_STREAM, authmode, p_ca_certs);
ERR_FAIL_COND_V(err != OK, err);
- mbedtls_ssl_set_hostname(ssl_ctx->get_context(), p_for_hostname.utf8().get_data());
- mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr);
+ mbedtls_ssl_set_hostname(tls_ctx->get_context(), p_for_hostname.utf8().get_data());
+ mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
status = STATUS_HANDSHAKING;
@@ -127,12 +127,12 @@ Error StreamPeerMbedTLS::connect_to_stream(Ref<StreamPeer> p_base, bool p_valida
Error StreamPeerMbedTLS::accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain) {
ERR_FAIL_COND_V(p_base.is_null(), ERR_INVALID_PARAMETER);
- Error err = ssl_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert);
+ Error err = tls_ctx->init_server(MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_VERIFY_NONE, p_key, p_cert);
ERR_FAIL_COND_V(err != OK, err);
base = p_base;
- mbedtls_ssl_set_bio(ssl_ctx->get_context(), this, bio_send, bio_recv, nullptr);
+ mbedtls_ssl_set_bio(tls_ctx->get_context(), this, bio_send, bio_recv, nullptr);
status = STATUS_HANDSHAKING;
@@ -173,7 +173,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in
return OK;
}
- int ret = mbedtls_ssl_write(ssl_ctx->get_context(), p_data, p_bytes);
+ int ret = mbedtls_ssl_write(tls_ctx->get_context(), p_data, p_bytes);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
// Non blocking IO
ret = 0;
@@ -182,7 +182,7 @@ Error StreamPeerMbedTLS::put_partial_data(const uint8_t *p_data, int p_bytes, in
disconnect_from_stream();
return ERR_FILE_EOF;
} else if (ret <= 0) {
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
disconnect_from_stream();
return ERR_CONNECTION_ERROR;
}
@@ -216,7 +216,7 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r
r_received = 0;
- int ret = mbedtls_ssl_read(ssl_ctx->get_context(), p_buffer, p_bytes);
+ int ret = mbedtls_ssl_read(tls_ctx->get_context(), p_buffer, p_bytes);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
ret = 0; // non blocking io
} else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
@@ -224,7 +224,7 @@ Error StreamPeerMbedTLS::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r
disconnect_from_stream();
return ERR_FILE_EOF;
} else if (ret <= 0) {
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
disconnect_from_stream();
return ERR_CONNECTION_ERROR;
}
@@ -242,10 +242,10 @@ void StreamPeerMbedTLS::poll() {
return;
}
- // We could pass nullptr as second parameter, but some behaviour sanitizers don't seem to like that.
+ // We could pass nullptr as second parameter, but some behavior sanitizers don't seem to like that.
// Passing a 1 byte buffer to workaround it.
uint8_t byte;
- int ret = mbedtls_ssl_read(ssl_ctx->get_context(), &byte, 0);
+ int ret = mbedtls_ssl_read(tls_ctx->get_context(), &byte, 0);
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
// Nothing to read/write (non blocking IO)
@@ -254,7 +254,7 @@ void StreamPeerMbedTLS::poll() {
disconnect_from_stream();
return;
} else if (ret < 0) {
- SSLContextMbedTLS::print_mbedtls_error(ret);
+ TLSContextMbedTLS::print_mbedtls_error(ret);
disconnect_from_stream();
return;
}
@@ -269,11 +269,11 @@ void StreamPeerMbedTLS::poll() {
int StreamPeerMbedTLS::get_available_bytes() const {
ERR_FAIL_COND_V(status != STATUS_CONNECTED, 0);
- return mbedtls_ssl_get_bytes_avail(&(ssl_ctx->ssl));
+ return mbedtls_ssl_get_bytes_avail(&(tls_ctx->tls));
}
StreamPeerMbedTLS::StreamPeerMbedTLS() {
- ssl_ctx.instantiate();
+ tls_ctx.instantiate();
}
StreamPeerMbedTLS::~StreamPeerMbedTLS() {
@@ -288,7 +288,7 @@ void StreamPeerMbedTLS::disconnect_from_stream() {
Ref<StreamPeerTCP> tcp = base;
if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
// We are still connected on the socket, try to send close notify.
- mbedtls_ssl_close_notify(ssl_ctx->get_context());
+ mbedtls_ssl_close_notify(tls_ctx->get_context());
}
_cleanup();
@@ -302,16 +302,16 @@ Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {
return base;
}
-StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
+StreamPeerTLS *StreamPeerMbedTLS::_create_func() {
return memnew(StreamPeerMbedTLS);
}
-void StreamPeerMbedTLS::initialize_ssl() {
+void StreamPeerMbedTLS::initialize_tls() {
_create = _create_func;
available = true;
}
-void StreamPeerMbedTLS::finalize_ssl() {
+void StreamPeerMbedTLS::finalize_tls() {
available = false;
_create = nullptr;
}
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index 68b07feea9..8a36a7ea9a 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -1,54 +1,54 @@
-/*************************************************************************/
-/* stream_peer_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* stream_peer_mbedtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 STREAM_PEER_MBEDTLS_H
#define STREAM_PEER_MBEDTLS_H
-#include "core/io/stream_peer_ssl.h"
-#include "ssl_context_mbedtls.h"
+#include "core/io/stream_peer_tls.h"
+#include "tls_context_mbedtls.h"
-class StreamPeerMbedTLS : public StreamPeerSSL {
+class StreamPeerMbedTLS : public StreamPeerTLS {
private:
Status status = STATUS_DISCONNECTED;
String hostname;
Ref<StreamPeer> base;
- static StreamPeerSSL *_create_func();
+ static StreamPeerTLS *_create_func();
static int bio_recv(void *ctx, unsigned char *buf, size_t len);
static int bio_send(void *ctx, const unsigned char *buf, size_t len);
void _cleanup();
protected:
- Ref<SSLContextMbedTLS> ssl_ctx;
+ Ref<TLSContextMbedTLS> tls_ctx;
Error _do_handshake();
@@ -69,8 +69,8 @@ public:
virtual int get_available_bytes() const;
- static void initialize_ssl();
- static void finalize_ssl();
+ static void initialize_tls();
+ static void finalize_tls();
StreamPeerMbedTLS();
~StreamPeerMbedTLS();
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.cpp b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
index f88012c6cd..598e1d972e 100644
--- a/modules/mbedtls/tests/test_crypto_mbedtls.cpp
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* test_crypto_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* test_crypto_mbedtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "test_crypto_mbedtls.h"
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.h b/modules/mbedtls/tests/test_crypto_mbedtls.h
index 6f920e5b34..0b24925d6b 100644
--- a/modules/mbedtls/tests/test_crypto_mbedtls.h
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* test_crypto_mbedtls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* test_crypto_mbedtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef TEST_CRYPTO_MBEDTLS_H
#define TEST_CRYPTO_MBEDTLS_H
diff --git a/modules/mbedtls/ssl_context_mbedtls.cpp b/modules/mbedtls/tls_context_mbedtls.cpp
index e2dad074cc..a01137f262 100644
--- a/modules/mbedtls/ssl_context_mbedtls.cpp
+++ b/modules/mbedtls/tls_context_mbedtls.cpp
@@ -1,34 +1,34 @@
-/*************************************************************************/
-/* ssl_context_mbedtls.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 "ssl_context_mbedtls.h"
+/**************************************************************************/
+/* tls_context_mbedtls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "tls_context_mbedtls.h"
static void my_debug(void *ctx, int level,
const char *file, int line,
@@ -37,7 +37,7 @@ static void my_debug(void *ctx, int level,
fflush(stdout);
}
-void SSLContextMbedTLS::print_mbedtls_error(int p_ret) {
+void TLSContextMbedTLS::print_mbedtls_error(int p_ret) {
printf("mbedtls error: returned -0x%x\n\n", -p_ret);
fflush(stdout);
}
@@ -82,12 +82,12 @@ CookieContextMbedTLS::~CookieContextMbedTLS() {
clear();
}
-/// SSLContextMbedTLS
+/// TLSContextMbedTLS
-Error SSLContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) {
+Error TLSContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode) {
ERR_FAIL_COND_V_MSG(inited, ERR_ALREADY_IN_USE, "This SSL context is already active");
- mbedtls_ssl_init(&ssl);
+ mbedtls_ssl_init(&tls);
mbedtls_ssl_config_init(&conf);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
@@ -110,7 +110,7 @@ Error SSLContextMbedTLS::_setup(int p_endpoint, int p_transport, int p_authmode)
return OK;
}
-Error SSLContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies) {
+Error TLSContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<CryptoKeyMbedTLS> p_pkey, Ref<X509CertificateMbedTLS> p_cert, Ref<CookieContextMbedTLS> p_cookies) {
ERR_FAIL_COND_V(!p_pkey.is_valid(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!p_cert.is_valid(), ERR_INVALID_PARAMETER);
@@ -146,11 +146,11 @@ Error SSLContextMbedTLS::init_server(int p_transport, int p_authmode, Ref<Crypto
cookies = p_cookies;
mbedtls_ssl_conf_dtls_cookies(&conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &(cookies->cookie_ctx));
}
- mbedtls_ssl_setup(&ssl, &conf);
+ mbedtls_ssl_setup(&tls, &conf);
return OK;
}
-Error SSLContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) {
+Error TLSContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509CertificateMbedTLS> p_valid_cas) {
Error err = _setup(MBEDTLS_SSL_IS_CLIENT, p_transport, p_authmode);
ERR_FAIL_COND_V(err != OK, err);
@@ -172,15 +172,15 @@ Error SSLContextMbedTLS::init_client(int p_transport, int p_authmode, Ref<X509Ce
// Set valid CAs
mbedtls_ssl_conf_ca_chain(&conf, &(cas->cert), nullptr);
- mbedtls_ssl_setup(&ssl, &conf);
+ mbedtls_ssl_setup(&tls, &conf);
return OK;
}
-void SSLContextMbedTLS::clear() {
+void TLSContextMbedTLS::clear() {
if (!inited) {
return;
}
- mbedtls_ssl_free(&ssl);
+ mbedtls_ssl_free(&tls);
mbedtls_ssl_config_free(&conf);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
@@ -198,14 +198,14 @@ void SSLContextMbedTLS::clear() {
inited = false;
}
-mbedtls_ssl_context *SSLContextMbedTLS::get_context() {
+mbedtls_ssl_context *TLSContextMbedTLS::get_context() {
ERR_FAIL_COND_V(!inited, nullptr);
- return &ssl;
+ return &tls;
}
-SSLContextMbedTLS::SSLContextMbedTLS() {
+TLSContextMbedTLS::TLSContextMbedTLS() {
}
-SSLContextMbedTLS::~SSLContextMbedTLS() {
+TLSContextMbedTLS::~TLSContextMbedTLS() {
clear();
}
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/tls_context_mbedtls.h
index 5883388311..574e80e199 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/tls_context_mbedtls.h
@@ -1,35 +1,35 @@
-/*************************************************************************/
-/* ssl_context_mbedtls.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 SSL_CONTEXT_MBEDTLS_H
-#define SSL_CONTEXT_MBEDTLS_H
+/**************************************************************************/
+/* tls_context_mbedtls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TLS_CONTEXT_MBEDTLS_H
+#define TLS_CONTEXT_MBEDTLS_H
#include "crypto_mbedtls.h"
@@ -44,10 +44,10 @@
#include <mbedtls/ssl.h>
#include <mbedtls/ssl_cookie.h>
-class SSLContextMbedTLS;
+class TLSContextMbedTLS;
class CookieContextMbedTLS : public RefCounted {
- friend class SSLContextMbedTLS;
+ friend class TLSContextMbedTLS;
protected:
bool inited = false;
@@ -63,7 +63,7 @@ public:
~CookieContextMbedTLS();
};
-class SSLContextMbedTLS : public RefCounted {
+class TLSContextMbedTLS : public RefCounted {
protected:
bool inited = false;
@@ -73,7 +73,7 @@ public:
Ref<X509CertificateMbedTLS> certs;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
- mbedtls_ssl_context ssl;
+ mbedtls_ssl_context tls;
mbedtls_ssl_config conf;
Ref<CookieContextMbedTLS> cookies;
@@ -86,8 +86,8 @@ public:
mbedtls_ssl_context *get_context();
- SSLContextMbedTLS();
- ~SSLContextMbedTLS();
+ TLSContextMbedTLS();
+ ~TLSContextMbedTLS();
};
-#endif // SSL_CONTEXT_MBEDTLS_H
+#endif // TLS_CONTEXT_MBEDTLS_H
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 3e212360c0..d807fd9517 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "scene/resources/surface_tool.h"
diff --git a/modules/meshoptimizer/register_types.h b/modules/meshoptimizer/register_types.h
index 3a84aab7bc..02c52c48a2 100644
--- a/modules/meshoptimizer/register_types.h
+++ b/modules/meshoptimizer/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MESHOPTIMIZER_REGISTER_TYPES_H
#define MESHOPTIMIZER_REGISTER_TYPES_H
diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub
index f4d1605d55..20e3165f38 100644
--- a/modules/minimp3/SCsub
+++ b/modules/minimp3/SCsub
@@ -13,5 +13,5 @@ if not env.msvc:
else:
env_minimp3.Prepend(CPPPATH=[thirdparty_dir])
-# Godot's own source files
+# Godot source files
env_minimp3.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 98bcdea8f4..6af86a96dc 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* audio_stream_mp3.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* audio_stream_mp3.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#define MINIMP3_ONLY_MP3
#define MINIMP3_FLOAT_OUTPUT
@@ -104,7 +104,7 @@ float AudioStreamPlaybackMP3::get_stream_sampling_rate() {
return mp3_stream->sample_rate;
}
-void AudioStreamPlaybackMP3::start(float p_from_pos) {
+void AudioStreamPlaybackMP3::start(double p_from_pos) {
active = true;
seek(p_from_pos);
loops = 0;
@@ -123,11 +123,11 @@ int AudioStreamPlaybackMP3::get_loop_count() const {
return loops;
}
-float AudioStreamPlaybackMP3::get_playback_position() const {
- return float(frames_mixed) / mp3_stream->sample_rate;
+double AudioStreamPlaybackMP3::get_playback_position() const {
+ return double(frames_mixed) / mp3_stream->sample_rate;
}
-void AudioStreamPlaybackMP3::seek(float p_time) {
+void AudioStreamPlaybackMP3::seek(double p_time) {
if (!active) {
return;
}
@@ -217,15 +217,15 @@ bool AudioStreamMP3::has_loop() const {
return loop;
}
-void AudioStreamMP3::set_loop_offset(float p_seconds) {
+void AudioStreamMP3::set_loop_offset(double p_seconds) {
loop_offset = p_seconds;
}
-float AudioStreamMP3::get_loop_offset() const {
+double AudioStreamMP3::get_loop_offset() const {
return loop_offset;
}
-float AudioStreamMP3::get_length() const {
+double AudioStreamMP3::get_length() const {
return length;
}
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 428ac1240e..c14db28460 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* audio_stream_mp3.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* audio_stream_mp3.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 AUDIO_STREAM_MP3_H
#define AUDIO_STREAM_MP3_H
@@ -61,14 +61,14 @@ protected:
virtual float get_stream_sampling_rate() override;
public:
- virtual void start(float p_from_pos = 0.0) override;
+ virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
- virtual float get_playback_position() const override;
- virtual void seek(float p_time) override;
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
virtual void tag_used_streams() override;
@@ -104,8 +104,8 @@ public:
void set_loop(bool p_enable);
virtual bool has_loop() const override;
- void set_loop_offset(float p_seconds);
- float get_loop_offset() const;
+ void set_loop_offset(double p_seconds);
+ double get_loop_offset() const;
void set_bpm(double p_bpm);
virtual double get_bpm() const override;
@@ -122,7 +122,7 @@ public:
void set_data(const Vector<uint8_t> &p_data);
Vector<uint8_t> get_data() const;
- virtual float get_length() const override;
+ virtual double get_length() const override;
virtual bool is_monophonic() const override;
diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp
index 9d1b56abdf..c9e05c44e1 100644
--- a/modules/minimp3/register_types.cpp
+++ b/modules/minimp3/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/minimp3/register_types.h b/modules/minimp3/register_types.h
index 9e5eb85abe..24f29b682d 100644
--- a/modules/minimp3/register_types.h
+++ b/modules/minimp3/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MINIMP3_REGISTER_TYPES_H
#define MINIMP3_REGISTER_TYPES_H
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index 7492533094..6a04e40c73 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* resource_importer_mp3.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_importer_mp3.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_importer_mp3.h"
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 38729d68f8..b887963050 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* resource_importer_mp3.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_importer_mp3.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_IMPORTER_MP3_H
#define RESOURCE_IMPORTER_MP3_H
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index db186079b0..63592042c7 100644
--- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml
+++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
@@ -6,7 +6,7 @@
<description>
This is a generic mobile VR implementation where you need to provide details about the phone and HMD used. It does not rely on any existing framework. This is the most basic interface we have. For the best effect, you need a mobile phone with a gyroscope and accelerometer.
Note that even though there is no positional tracking, the camera will assume the headset is at a height of 1.85 meters. You can change this by setting [member eye_height].
- You can initialise this interface as follows:
+ You can initialize this interface as follows:
[codeblock]
var interface = XRServer.find_interface("Native mobile")
if interface and interface.initialize():
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index b14f5f469c..5fab53441c 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* mobile_vr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* mobile_vr_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "mobile_vr_interface.h"
@@ -155,7 +155,7 @@ void MobileVRInterface::set_position_from_sensors() {
last_magnetometer_data = magneto;
if (grav.length() < 0.1) {
- // not ideal but use our accelerometer, this will contain shaky user behaviour
+ // not ideal but use our accelerometer, this will contain shaky user behavior
// maybe look into some math but I'm guessing that if this isn't available, it's because we lack the gyro sensor to actually work out
// what a stable gravity vector is
grav = acc;
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index b934a09dd3..12075eda82 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* mobile_vr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* mobile_vr_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MOBILE_VR_INTERFACE_H
#define MOBILE_VR_INTERFACE_H
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 4df8af9009..4ae0c65a22 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -53,7 +53,7 @@ void uninitialize_mobile_vr_module(ModuleInitializationLevel p_level) {
}
if (mobile_vr.is_valid()) {
- // uninitialise our interface if it is initialised
+ // uninitialize our interface if it is initialized
if (mobile_vr->is_initialized()) {
mobile_vr->uninitialize();
}
diff --git a/modules/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h
index 26812af512..499de36d24 100644
--- a/modules/mobile_vr/register_types.h
+++ b/modules/mobile_vr/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MOBILE_VR_REGISTER_TYPES_H
#define MOBILE_VR_REGISTER_TYPES_H
diff --git a/modules/mono/.editorconfig b/modules/mono/.editorconfig
index c9dcd7724e..9434d0693c 100644
--- a/modules/mono/.editorconfig
+++ b/modules/mono/.editorconfig
@@ -12,3 +12,32 @@ insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
csharp_indent_case_contents_when_block = false
+
+[*.cs]
+# CA1707: Identifiers should not contain underscores
+# TODO:
+# Maybe we could disable this selectively only
+# where it's not desired and for generated code.
+dotnet_diagnostic.CA1707.severity = none
+# CA1711: Identifiers should not have incorrect suffix
+# Disable warning for suffixes like EventHandler, Flags, Enum, etc.
+dotnet_diagnostic.CA1711.severity = none
+# CA1716: Identifiers should not match keywords
+# TODO: We should look into this.
+dotnet_diagnostic.CA1716.severity = warning
+# CA1720: Identifiers should not contain type names
+dotnet_diagnostic.CA1720.severity = none
+# CA1805: Do not initialize unnecessarily
+# Don't tell me what to do.
+dotnet_diagnostic.CA1805.severity = none
+# CA1304: Specify CultureInfo
+# TODO: We should look into this.
+dotnet_diagnostic.CA1304.severity = warning
+# CA1305: Specify IFormatProvider
+# TODO: We should look into this. Disabled for now because it's annoying.
+dotnet_diagnostic.CA1305.severity = none
+# CA1310: Specify StringComparison for correctness
+# TODO: We should look into this. Disabled for now because it's annoying.
+dotnet_diagnostic.CA1310.severity = none
+# Diagnostics to prevent defensive copies of `in` struct parameters
+resharper_possibly_impure_method_call_on_readonly_variable_highlighting = error
diff --git a/modules/mono/.gitignore b/modules/mono/.gitignore
index fa6d00cbbb..2d62f9f88a 100644
--- a/modules/mono/.gitignore
+++ b/modules/mono/.gitignore
@@ -1,2 +1,5 @@
# Do not ignore solution files inside the mono module. Overrides Godot's global gitignore.
!*.sln
+
+# Generated by build_assemblies.py.
+SdkPackageVersions.props
diff --git a/modules/mono/Directory.Build.props b/modules/mono/Directory.Build.props
index fbf864b11b..f7c8a825f9 100644
--- a/modules/mono/Directory.Build.props
+++ b/modules/mono/Directory.Build.props
@@ -1,3 +1,6 @@
<Project>
- <Import Project="$(MSBuildThisFileDirectory)\SdkPackageVersions.props" />
+ <PropertyGroup>
+ <GodotSdkPackageVersionsFilePath>$(MSBuildThisFileDirectory)\SdkPackageVersions.props</GodotSdkPackageVersionsFilePath>
+ </PropertyGroup>
+ <Import Project="$(GodotSdkPackageVersionsFilePath)" />
</Project>
diff --git a/modules/mono/Directory.Build.targets b/modules/mono/Directory.Build.targets
new file mode 100644
index 0000000000..e666b3ac9d
--- /dev/null
+++ b/modules/mono/Directory.Build.targets
@@ -0,0 +1,26 @@
+<Project>
+ <PropertyGroup>
+ <_HasNuGetPackage Condition=" '$(_HasNuGetPackage)' == '' And '$(PackageId)' != '' And '$(GeneratePackageOnBuild.ToLower())' == 'true' ">true</_HasNuGetPackage>
+ <_HasNuGetPackage Condition=" '$(_HasNuGetPackage)' == '' ">false</_HasNuGetPackage>
+ <_HasSymbolsNuGetPackage Condition=" '$(_HasSymbolsNuGetPackage)' == '' And '$(PackageId)' != '' And '$(IncludeSymbols.ToLower())' == 'true' And '$(SymbolPackageFormat)' == 'snupkg' ">true</_HasSymbolsNuGetPackage>
+ <_HasSymbolsNuGetPackage Condition=" '$(_HasSymbolsNuGetPackage)' == '' ">false</_HasSymbolsNuGetPackage>
+ </PropertyGroup>
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' ">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(MSBuildThisFileDirectory)\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
+ </PropertyGroup>
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ <Copy Condition=" '$(_HasSymbolsNuGetPackage)' == 'true' " SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).snupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ </Target>
+ <Target Name="PushNuGetPackagesToLocalSource" BeforeTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' And '$(PushNuGetToLocalSource)' != '' ">
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(PushNuGetToLocalSource)\" />
+ <Copy Condition=" '$(_HasSymbolsNuGetPackage)' == 'true' " SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).snupkg" DestinationFolder="$(PushNuGetToLocalSource)\" />
+ </Target>
+ <Target Name="ClearNuGetLocalPackageCache" BeforeTargets="Pack"
+ Condition=" '$(_HasNuGetPackage)' == 'true' And '$(ClearNuGetLocalCache.ToLower())' == 'true' ">
+ <RemoveDir Directories="$(NugetPackageRoot)/$(PackageId.ToLower())/$(PackageVersion)" />
+ </Target>
+</Project>
diff --git a/modules/mono/README.md b/modules/mono/README.md
new file mode 100644
index 0000000000..74b4531dfb
--- /dev/null
+++ b/modules/mono/README.md
@@ -0,0 +1,55 @@
+# How to build and run
+
+1. Build Godot with the module enabled: `module_mono_enabled=yes`.
+2. After building Godot, use it to generate the C# glue code:
+ ```sh
+ <godot_binary> --generate-mono-glue ./modules/mono/glue
+ ```
+3. Build the C# solutions:
+ ```sh
+ ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir ./bin
+ ```
+
+The paths specified in these examples assume the command is being run from
+the Godot source root.
+
+# How to deal with NuGet packages
+
+We distribute the API assemblies, our source generators, and our custom
+MSBuild project SDK as NuGet packages. This is all transparent to the user,
+but it can make things complicated during development.
+
+In order to use Godot with a development of those packages, we must create
+a local NuGet source where MSBuild can find them. This can be done with
+the .NET CLI:
+
+```sh
+dotnet nuget add source ~/MyLocalNugetSource --name MyLocalNugetSource
+```
+
+The Godot NuGet packages must be added to that local source. Additionally,
+we must make sure there are no other versions of the package in the NuGet
+cache, as MSBuild may pick one of those instead.
+
+In order to simplify this process, the `build_assemblies.py` script provides
+the following `--push-nupkgs-local` option:
+
+```sh
+./modules/mono/build_scripts/build_assemblies.py --godot-output-dir ./bin \
+ --push-nupkgs-local ~/MyLocalNugetSource
+```
+
+This option ensures the packages will be added to the specified local NuGet
+source and that conflicting versions of the package are removed from the
+NuGet cache. It's recommended to always use this option when building the
+C# solutions during development to avoid mistakes.
+
+# Double Precision Support (REAL_T_IS_DOUBLE)
+
+Follow the above instructions but build Godot with the precision=double argument to scons
+
+When building the NuGet packages, specify `--precision=double` - for example:
+```sh
+./modules/mono/build_scripts/build_assemblies.py --godot-output-dir ./bin \
+ --push-nupkgs-local ~/MyLocalNugetSource --precision=double
+```
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index d10ebc7b47..a4667f784d 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -7,49 +7,14 @@ Import("env_modules")
env_mono = env_modules.Clone()
-if env_mono["tools"]:
- # NOTE: It is safe to generate this file here, since this is still executed serially
- import build_scripts.gen_cs_glue_version as gen_cs_glue_version
-
- gen_cs_glue_version.generate_header("glue/GodotSharp", "glue/cs_glue_version.gen.h")
-
-# Glue sources
-if env_mono["mono_glue"]:
- env_mono.Append(CPPDEFINES=["MONO_GLUE_ENABLED"])
-
- import os.path
-
- if not os.path.isfile("glue/mono_glue.gen.cpp"):
- raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
-
-if env_mono["tools"] or env_mono["target"] != "release":
- env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
-
# Configure Mono
mono_configure.configure(env, env_mono)
-if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
- # Build Godot API solution
- import build_scripts.api_solution_build as api_solution_build
-
- api_sln_cmd = api_solution_build.build(env_mono)
-
- # Build GodotTools
- import build_scripts.godot_tools_build as godot_tools_build
-
- godot_tools_build.build(env_mono, api_sln_cmd)
-
- # Build Godot.NET.Sdk
- import build_scripts.godot_net_sdk_build as godot_net_sdk_build
-
- godot_net_sdk_build.build(env_mono)
-
# Add sources
env_mono.add_source_files(env.modules_sources, "*.cpp")
env_mono.add_source_files(env.modules_sources, "glue/*.cpp")
-env_mono.add_source_files(env.modules_sources, "glue/mono_glue.gen.cpp")
env_mono.add_source_files(env.modules_sources, "mono_gd/*.cpp")
env_mono.add_source_files(env.modules_sources, "utils/*.cpp")
@@ -61,6 +26,6 @@ if env["platform"] in ["macos", "ios"]:
elif env["platform"] == "android":
env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp")
-if env["tools"]:
+if env.editor_build:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
SConscript("editor/script_templates/SCsub")
diff --git a/modules/mono/SdkPackageVersions.props b/modules/mono/SdkPackageVersions.props
deleted file mode 100644
index bdec051625..0000000000
--- a/modules/mono/SdkPackageVersions.props
+++ /dev/null
@@ -1,7 +0,0 @@
-<Project>
- <PropertyGroup>
- <PackageFloatingVersion_Godot>4.0.*-*</PackageFloatingVersion_Godot>
- <PackageVersion_Godot_NET_Sdk>4.0.0-dev6</PackageVersion_Godot_NET_Sdk>
- <PackageVersion_Godot_SourceGenerators>4.0.0-dev3</PackageVersion_Godot_SourceGenerators>
- </PropertyGroup>
-</Project>
diff --git a/modules/mono/build_scripts/api_solution_build.py b/modules/mono/build_scripts/api_solution_build.py
deleted file mode 100644
index 9abac22df6..0000000000
--- a/modules/mono/build_scripts/api_solution_build.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Build the Godot API solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_api_solution(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
-
- build_config = env["solution_build_config"]
-
- extra_msbuild_args = ["/p:NoWarn=1591"] # Ignore missing documentation warnings
-
- from .solution_builder import build_solution
-
- build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
-
- # Copy targets
-
- core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharp", "bin", build_config))
- editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, "GodotSharpEditor", "bin", build_config))
-
- dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
-
- if not os.path.isdir(dst_dir):
- assert not os.path.isfile(dst_dir)
- os.makedirs(dst_dir)
-
- def copy_target(target_path):
- from shutil import copy
-
- filename = os.path.basename(target_path)
-
- src_path = os.path.join(core_src_dir, filename)
- if not os.path.isfile(src_path):
- src_path = os.path.join(editor_src_dir, filename)
-
- copy(src_path, target_path)
-
- for scons_target in target:
- copy_target(str(scons_target))
-
-
-def build(env_mono):
- assert env_mono["tools"]
-
- target_filenames = [
- "GodotSharp.dll",
- "GodotSharp.pdb",
- "GodotSharp.xml",
- "GodotSharpEditor.dll",
- "GodotSharpEditor.pdb",
- "GodotSharpEditor.xml",
- ]
-
- depend_cmd = []
-
- for build_config in ["Debug", "Release"]:
- output_dir = Dir("#bin").abspath
- editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config)
-
- targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(
- targets, depend_cmd, build_api_solution, module_dir=os.getcwd(), solution_build_config=build_config
- )
- env_mono.AlwaysBuild(cmd)
-
- # Make the Release build of the API solution depend on the Debug build.
- # We do this in order to prevent SCons from building them in parallel,
- # which can freak out MSBuild. In many cases, one of the builds would
- # hang indefinitely requiring a key to be pressed for it to continue.
- depend_cmd = cmd
-
- return depend_cmd
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
new file mode 100755
index 0000000000..0b91cda9b8
--- /dev/null
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -0,0 +1,389 @@
+#!/usr/bin/python3
+
+import os
+import os.path
+import shlex
+import subprocess
+from dataclasses import dataclass
+from typing import Optional, List
+
+
+def find_dotnet_cli():
+ if os.name == "nt":
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, "dotnet")
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
+ return hint_path + ".exe"
+ else:
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, "dotnet")
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+
+
+def find_msbuild_standalone_windows():
+ msbuild_tools_path = find_msbuild_tools_path_reg()
+
+ if msbuild_tools_path:
+ return os.path.join(msbuild_tools_path, "MSBuild.exe")
+
+ return None
+
+
+def find_msbuild_mono_windows(mono_prefix):
+ assert mono_prefix is not None
+
+ mono_bin_dir = os.path.join(mono_prefix, "bin")
+ msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
+
+ if os.path.isfile(msbuild_mono):
+ return msbuild_mono
+
+ return None
+
+
+def find_msbuild_mono_unix():
+ import sys
+
+ hint_dirs = []
+ if sys.platform == "darwin":
+ hint_dirs[:0] = [
+ "/Library/Frameworks/Mono.framework/Versions/Current/bin",
+ "/usr/local/var/homebrew/linked/mono/bin",
+ ]
+
+ for hint_dir in hint_dirs:
+ hint_path = os.path.join(hint_dir, "msbuild")
+ if os.path.isfile(hint_path):
+ return hint_path
+ elif os.path.isfile(hint_path + ".exe"):
+ return hint_path + ".exe"
+
+ for hint_dir in os.environ["PATH"].split(os.pathsep):
+ hint_dir = hint_dir.strip('"')
+ hint_path = os.path.join(hint_dir, "msbuild")
+ if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
+ return hint_path
+ if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
+ return hint_path + ".exe"
+
+ return None
+
+
+def find_msbuild_tools_path_reg():
+ import subprocess
+
+ program_files = os.getenv("PROGRAMFILES(X86)")
+ if not program_files:
+ program_files = os.getenv("PROGRAMFILES")
+ vswhere = os.path.join(program_files, "Microsoft Visual Studio", "Installer", "vswhere.exe")
+
+ vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"]
+
+ try:
+ lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
+
+ for line in lines:
+ parts = line.decode("utf-8").split(":", 1)
+
+ if len(parts) < 2 or parts[0] != "installationPath":
+ continue
+
+ val = parts[1].strip()
+
+ if not val:
+ raise ValueError("Value of `installationPath` entry is empty")
+
+ # Since VS2019, the directory is simply named "Current"
+ msbuild_dir = os.path.join(val, "MSBuild", "Current", "Bin")
+ if os.path.isdir(msbuild_dir):
+ return msbuild_dir
+
+ # Directory name "15.0" is used in VS 2017
+ return os.path.join(val, "MSBuild", "15.0", "Bin")
+
+ raise ValueError("Cannot find `installationPath` entry")
+ except ValueError as e:
+ print("Error reading output from vswhere: " + str(e))
+ except OSError:
+ pass # Fine, vswhere not found
+ except (subprocess.CalledProcessError, OSError):
+ pass
+
+
+@dataclass
+class ToolsLocation:
+ dotnet_cli: str = ""
+ msbuild_standalone: str = ""
+ msbuild_mono: str = ""
+ mono_bin_dir: str = ""
+
+
+def find_any_msbuild_tool(mono_prefix):
+ # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild
+
+ # Find dotnet CLI
+ dotnet_cli = find_dotnet_cli()
+ if dotnet_cli:
+ return ToolsLocation(dotnet_cli=dotnet_cli)
+
+ # Find standalone MSBuild
+ if os.name == "nt":
+ msbuild_standalone = find_msbuild_standalone_windows()
+ if msbuild_standalone:
+ return ToolsLocation(msbuild_standalone=msbuild_standalone)
+
+ if mono_prefix:
+ # Find Mono's MSBuild
+ if os.name == "nt":
+ msbuild_mono = find_msbuild_mono_windows(mono_prefix)
+ if msbuild_mono:
+ return ToolsLocation(msbuild_mono=msbuild_mono)
+ else:
+ msbuild_mono = find_msbuild_mono_unix()
+ if msbuild_mono:
+ return ToolsLocation(msbuild_mono=msbuild_mono)
+
+ return None
+
+
+def run_msbuild(tools: ToolsLocation, sln: str, msbuild_args: Optional[List[str]] = None):
+ using_msbuild_mono = False
+
+ # Preference order: dotnet CLI > Standalone MSBuild > Mono's MSBuild
+ if tools.dotnet_cli:
+ args = [tools.dotnet_cli, "msbuild"]
+ elif tools.msbuild_standalone:
+ args = [tools.msbuild_standalone]
+ elif tools.msbuild_mono:
+ args = [tools.msbuild_mono]
+ using_msbuild_mono = True
+ else:
+ raise RuntimeError("Path to MSBuild or dotnet CLI not provided.")
+
+ args += [sln]
+
+ if msbuild_args:
+ args += msbuild_args
+
+ print("Running MSBuild: ", " ".join(shlex.quote(arg) for arg in args), flush=True)
+
+ msbuild_env = os.environ.copy()
+
+ # Needed when running from Developer Command Prompt for VS
+ if "PLATFORM" in msbuild_env:
+ del msbuild_env["PLATFORM"]
+
+ if using_msbuild_mono:
+ # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
+ # building with Mono's MSBuild. They must point to the batch files
+ # in Mono's bin directory to make sure they are executed with Mono.
+ msbuild_env.update(
+ {
+ "CscToolExe": os.path.join(tools.mono_bin_dir, "csc.bat"),
+ "VbcToolExe": os.path.join(tools.mono_bin_dir, "vbc.bat"),
+ "FscToolExe": os.path.join(tools.mono_bin_dir, "fsharpc.bat"),
+ }
+ )
+
+ return subprocess.call(args, env=msbuild_env)
+
+
+def build_godot_api(msbuild_tool, module_dir, output_dir, push_nupkgs_local, precision):
+ target_filenames = [
+ "GodotSharp.dll",
+ "GodotSharp.pdb",
+ "GodotSharp.xml",
+ "GodotSharpEditor.dll",
+ "GodotSharpEditor.pdb",
+ "GodotSharpEditor.xml",
+ "GodotPlugins.dll",
+ "GodotPlugins.pdb",
+ "GodotPlugins.runtimeconfig.json",
+ ]
+
+ for build_config in ["Debug", "Release"]:
+ editor_api_dir = os.path.join(output_dir, "GodotSharp", "Api", build_config)
+
+ targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
+
+ args = ["/restore", "/t:Build", "/p:Configuration=" + build_config, "/p:NoWarn=1591"]
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+ if precision == "double":
+ args += ["/p:GodotFloat64=true"]
+
+ sln = os.path.join(module_dir, "glue/GodotSharp/GodotSharp.sln")
+ exit_code = run_msbuild(
+ msbuild_tool,
+ sln=sln,
+ msbuild_args=args,
+ )
+ if exit_code != 0:
+ return exit_code
+
+ # Copy targets
+
+ core_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharp", "bin", build_config))
+ editor_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotSharpEditor", "bin", build_config))
+ plugins_src_dir = os.path.abspath(os.path.join(sln, os.pardir, "GodotPlugins", "bin", build_config, "net6.0"))
+
+ if not os.path.isdir(editor_api_dir):
+ assert not os.path.isfile(editor_api_dir)
+ os.makedirs(editor_api_dir)
+
+ def copy_target(target_path):
+ from shutil import copy
+
+ filename = os.path.basename(target_path)
+
+ src_path = os.path.join(core_src_dir, filename)
+ if not os.path.isfile(src_path):
+ src_path = os.path.join(editor_src_dir, filename)
+ if not os.path.isfile(src_path):
+ src_path = os.path.join(plugins_src_dir, filename)
+
+ print(f"Copying assembly to {target_path}...")
+ copy(src_path, target_path)
+
+ for scons_target in targets:
+ copy_target(scons_target)
+
+ return 0
+
+
+def generate_sdk_package_versions():
+ # I can't believe importing files in Python is so convoluted when not
+ # following the golden standard for packages/modules.
+ import os
+ import sys
+ from os.path import dirname
+
+ # We want ../../../methods.py.
+ script_path = dirname(os.path.abspath(__file__))
+ root_path = dirname(dirname(dirname(script_path)))
+
+ sys.path.insert(0, root_path)
+ from methods import get_version_info
+
+ version_info = get_version_info("")
+ sys.path.remove(root_path)
+
+ version_str = "{major}.{minor}.{patch}".format(**version_info)
+ version_status = version_info["status"]
+ if version_status != "stable": # Pre-release
+ # If version was overridden to be e.g. "beta3", we insert a dot between
+ # "beta" and "3" to follow SemVer 2.0.
+ import re
+
+ match = re.search(r"[\d]+$", version_status)
+ if match:
+ pos = match.start()
+ version_status = version_status[:pos] + "." + version_status[pos:]
+ version_str += "-" + version_status
+
+ props = """<Project>
+ <PropertyGroup>
+ <PackageVersion_GodotSharp>{0}</PackageVersion_GodotSharp>
+ <PackageVersion_Godot_NET_Sdk>{0}</PackageVersion_Godot_NET_Sdk>
+ <PackageVersion_Godot_SourceGenerators>{0}</PackageVersion_Godot_SourceGenerators>
+ </PropertyGroup>
+</Project>
+""".format(
+ version_str
+ )
+
+ # We write in ../SdkPackageVersions.props.
+ with open(os.path.join(dirname(script_path), "SdkPackageVersions.props"), "w") as f:
+ f.write(props)
+ f.close()
+
+
+def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, push_nupkgs_local, precision):
+ # Generate SdkPackageVersions.props
+ generate_sdk_package_versions()
+
+ # Godot API
+ exit_code = build_godot_api(msbuild_tool, module_dir, output_dir, push_nupkgs_local, precision)
+ if exit_code != 0:
+ return exit_code
+
+ # GodotTools
+ sln = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
+ args = ["/restore", "/t:Build", "/p:Configuration=" + ("Debug" if dev_debug else "Release")] + (
+ ["/p:GodotPlatform=" + godot_platform] if godot_platform else []
+ )
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+ if precision == "double":
+ args += ["/p:GodotFloat64=true"]
+ exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args)
+ if exit_code != 0:
+ return exit_code
+
+ # Godot.NET.Sdk
+ args = ["/restore", "/t:Build", "/p:Configuration=Release"]
+ if push_nupkgs_local:
+ args += ["/p:ClearNuGetLocalCache=true", "/p:PushNuGetToLocalSource=" + push_nupkgs_local]
+ if precision == "double":
+ args += ["/p:GodotFloat64=true"]
+ sln = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
+ exit_code = run_msbuild(msbuild_tool, sln=sln, msbuild_args=args)
+ if exit_code != 0:
+ return exit_code
+
+ return 0
+
+
+def main():
+ import argparse
+ import sys
+
+ parser = argparse.ArgumentParser(description="Builds all Godot .NET solutions")
+ parser.add_argument("--godot-output-dir", type=str, required=True)
+ parser.add_argument(
+ "--dev-debug",
+ action="store_true",
+ default=False,
+ help="Build GodotTools and Godot.NET.Sdk with 'Configuration=Debug'",
+ )
+ parser.add_argument("--godot-platform", type=str, default="")
+ parser.add_argument("--mono-prefix", type=str, default="")
+ parser.add_argument("--push-nupkgs-local", type=str, default="")
+ parser.add_argument(
+ "--precision", type=str, default="single", choices=["single", "double"], help="Floating-point precision level"
+ )
+
+ args = parser.parse_args()
+
+ this_script_dir = os.path.dirname(os.path.realpath(__file__))
+ module_dir = os.path.abspath(os.path.join(this_script_dir, os.pardir))
+
+ output_dir = os.path.abspath(args.godot_output_dir)
+
+ push_nupkgs_local = os.path.abspath(args.push_nupkgs_local) if args.push_nupkgs_local else None
+
+ msbuild_tool = find_any_msbuild_tool(args.mono_prefix)
+
+ if msbuild_tool is None:
+ print("Unable to find MSBuild")
+ sys.exit(1)
+
+ exit_code = build_all(
+ msbuild_tool,
+ module_dir,
+ output_dir,
+ args.godot_platform,
+ args.dev_debug,
+ push_nupkgs_local,
+ args.precision,
+ )
+ sys.exit(exit_code)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/modules/mono/build_scripts/gen_cs_glue_version.py b/modules/mono/build_scripts/gen_cs_glue_version.py
deleted file mode 100644
index 98bbb4d9be..0000000000
--- a/modules/mono/build_scripts/gen_cs_glue_version.py
+++ /dev/null
@@ -1,20 +0,0 @@
-def generate_header(solution_dir, version_header_dst):
- import os
-
- latest_mtime = 0
- for root, dirs, files in os.walk(solution_dir, topdown=True):
- dirs[:] = [d for d in dirs if d not in ["Generated"]] # Ignored generated files
- files = [f for f in files if f.endswith(".cs")]
- for file in files:
- filepath = os.path.join(root, file)
- mtime = os.path.getmtime(filepath)
- latest_mtime = mtime if mtime > latest_mtime else latest_mtime
-
- glue_version = int(latest_mtime) # The latest modified time will do for now
-
- with open(version_header_dst, "w") as version_header:
- version_header.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- version_header.write("#ifndef CS_GLUE_VERSION_H\n")
- version_header.write("#define CS_GLUE_VERSION_H\n\n")
- version_header.write("#define CS_GLUE_VERSION UINT32_C(" + str(glue_version) + ")\n")
- version_header.write("\n#endif // CS_GLUE_VERSION_H\n")
diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py
deleted file mode 100644
index 8c5a60d2db..0000000000
--- a/modules/mono/build_scripts/godot_net_sdk_build.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Build Godot.NET.Sdk solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_godot_net_sdk(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
- build_config = "Release"
-
- from .solution_builder import build_solution
-
- extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
-
- build_solution(env, solution_path, build_config, extra_msbuild_args)
- # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
-
-
-def get_nupkgs_versions(props_file):
- import xml.etree.ElementTree as ET
-
- tree = ET.parse(props_file)
- root = tree.getroot()
-
- return {
- "Godot.NET.Sdk": root.find("./PropertyGroup/PackageVersion_Godot_NET_Sdk").text.strip(),
- "Godot.SourceGenerators": root.find("./PropertyGroup/PackageVersion_Godot_SourceGenerators").text.strip(),
- }
-
-
-def build(env_mono):
- assert env_mono["tools"]
-
- output_dir = Dir("#bin").abspath
- editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
- nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
-
- module_dir = os.getcwd()
-
- nupkgs_versions = get_nupkgs_versions(os.path.join(module_dir, "SdkPackageVersions.props"))
-
- target_filenames = [
- "Godot.NET.Sdk.%s.nupkg" % nupkgs_versions["Godot.NET.Sdk"],
- "Godot.SourceGenerators.%s.nupkg" % nupkgs_versions["Godot.SourceGenerators"],
- ]
-
- targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
- env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py
deleted file mode 100644
index 3bbbf29d3b..0000000000
--- a/modules/mono/build_scripts/godot_tools_build.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Build GodotTools solution
-
-import os
-
-from SCons.Script import Dir
-
-
-def build_godot_tools(source, target, env):
- # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
-
- module_dir = env["module_dir"]
-
- solution_path = os.path.join(module_dir, "editor/GodotTools/GodotTools.sln")
- build_config = "Debug" if env["target"] == "debug" else "Release"
-
- from .solution_builder import build_solution
-
- extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
-
- build_solution(env, solution_path, build_config, extra_msbuild_args)
- # No need to copy targets. The GodotTools csproj takes care of copying them.
-
-
-def build(env_mono, api_sln_cmd):
- assert env_mono["tools"]
-
- output_dir = Dir("#bin").abspath
- editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
-
- target_filenames = ["GodotTools.dll"]
-
- if env_mono["target"] == "debug":
- target_filenames += ["GodotTools.pdb"]
-
- targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
-
- cmd = env_mono.CommandNoCache(targets, api_sln_cmd, build_godot_tools, module_dir=os.getcwd())
- env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
deleted file mode 100644
index 3459244bc2..0000000000
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ /dev/null
@@ -1,55 +0,0 @@
-def generate_compressed_config(config_src, output_dir):
- import os.path
-
- # Source file
- with open(os.path.join(output_dir, "android_mono_config.gen.cpp"), "w") as cpp:
- with open(config_src, "rb") as f:
- buf = f.read()
- decompr_size = len(buf)
- import zlib
-
- # Use maximum zlib compression level to further reduce file size
- # (at the cost of initial build times).
- buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
- compr_size = len(buf)
-
- bytes_seq_str = ""
- for i, buf_idx in enumerate(range(compr_size)):
- if i > 0:
- bytes_seq_str += ", "
- bytes_seq_str += str(buf[buf_idx])
-
- cpp.write(
- """/* THIS FILE IS GENERATED DO NOT EDIT */
-#include "android_mono_config.h"
-
-#ifdef ANDROID_ENABLED
-
-#include "core/io/compression.h"
-
-
-namespace {
-
-// config
-static const int config_compressed_size = %d;
-static const int config_uncompressed_size = %d;
-static const unsigned char config_compressed_data[] = { %s };
-} // namespace
-
-String get_godot_android_mono_config() {
- Vector<uint8_t> data;
- data.resize(config_uncompressed_size);
- uint8_t* w = data.ptrw();
- 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()) != OK) {
- ERR_FAIL_V(String());
- }
- return s;
-}
-
-#endif // ANDROID_ENABLED
-"""
- % (compr_size, decompr_size, bytes_seq_str)
- )
diff --git a/modules/mono/build_scripts/mono_android_config.xml b/modules/mono/build_scripts/mono_android_config.xml
deleted file mode 100644
index e79670afd2..0000000000
--- a/modules/mono/build_scripts/mono_android_config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<configuration>
- <dllmap wordsize="32" dll="i:cygwin1.dll" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:cygwin1.dll" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="libc" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="libc" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="intl" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="intl" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="libintl" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="libintl" target="/system/lib64/libc.so" />
- <dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.so" />
- <dllmap dll="System.Native" target="libmono-native.so" />
- <dllmap wordsize="32" dll="i:msvcrt" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:msvcrt" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="i:msvcrt.dll" target="/system/lib/libc.so" />
- <dllmap wordsize="64" dll="i:msvcrt.dll" target="/system/lib64/libc.so" />
- <dllmap wordsize="32" dll="sqlite" target="/system/lib/libsqlite.so" />
- <dllmap wordsize="64" dll="sqlite" target="/system/lib64/libsqlite.so" />
- <dllmap wordsize="32" dll="sqlite3" target="/system/lib/libsqlite.so" />
- <dllmap wordsize="64" dll="sqlite3" target="/system/lib64/libsqlite.so" />
- <dllmap wordsize="32" dll="liblog" target="/system/lib/liblog.so" />
- <dllmap wordsize="64" dll="liblog" target="/system/lib64/liblog.so" />
- <dllmap dll="i:kernel32.dll">
- <dllentry dll="__Internal" name="CopyMemory" target="mono_win32_compat_CopyMemory"/>
- <dllentry dll="__Internal" name="FillMemory" target="mono_win32_compat_FillMemory"/>
- <dllentry dll="__Internal" name="MoveMemory" target="mono_win32_compat_MoveMemory"/>
- <dllentry dll="__Internal" name="ZeroMemory" target="mono_win32_compat_ZeroMemory"/>
- </dllmap>
-</configuration>
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index e69904c54b..5cec8f41f5 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -1,574 +1,26 @@
import os
import os.path
-import subprocess
-
-from SCons.Script import Dir, Environment
-
-if os.name == "nt":
- from . import mono_reg_utils as monoreg
-
-
-android_arch_dirs = {
- "armv7": "armeabi-v7a",
- "arm64v8": "arm64-v8a",
- "x86": "x86",
- "x86_64": "x86_64",
-}
-
-
-def get_android_out_dir(env):
- return os.path.join(
- Dir("#platform/android/java/lib/libs").abspath,
- "release" if env["target"] == "release" else "debug",
- android_arch_dirs[env["android_arch"]],
- )
-
-
-def find_name_in_dir_files(directory, names, prefixes=[""], extensions=[""]):
- for extension in extensions:
- if extension and not extension.startswith("."):
- extension = "." + extension
- for prefix in prefixes:
- for curname in names:
- if os.path.isfile(os.path.join(directory, prefix + curname + extension)):
- return curname
- return ""
-
-
-def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]):
- for extension in extensions:
- if extension and not extension.startswith("."):
- extension = "." + extension
- for prefix in prefixes:
- for curname in names:
- filename = prefix + curname + extension
- if os.path.isfile(os.path.join(directory, filename)):
- return filename
- return ""
-
-
-def copy_file(src_dir, dst_dir, src_name, dst_name=""):
- from shutil import copy
-
- src_path = os.path.join(Dir(src_dir).abspath, src_name)
- dst_dir = Dir(dst_dir).abspath
-
- if not os.path.isdir(dst_dir):
- os.makedirs(dst_dir)
-
- if dst_name:
- copy(src_path, os.path.join(dst_dir, dst_name))
- else:
- copy(src_path, dst_dir)
def is_desktop(platform):
- return platform in ["windows", "macos", "linuxbsd", "server", "uwp", "haiku"]
+ return platform in ["windows", "macos", "linuxbsd", "uwp", "haiku"]
def is_unix_like(platform):
- return platform in ["macos", "linuxbsd", "server", "android", "haiku", "ios"]
+ return platform in ["macos", "linuxbsd", "android", "haiku", "ios"]
def module_supports_tools_on(platform):
- return platform not in ["android", "javascript", "ios"]
-
-
-def find_wasm_src_dir(mono_root):
- hint_dirs = [
- os.path.join(mono_root, "src"),
- os.path.join(mono_root, "../src"),
- ]
- for hint_dir in hint_dirs:
- if os.path.isfile(os.path.join(hint_dir, "driver.c")):
- return hint_dir
- return ""
+ return is_desktop(platform)
def configure(env, env_mono):
- bits = env["bits"]
- is_android = env["platform"] == "android"
- is_javascript = env["platform"] == "javascript"
- is_ios = env["platform"] == "ios"
- is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"]
-
- tools_enabled = env["tools"]
- mono_static = env["mono_static"]
- copy_mono_root = env["copy_mono_root"]
-
- mono_prefix = env["mono_prefix"]
- mono_bcl = env["mono_bcl"]
-
- mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
-
- if is_android and not env["android_arch"] in android_arch_dirs:
- raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"])
-
- if tools_enabled and not module_supports_tools_on(env["platform"]):
- # TODO:
- # Android: We have to add the data directory to the apk, concretely the Api and Tools folders.
- raise RuntimeError("This module does not currently support building for this platform with tools enabled")
-
- if is_android and mono_static:
- # FIXME: When static linking and doing something that requires libmono-native, we get a dlopen error as 'libmono-native'
- # seems to depend on 'libmonosgen-2.0'. Could be fixed by re-directing to '__Internal' with a dllmap or in the dlopen hook.
- raise RuntimeError("Statically linking Mono is not currently supported for this platform")
-
- if not mono_static and (is_javascript or is_ios):
- raise RuntimeError("Dynamically linking Mono is not currently supported for this platform")
-
- if not mono_prefix and (os.getenv("MONO32_PREFIX") or os.getenv("MONO64_PREFIX")):
- print(
- "WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the"
- " 'mono_prefix' SCons parameter instead"
- )
-
- # Although we don't support building with tools for any platform where we currently use static AOT,
- # if these are supported in the future, we won't be using static AOT for them as that would be
- # too restrictive for the editor. These builds would probably be made to only use the interpreter.
- mono_aot_static = (is_ios and not is_ios_sim) and not env["tools"]
-
- # Static AOT is only supported on the root domain
- mono_single_appdomain = mono_aot_static
-
- if mono_single_appdomain:
- env_mono.Append(CPPDEFINES=["GD_MONO_SINGLE_APPDOMAIN"])
-
- if (env["tools"] or env["target"] != "release") and not mono_single_appdomain:
+ # is_android = env["platform"] == "android"
+ # is_web = env["platform"] == "web"
+ # is_ios = env["platform"] == "ios"
+ # is_ios_sim = is_ios and env["arch"] in ["x86_32", "x86_64"]
+
+ if env.editor_build:
+ if not module_supports_tools_on(env["platform"]):
+ raise RuntimeError("This module does not currently support building for this platform for editor builds.")
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])
-
- if env["platform"] == "windows":
- mono_root = mono_prefix
-
- if not mono_root and os.name == "nt":
- mono_root = monoreg.find_mono_root_dir(bits)
-
- if not mono_root:
- raise RuntimeError(
- "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
- )
-
- print("Found Mono root directory: " + mono_root)
-
- mono_lib_path = os.path.join(mono_root, "lib")
-
- env.Append(LIBPATH=mono_lib_path)
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
-
- lib_suffixes = [".lib"]
-
- if not env.msvc:
- # MingW supports both '.a' and '.lib'
- lib_suffixes.insert(0, ".a")
-
- if mono_static:
- if env.msvc:
- mono_static_lib_name = "libmono-static-sgen"
- else:
- mono_static_lib_name = "libmonosgen-2.0"
-
- mono_static_lib_file = find_file_in_dir(mono_lib_path, [mono_static_lib_name], extensions=lib_suffixes)
-
- if not mono_static_lib_file:
- raise RuntimeError("Could not find static mono library in: " + mono_lib_path)
-
- if env.msvc:
- env.Append(LINKFLAGS=mono_static_lib_file)
-
- env.Append(LINKFLAGS="Mincore.lib")
- env.Append(LINKFLAGS="msvcrt.lib")
- env.Append(LINKFLAGS="LIBCMT.lib")
- env.Append(LINKFLAGS="Psapi.lib")
- else:
- mono_static_lib_file_path = os.path.join(mono_lib_path, mono_static_lib_file)
- env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_static_lib_file_path, "-Wl,-no-whole-archive"])
-
- env.Append(LIBS=["psapi"])
- env.Append(LIBS=["version"])
- else:
- mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes)
-
- if not mono_lib_file:
- raise RuntimeError("Could not find mono library in: " + mono_lib_path)
-
- if env.msvc:
- env.Append(LINKFLAGS=mono_lib_file)
- else:
- mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file)
- env.Append(LINKFLAGS=mono_lib_file_path)
-
- mono_bin_path = os.path.join(mono_root, "bin")
-
- mono_dll_file = find_file_in_dir(mono_bin_path, mono_lib_names, prefixes=["", "lib"], extensions=[".dll"])
-
- if not mono_dll_file:
- raise RuntimeError("Could not find mono shared library in: " + mono_bin_path)
-
- copy_file(mono_bin_path, "#bin", mono_dll_file)
- else:
- is_apple = env["platform"] in ["macos", "ios"]
- is_macos = is_apple and not is_ios
-
- sharedlib_ext = ".dylib" if is_apple else ".so"
-
- mono_root = mono_prefix
- mono_lib_path = ""
- mono_so_file = ""
-
- if not mono_root and (is_android or is_javascript or is_ios):
- raise RuntimeError(
- "Mono installation directory not found; specify one manually with the 'mono_prefix' SCons parameter"
- )
-
- if not mono_root and is_macos:
- # 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):
- mono_root = hint_dir
- break
-
- # We can't use pkg-config to link mono statically,
- # but we can still use it to find the mono root directory
- if not mono_root and mono_static:
- mono_root = pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext)
- if not mono_root:
- raise RuntimeError(
- "Building with mono_static=yes, but failed to find the mono prefix with pkg-config; "
- + "specify one manually with the 'mono_prefix' SCons parameter"
- )
-
- if is_ios and not is_ios_sim:
- env_mono.Append(CPPDEFINES=["IOS_DEVICE"])
-
- if mono_root:
- print("Found Mono root directory: " + mono_root)
-
- mono_lib_path = os.path.join(mono_root, "lib")
-
- env.Append(LIBPATH=[mono_lib_path])
- env_mono.Prepend(CPPPATH=os.path.join(mono_root, "include", "mono-2.0"))
-
- mono_lib = find_name_in_dir_files(mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[".a"])
-
- if not mono_lib:
- raise RuntimeError("Could not find mono library in: " + mono_lib_path)
-
- env_mono.Append(CPPDEFINES=["_REENTRANT"])
-
- if mono_static:
- if not is_javascript:
- env.Append(LINKFLAGS=["-rdynamic"])
-
- mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a")
-
- if is_apple:
- if is_macos:
- env.Append(LINKFLAGS=["-Wl,-force_load," + mono_lib_file])
- else:
- arch = env["arch"]
-
- def copy_mono_lib(libname_wo_ext):
- copy_file(
- 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
- # the export templates and added to the Xcode project when exporting a game.
- copy_mono_lib("lib" + mono_lib)
- copy_mono_lib("libmono-native")
- copy_mono_lib("libmono-profiler-log")
-
- if not is_ios_sim:
- copy_mono_lib("libmono-ee-interp")
- copy_mono_lib("libmono-icall-table")
- copy_mono_lib("libmono-ilgen")
- else:
- assert is_desktop(env["platform"]) or is_android or is_javascript
- env.Append(LINKFLAGS=["-Wl,-whole-archive", mono_lib_file, "-Wl,-no-whole-archive"])
-
- if is_javascript:
- env.Append(LIBS=["mono-icall-table", "mono-native", "mono-ilgen", "mono-ee-interp"])
-
- wasm_src_dir = os.path.join(mono_root, "src")
- if not os.path.isdir(wasm_src_dir):
- raise RuntimeError("Could not find mono wasm src directory")
-
- # Ideally this should be defined only for 'driver.c', but I can't fight scons for another 2 hours
- env_mono.Append(CPPDEFINES=["CORE_BINDINGS"])
-
- env_mono.add_source_files(
- env.modules_sources,
- [
- os.path.join(wasm_src_dir, "driver.c"),
- os.path.join(wasm_src_dir, "zlib-helper.c"),
- os.path.join(wasm_src_dir, "corebindings.c"),
- ],
- )
-
- env.Append(
- LINKFLAGS=[
- "--js-library",
- os.path.join(wasm_src_dir, "library_mono.js"),
- "--js-library",
- os.path.join(wasm_src_dir, "binding_support.js"),
- "--js-library",
- os.path.join(wasm_src_dir, "dotnet_support.js"),
- ]
- )
- else:
- env.Append(LIBS=[mono_lib])
-
- if is_macos:
- env.Append(LIBS=["iconv", "pthread"])
- elif is_android:
- pass # Nothing
- elif is_ios:
- pass # Nothing, linking is delegated to the exported Xcode project
- elif is_javascript:
- env.Append(LIBS=["m", "rt", "dl", "pthread"])
- else:
- env.Append(LIBS=["m", "rt", "dl", "pthread"])
-
- if not mono_static:
- mono_so_file = find_file_in_dir(
- mono_lib_path, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext]
- )
-
- if not mono_so_file:
- raise RuntimeError("Could not find mono shared library in: " + mono_lib_path)
- else:
- assert not mono_static
-
- # TODO: Add option to force using pkg-config
- print("Mono root directory not found. Using pkg-config instead")
-
- env.ParseConfig("pkg-config monosgen-2 --libs")
- env_mono.ParseConfig("pkg-config monosgen-2 --cflags")
-
- tmpenv = Environment()
- tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
- tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
-
- for hint_dir in tmpenv["LIBPATH"]:
- file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
- if file_found:
- mono_lib_path = hint_dir
- mono_so_file = file_found
- break
-
- if not mono_so_file:
- raise RuntimeError("Could not find mono shared library in: " + str(tmpenv["LIBPATH"]))
-
- if not mono_static:
- libs_output_dir = get_android_out_dir(env) if is_android else "#bin"
- copy_file(mono_lib_path, libs_output_dir, mono_so_file)
-
- if not tools_enabled:
- if is_desktop(env["platform"]):
- if not mono_root:
- mono_root = (
- subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
- )
-
- make_template_dir(env, mono_root)
- elif is_android:
- # Compress Android Mono Config
- from . import make_android_mono_config
-
- module_dir = os.getcwd()
- config_file_path = os.path.join(module_dir, "build_scripts", "mono_android_config.xml")
- make_android_mono_config.generate_compressed_config(config_file_path, "mono_gd/")
-
- # Copy the required shared libraries
- copy_mono_shared_libs(env, mono_root, None)
- elif is_javascript:
- pass # No data directory for this platform
- elif is_ios:
- pass # No data directory for this platform
-
- if copy_mono_root:
- if not mono_root:
- mono_root = subprocess.check_output(["pkg-config", "mono-2", "--variable=prefix"]).decode("utf8").strip()
-
- if tools_enabled:
- # Only supported for editor builds.
- copy_mono_root_files(env, mono_root, mono_bcl)
-
-
-def make_template_dir(env, mono_root):
- from shutil import rmtree
-
- platform = env["platform"]
- target = env["target"]
-
- template_dir_name = ""
-
- assert is_desktop(platform)
-
- template_dir_name = "data.mono.%s.%s.%s" % (platform, env["bits"], target)
-
- output_dir = Dir("#bin").abspath
- template_dir = os.path.join(output_dir, template_dir_name)
-
- template_mono_root_dir = os.path.join(template_dir, "Mono")
-
- if os.path.isdir(template_mono_root_dir):
- rmtree(template_mono_root_dir) # Clean first
-
- # Copy etc/mono/
-
- template_mono_config_dir = os.path.join(template_mono_root_dir, "etc", "mono")
- copy_mono_etc_dir(mono_root, template_mono_config_dir, platform)
-
- # Copy the required shared libraries
-
- copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
-
-
-def copy_mono_root_files(env, mono_root, mono_bcl):
- from glob import glob
- from shutil import copy
- from shutil import rmtree
-
- if not mono_root:
- raise RuntimeError("Mono installation directory not found")
-
- output_dir = Dir("#bin").abspath
- editor_mono_root_dir = os.path.join(output_dir, "GodotSharp", "Mono")
-
- if os.path.isdir(editor_mono_root_dir):
- rmtree(editor_mono_root_dir) # Clean first
-
- # Copy etc/mono/
-
- editor_mono_config_dir = os.path.join(editor_mono_root_dir, "etc", "mono")
- copy_mono_etc_dir(mono_root, editor_mono_config_dir, env["platform"])
-
- # Copy the required shared libraries
-
- copy_mono_shared_libs(env, mono_root, editor_mono_root_dir)
-
- # Copy framework assemblies
-
- mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5")
- mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
-
- editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
- editor_mono_framework_facades_dir = os.path.join(editor_mono_framework_dir, "Facades")
-
- if not os.path.isdir(editor_mono_framework_dir):
- os.makedirs(editor_mono_framework_dir)
- if not os.path.isdir(editor_mono_framework_facades_dir):
- os.makedirs(editor_mono_framework_facades_dir)
-
- for assembly in glob(os.path.join(mono_framework_dir, "*.dll")):
- copy(assembly, editor_mono_framework_dir)
- for assembly in glob(os.path.join(mono_framework_facades_dir, "*.dll")):
- copy(assembly, editor_mono_framework_facades_dir)
-
-
-def copy_mono_etc_dir(mono_root, target_mono_config_dir, platform):
- from distutils.dir_util import copy_tree
- from glob import glob
- from shutil import copy
-
- if not os.path.isdir(target_mono_config_dir):
- os.makedirs(target_mono_config_dir)
-
- mono_etc_dir = os.path.join(mono_root, "etc", "mono")
- if not os.path.isdir(mono_etc_dir):
- mono_etc_dir = ""
- etc_hint_dirs = []
- if platform != "windows":
- etc_hint_dirs += ["/etc/mono", "/usr/local/etc/mono"]
- if "MONO_CFG_DIR" in os.environ:
- etc_hint_dirs += [os.path.join(os.environ["MONO_CFG_DIR"], "mono")]
- for etc_hint_dir in etc_hint_dirs:
- if os.path.isdir(etc_hint_dir):
- mono_etc_dir = etc_hint_dir
- break
- if not mono_etc_dir:
- raise RuntimeError("Mono installation etc directory not found")
-
- copy_tree(os.path.join(mono_etc_dir, "2.0"), os.path.join(target_mono_config_dir, "2.0"))
- copy_tree(os.path.join(mono_etc_dir, "4.0"), os.path.join(target_mono_config_dir, "4.0"))
- copy_tree(os.path.join(mono_etc_dir, "4.5"), os.path.join(target_mono_config_dir, "4.5"))
- if os.path.isdir(os.path.join(mono_etc_dir, "mconfig")):
- copy_tree(os.path.join(mono_etc_dir, "mconfig"), os.path.join(target_mono_config_dir, "mconfig"))
-
- for file in glob(os.path.join(mono_etc_dir, "*")):
- if os.path.isfile(file):
- copy(file, target_mono_config_dir)
-
-
-def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
- from shutil import copy
-
- def copy_if_exists(src, dst):
- if os.path.isfile(src):
- copy(src, dst)
-
- platform = env["platform"]
-
- if platform == "windows":
- src_mono_bin_dir = os.path.join(mono_root, "bin")
- target_mono_bin_dir = os.path.join(target_mono_root_dir, "bin")
-
- if not os.path.isdir(target_mono_bin_dir):
- os.makedirs(target_mono_bin_dir)
-
- mono_posix_helper_file = find_file_in_dir(
- src_mono_bin_dir, ["MonoPosixHelper"], prefixes=["", "lib"], extensions=[".dll"]
- )
- copy(
- os.path.join(src_mono_bin_dir, mono_posix_helper_file),
- os.path.join(target_mono_bin_dir, "MonoPosixHelper.dll"),
- )
-
- # For newer versions
- btls_dll_path = os.path.join(src_mono_bin_dir, "libmono-btls-shared.dll")
- if os.path.isfile(btls_dll_path):
- copy(btls_dll_path, target_mono_bin_dir)
- else:
- target_mono_lib_dir = (
- get_android_out_dir(env) if platform == "android" else os.path.join(target_mono_root_dir, "lib")
- )
-
- if not os.path.isdir(target_mono_lib_dir):
- os.makedirs(target_mono_lib_dir)
-
- lib_file_names = []
- if platform == "macos":
- lib_file_names = [
- lib_name + ".dylib"
- for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"]
- ]
- elif is_unix_like(platform):
- lib_file_names = [
- lib_name + ".so"
- for lib_name in [
- "libmono-btls-shared",
- "libmono-ee-interp",
- "libmono-native",
- "libMonoPosixHelper",
- "libmono-profiler-aot",
- "libmono-profiler-coverage",
- "libmono-profiler-log",
- "libMonoSupportW",
- ]
- ]
-
- for lib_file_name in lib_file_names:
- copy_if_exists(os.path.join(mono_root, "lib", lib_file_name), target_mono_lib_dir)
-
-
-def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
- tmpenv = Environment()
- tmpenv.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
- tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
- for hint_dir in tmpenv["LIBPATH"]:
- name_found = find_name_in_dir_files(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
- if name_found and os.path.isdir(os.path.join(hint_dir, "..", "include", "mono-2.0")):
- return os.path.join(hint_dir, "..")
- return ""
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
deleted file mode 100644
index 43c1ec8f8a..0000000000
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ /dev/null
@@ -1,112 +0,0 @@
-import os
-import platform
-
-if os.name == "nt":
- import winreg
-
-
-def _reg_open_key(key, subkey):
- try:
- return winreg.OpenKey(key, subkey)
- except OSError:
- if platform.architecture()[0] == "32bit":
- bitness_sam = winreg.KEY_WOW64_64KEY
- else:
- bitness_sam = winreg.KEY_WOW64_32KEY
- return winreg.OpenKey(key, subkey, 0, winreg.KEY_READ | bitness_sam)
-
-
-def _reg_open_key_bits(key, subkey, bits):
- sam = winreg.KEY_READ
-
- if platform.architecture()[0] == "32bit":
- if bits == "64":
- # Force 32bit process to search in 64bit registry
- sam |= winreg.KEY_WOW64_64KEY
- else:
- if bits == "32":
- # Force 64bit process to search in 32bit registry
- sam |= winreg.KEY_WOW64_32KEY
-
- return winreg.OpenKey(key, subkey, 0, sam)
-
-
-def _find_mono_in_reg(subkey, bits):
- try:
- with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- value = winreg.QueryValueEx(hKey, "SdkInstallRoot")[0]
- return value
- except OSError:
- return None
-
-
-def _find_mono_in_reg_old(subkey, bits):
- try:
- with _reg_open_key_bits(winreg.HKEY_LOCAL_MACHINE, subkey, bits) as hKey:
- default_clr = winreg.QueryValueEx(hKey, "DefaultCLR")[0]
- if default_clr:
- return _find_mono_in_reg(subkey + "\\" + default_clr, bits)
- return None
- except OSError:
- return None
-
-
-def find_mono_root_dir(bits):
- root_dir = _find_mono_in_reg(r"SOFTWARE\Mono", bits)
- if root_dir is not None:
- return str(root_dir)
- root_dir = _find_mono_in_reg_old(r"SOFTWARE\Novell\Mono", bits)
- if root_dir is not None:
- return str(root_dir)
- return ""
-
-
-def find_msbuild_tools_path_reg():
- import subprocess
-
- vswhere = os.getenv("PROGRAMFILES(X86)")
- if not vswhere:
- vswhere = os.getenv("PROGRAMFILES")
- vswhere += r"\Microsoft Visual Studio\Installer\vswhere.exe"
-
- vswhere_args = ["-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"]
-
- try:
- lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
-
- for line in lines:
- parts = line.decode("utf-8").split(":", 1)
-
- if len(parts) < 2 or parts[0] != "installationPath":
- continue
-
- val = parts[1].strip()
-
- if not val:
- raise ValueError("Value of `installationPath` entry is empty")
-
- # Since VS2019, the directory is simply named "Current"
- msbuild_dir = os.path.join(val, "MSBuild\\Current\\Bin")
- if os.path.isdir(msbuild_dir):
- return msbuild_dir
-
- # Directory name "15.0" is used in VS 2017
- return os.path.join(val, "MSBuild\\15.0\\Bin")
-
- raise ValueError("Cannot find `installationPath` entry")
- except ValueError as e:
- print("Error reading output from vswhere: " + e.message)
- except subprocess.CalledProcessError as e:
- print(e.output)
- except OSError as e:
- print(e)
-
- # Try to find 14.0 in the Registry
-
- try:
- subkey = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"
- with _reg_open_key(winreg.HKEY_LOCAL_MACHINE, subkey) as hKey:
- value = winreg.QueryValueEx(hKey, "MSBuildToolsPath")[0]
- return value
- except OSError:
- return ""
diff --git a/modules/mono/build_scripts/solution_builder.py b/modules/mono/build_scripts/solution_builder.py
deleted file mode 100644
index 6a621c3c8b..0000000000
--- a/modules/mono/build_scripts/solution_builder.py
+++ /dev/null
@@ -1,145 +0,0 @@
-import os
-
-
-verbose = False
-
-
-def find_dotnet_cli():
- import os.path
-
- if os.name == "nt":
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "dotnet")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
- else:
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "dotnet")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
-
-
-def find_msbuild_unix():
- import os.path
- import sys
-
- hint_dirs = []
- if sys.platform == "darwin":
- hint_dirs[:0] = [
- "/Library/Frameworks/Mono.framework/Versions/Current/bin",
- "/usr/local/var/homebrew/linked/mono/bin",
- ]
-
- for hint_dir in hint_dirs:
- hint_path = os.path.join(hint_dir, "msbuild")
- if os.path.isfile(hint_path):
- return hint_path
- elif os.path.isfile(hint_path + ".exe"):
- return hint_path + ".exe"
-
- for hint_dir in os.environ["PATH"].split(os.pathsep):
- hint_dir = hint_dir.strip('"')
- hint_path = os.path.join(hint_dir, "msbuild")
- if os.path.isfile(hint_path) and os.access(hint_path, os.X_OK):
- return hint_path
- if os.path.isfile(hint_path + ".exe") and os.access(hint_path + ".exe", os.X_OK):
- return hint_path + ".exe"
-
- return None
-
-
-def find_msbuild_windows(env):
- from .mono_reg_utils import find_mono_root_dir, find_msbuild_tools_path_reg
-
- mono_root = env["mono_prefix"] or find_mono_root_dir(env["bits"])
-
- if not mono_root:
- raise RuntimeError("Cannot find mono root directory")
-
- mono_bin_dir = os.path.join(mono_root, "bin")
- msbuild_mono = os.path.join(mono_bin_dir, "msbuild.bat")
-
- msbuild_tools_path = find_msbuild_tools_path_reg()
-
- if msbuild_tools_path:
- return (os.path.join(msbuild_tools_path, "MSBuild.exe"), {})
-
- if os.path.isfile(msbuild_mono):
- # The (Csc/Vbc/Fsc)ToolExe environment variables are required when
- # building with Mono's MSBuild. They must point to the batch files
- # in Mono's bin directory to make sure they are executed with Mono.
- mono_msbuild_env = {
- "CscToolExe": os.path.join(mono_bin_dir, "csc.bat"),
- "VbcToolExe": os.path.join(mono_bin_dir, "vbc.bat"),
- "FscToolExe": os.path.join(mono_bin_dir, "fsharpc.bat"),
- }
- return (msbuild_mono, mono_msbuild_env)
-
- return None
-
-
-def run_command(command, args, env_override=None, name=None):
- def cmd_args_to_str(cmd_args):
- return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args])
-
- args = [command] + args
-
- if name is None:
- name = os.path.basename(command)
-
- if verbose:
- print("Running '%s': %s" % (name, cmd_args_to_str(args)))
-
- import subprocess
-
- try:
- if env_override is None:
- subprocess.check_call(args)
- else:
- subprocess.check_call(args, env=env_override)
- except subprocess.CalledProcessError as e:
- raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
-
-
-def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
- global verbose
- verbose = env["verbose"]
-
- msbuild_env = os.environ.copy()
-
- # Needed when running from Developer Command Prompt for VS
- if "PLATFORM" in msbuild_env:
- del msbuild_env["PLATFORM"]
-
- msbuild_args = []
-
- dotnet_cli = find_dotnet_cli()
-
- if dotnet_cli:
- msbuild_path = dotnet_cli
- msbuild_args += ["msbuild"] # `dotnet msbuild` command
- else:
- # Find MSBuild
- if os.name == "nt":
- msbuild_info = find_msbuild_windows(env)
- if msbuild_info is None:
- raise RuntimeError("Cannot find MSBuild executable")
- msbuild_path = msbuild_info[0]
- msbuild_env.update(msbuild_info[1])
- else:
- msbuild_path = find_msbuild_unix()
- if msbuild_path is None:
- raise RuntimeError("Cannot find MSBuild executable")
-
- print("MSBuild path: " + msbuild_path)
-
- # Build solution
-
- msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config]
- msbuild_args += extra_msbuild_args
-
- run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index c4547b4323..733f1dbe34 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* class_db_api_json.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* class_db_api_json.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "class_db_api_json.h"
@@ -227,8 +227,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Ref<FileAccess> f = FileAccess::open(p_output_file, FileAccess::WRITE);
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_output_file + "'.");
- JSON json;
- f->store_string(json.stringify(classes_dict, "\t"));
+ f->store_string(JSON::stringify(classes_dict, "\t"));
print_line(String() + "ClassDB API JSON written to: " + ProjectSettings::get_singleton()->globalize_path(p_output_file));
}
diff --git a/modules/mono/class_db_api_json.h b/modules/mono/class_db_api_json.h
index 1d2000b033..d222988f1d 100644
--- a/modules/mono/class_db_api_json.h
+++ b/modules/mono/class_db_api_json.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* class_db_api_json.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* class_db_api_json.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CLASS_DB_API_JSON_H
#define CLASS_DB_API_JSON_H
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 3e6584590c..a36083b64b 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,8 +1,16 @@
-supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"]
+# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "haiku", "web", "ios"]
+# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
+supported_platforms = ["windows", "macos", "linuxbsd"]
def can_build(env, platform):
- return not env["arch"].startswith("rv")
+ if env["arch"].startswith("rv"):
+ return False
+
+ if env.editor_build:
+ env.module_add_dependencies("mono", ["regex"])
+
+ return True
def configure(env):
@@ -13,52 +21,6 @@ def configure(env):
env.add_module_version_string("mono")
- from SCons.Script import BoolVariable, PathVariable, Variables, Help
-
- default_mono_static = platform in ["ios", "javascript"]
- default_mono_bundles_zlib = platform in ["javascript"]
-
- envvars = Variables()
- envvars.Add(
- PathVariable(
- "mono_prefix",
- "Path to the Mono installation directory for the target platform and architecture",
- "",
- PathVariable.PathAccept,
- )
- )
- envvars.Add(
- PathVariable(
- "mono_bcl",
- "Path to a custom Mono BCL (Base Class Library) directory for the target platform",
- "",
- PathVariable.PathAccept,
- )
- )
- envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
- envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True))
- envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
- envvars.Add(
- BoolVariable("copy_mono_root", "Make a copy of the Mono installation directory to bundle with the editor", True)
- )
-
- # TODO: It would be great if this could be detected automatically instead
- envvars.Add(
- BoolVariable(
- "mono_bundles_zlib", "Specify if the Mono runtime was built with bundled zlib", default_mono_bundles_zlib
- )
- )
-
- envvars.Update(env)
- Help(envvars.GenerateHelpText(env))
-
- if env["mono_bundles_zlib"]:
- # Mono may come with zlib bundled for WASM or on newer version when built with MinGW.
- print("This Mono runtime comes with zlib bundled. Disabling 'builtin_zlib'...")
- env["builtin_zlib"] = False
- thirdparty_zlib_dir = "#thirdparty/zlib/"
- env.Prepend(CPPPATH=[thirdparty_zlib_dir])
-
def get_doc_classes():
return [
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index c7279be97f..7606465b8a 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1,37 +1,35 @@
-/*************************************************************************/
-/* csharp_script.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csharp_script.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "csharp_script.h"
-#include <mono/metadata/threads.h>
-#include <mono/metadata/tokentype.h>
#include <stdint.h>
#include "core/config/project_settings.h"
@@ -48,6 +46,7 @@
#include "editor/editor_internal_calls.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/inspector_dock.h"
#include "editor/node_dock.h"
#include "editor/script_templates/templates.gen.h"
#endif
@@ -57,10 +56,8 @@
#endif
#include "godotsharp_dirs.h"
+#include "managed_callable.h"
#include "mono_gd/gd_mono_cache.h"
-#include "mono_gd/gd_mono_class.h"
-#include "mono_gd/gd_mono_marshal.h"
-#include "mono_gd/gd_mono_utils.h"
#include "signal_awaiter_utils.h"
#include "utils/macros.h"
#include "utils/string_utils.h"
@@ -69,23 +66,14 @@
#ifdef TOOLS_ENABLED
static bool _create_project_solution_if_needed() {
- String sln_path = GodotSharpDirs::get_project_sln_path();
- String csproj_path = GodotSharpDirs::get_project_csproj_path();
-
- if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
- // A solution does not yet exist, create a new one
-
- CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
- return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
- }
-
- return true;
+ CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == nullptr);
+ return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolutionIfNeeded");
}
#endif
CSharpLanguage *CSharpLanguage::singleton = nullptr;
-GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = {
+GDExtensionInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = {
&_instance_binding_create_callback,
&_instance_binding_free_callback,
&_instance_binding_reference_callback
@@ -118,26 +106,26 @@ void CSharpLanguage::init() {
}
#endif
- gdmono = memnew(GDMono);
- gdmono->initialize();
-
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
- // Generate bindings here, before loading assemblies. 'initialize_load_assemblies' aborts
- // the applications if the api assemblies or the main tools assembly is missing, but this
- // is not a problem for BindingsGenerator as it only needs the tools project editor assembly.
+ // Generate the bindings here, before loading assemblies. The Godot assemblies
+ // may be missing if the glue wasn't generated yet in order to build them.
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
BindingsGenerator::handle_cmdline_args(cmdline_args);
#endif
-#ifndef MONO_GLUE_ENABLED
- print_line("Run this binary with '--generate-mono-glue path/to/modules/mono/glue'");
+ GLOBAL_DEF("dotnet/project/assembly_name", "");
+#ifdef TOOLS_ENABLED
+ GLOBAL_DEF("dotnet/project/solution_directory", "");
#endif
+ gdmono = memnew(GDMono);
+ gdmono->initialize();
+
+#ifdef TOOLS_ENABLED
if (gdmono->is_runtime_initialized()) {
gdmono->initialize_load_assemblies();
}
-#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&_editor_init_callback);
#endif
}
@@ -408,10 +396,10 @@ bool CSharpLanguage::supports_builtin_mode() const {
#ifdef TOOLS_ENABLED
static String variant_type_to_managed_name(const String &p_var_type_name) {
if (p_var_type_name.is_empty()) {
- return "object";
+ return "Variant";
}
- if (!ClassDB::class_exists(p_var_type_name)) {
+ if (ClassDB::class_exists(p_var_type_name)) {
return p_var_type_name;
}
@@ -419,12 +407,12 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
return "Godot.Object";
}
+ if (p_var_type_name == Variant::get_type_name(Variant::INT)) {
+ return "long";
+ }
+
if (p_var_type_name == Variant::get_type_name(Variant::FLOAT)) {
-#ifdef REAL_T_IS_DOUBLE
return "double";
-#else
- return "float";
-#endif
}
if (p_var_type_name == Variant::get_type_name(Variant::STRING)) {
@@ -468,7 +456,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
}
if (p_var_type_name == Variant::get_type_name(Variant::SIGNAL)) {
- return "SignalInfo";
+ return "Signal";
}
Variant::Type var_types[] = {
@@ -502,7 +490,7 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
}
}
- return "object";
+ return "Variant";
}
String CSharpLanguage::make_function(const String &, const String &p_name, const PackedStringArray &p_args) const {
@@ -596,23 +584,19 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
return Vector<StackInfo>();
}
_recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- GD_MONO_SCOPE_THREAD_ATTACH;
+ SCOPE_EXIT {
+ _recursion_flag_ = false;
+ };
- if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_core_api_assembly() || !GDMonoCache::cached_data.corlib_cache_updated) {
+ if (!gdmono->is_runtime_initialized()) {
return Vector<StackInfo>();
}
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
-
- MonoBoolean need_file_info = true;
- void *ctor_args[1] = { &need_file_info };
-
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_bool)->invoke_raw(stack_trace, ctor_args);
-
Vector<StackInfo> si;
- si = stack_trace_get_info(stack_trace);
+
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.DebuggingUtils_GetCurrentStackInfo(&si);
+ }
return si;
#else
@@ -620,63 +604,6 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
#endif
}
-#ifdef DEBUG_ENABLED
-Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
- // Printing an error here will result in endless recursion, so we must be careful
- static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_) {
- return Vector<StackInfo>();
- }
- _recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoException *exc = nullptr;
-
- MonoArray *frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames).invoke(p_stack_trace, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return Vector<StackInfo>();
- }
-
- int frame_count = mono_array_length(frames);
-
- if (frame_count <= 0) {
- return Vector<StackInfo>();
- }
-
- Vector<StackInfo> si;
- si.resize(frame_count);
-
- for (int i = 0; i < frame_count; i++) {
- StackInfo &sif = si.write[i];
- MonoObject *frame = mono_array_get(frames, MonoObject *, i);
-
- MonoString *file_name;
- int file_line_num;
- MonoString *method_decl;
- CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo).invoke(frame, &file_name, &file_line_num, &method_decl, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return Vector<StackInfo>();
- }
-
- // TODO
- // what if the StackFrame method is null (method_decl is empty). should we skip this frame?
- // can reproduce with a MissingMethodException on internal calls
-
- sif.file = GDMonoMarshal::mono_string_to_godot(file_name);
- sif.line = file_line_num;
- sif.func = GDMonoMarshal::mono_string_to_godot(method_decl);
- }
-
- return si;
-}
-#endif
-
void CSharpLanguage::post_unsafe_reference(Object *p_obj) {
#ifdef DEBUG_ENABLED
MutexLock lock(unsafe_object_references_lock);
@@ -698,48 +625,36 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
}
void CSharpLanguage::frame() {
- if (gdmono && gdmono->is_runtime_initialized() && gdmono->get_core_api_assembly() != nullptr) {
- const Ref<MonoGCHandleRef> &task_scheduler_handle = GDMonoCache::cached_data.task_scheduler_handle;
-
- if (task_scheduler_handle.is_valid()) {
- MonoObject *task_scheduler = task_scheduler_handle->get_target();
-
- if (task_scheduler) {
- MonoException *exc = nullptr;
- CACHED_METHOD_THUNK(GodotTaskScheduler, Activate).invoke(task_scheduler, &exc);
-
- if (exc) {
- GDMonoUtils::debug_unhandled_exception(exc);
- }
- }
- }
+ if (gdmono && gdmono->is_runtime_initialized() && GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_FrameCallback();
}
}
struct CSharpScriptDepSort {
- // must support sorting so inheritance works properly (parent must be reloaded first)
+ // Must support sorting so inheritance works properly (parent must be reloaded first)
bool operator()(const Ref<CSharpScript> &A, const Ref<CSharpScript> &B) const {
if (A == B) {
- return false; // shouldn't happen but..
+ // Shouldn't happen but just in case...
+ return false;
}
- GDMonoClass *I = B->base;
+ const Script *I = B->get_base_script().ptr();
while (I) {
- if (I == A->script_class) {
+ if (I == A.ptr()) {
// A is a base of B
return true;
}
- I = I->get_parent_class();
+ I = I->get_base_script().ptr();
}
- return false; // not a base
+ // A isn't a base of B
+ return false;
}
};
void CSharpLanguage::reload_all_scripts() {
#ifdef GD_MONO_HOT_RELOAD
if (is_assembly_reloading_needed()) {
- GD_MONO_SCOPE_THREAD_ATTACH;
reload_assemblies(false);
}
#endif
@@ -756,7 +671,6 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
#ifdef GD_MONO_HOT_RELOAD
if (is_assembly_reloading_needed()) {
- GD_MONO_SCOPE_THREAD_ATTACH;
reload_assemblies(p_soft_reload);
}
#endif
@@ -768,28 +682,28 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
return false;
}
- GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
-
- String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name();
+ String assembly_path = gdmono->get_project_assembly_path();
- appname_safe += ".dll";
-
- if (proj_assembly) {
- String proj_asm_path = proj_assembly->get_path();
-
- if (!FileAccess::exists(proj_asm_path)) {
- // Maybe it wasn't loaded from the default path, so check this as well
- proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
- if (!FileAccess::exists(proj_asm_path)) {
- return false; // No assembly to load
- }
+ if (!assembly_path.is_empty()) {
+ if (!FileAccess::exists(assembly_path)) {
+ return false; // No assembly to load
}
- if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) {
+ if (FileAccess::get_modified_time(assembly_path) <= gdmono->get_project_assembly_modified_time()) {
return false; // Already up to date
}
} else {
- if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe))) {
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
+
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
+ }
+
+ assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
+ .path_join(assembly_name + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
+
+ if (!FileAccess::exists(assembly_path)) {
return false; // No assembly to load
}
}
@@ -802,6 +716,18 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ // We disable collectible assemblies in the game player, because the limitations cause
+ // issues with mocking libraries. As such, we can only reload assemblies in the editor.
+ return;
+ }
+
+ // TODO:
+ // Currently, this reloads all scripts, including those whose class is not part of the
+ // assembly load context being unloaded. As such, we unnecessarily reload GodotTools.
+
+ print_verbose(".NET: Reloading assemblies...");
+
// There is no soft reloading with Mono. It's always hard reloading.
List<Ref<CSharpScript>> scripts;
@@ -824,18 +750,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
ManagedCallable *managed_callable = elem->self();
- MonoDelegate *delegate = (MonoDelegate *)managed_callable->delegate_handle.get_target();
+ ERR_CONTINUE(managed_callable->delegate_handle.value == nullptr);
Array serialized_data;
- MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
- MonoException *exc = nullptr;
- bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate, managed_serialized_data, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool success = GDMonoCache::managed_callbacks.DelegateUtils_TrySerializeDelegateWithGCHandle(
+ managed_callable->delegate_handle, &serialized_data);
if (success) {
ManagedCallable::instances_pending_reload.insert(managed_callable, serialized_data);
@@ -864,23 +784,25 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// If someone removes a script from a node, deletes the script, builds, adds a script to the
// same node, then builds again, the script might have no path and also no script_class. In
// that case, we can't (and don't need to) reload it.
- if (script->get_path().is_empty() && !script->script_class) {
+ if (script->get_path().is_empty() && !script->valid) {
continue;
}
to_reload.push_back(script);
- if (script->get_path().is_empty()) {
- script->tied_class_name_for_reload = script->script_class->get_name_for_lookup();
- script->tied_class_namespace_for_reload = script->script_class->get_namespace();
- }
-
// 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) {
script->pending_reload_instances.insert(obj->get_instance_id());
+ // Since this script instance wasn't a placeholder, add it to the list of placeholders
+ // that will have to be eventually replaced with a script instance in case it turns into one.
+ // This list is not cleared after the reload and the collected instances only leave
+ // the list if the script is instantiated or if it was a tool script but becomes a
+ // non-tool script in a rebuild.
+ script->pending_replace_placeholders.insert(obj->get_instance_id());
+
RefCounted *rc = Object::cast_to<RefCounted>(obj);
if (rc) {
rc_instances.push_back(Ref<RefCounted>(rc));
@@ -907,17 +829,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
- // Call OnBeforeSerialize
- if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call(string_names.on_before_serialize);
- }
+ // Call OnBeforeSerialize and save instance info
- // Save instance info
CSharpScript::StateBackup state;
- // TODO: Proper state backup (Not only variants, serialize managed state of scripts)
- csi->get_properties_state_for_reloading(state.properties);
- csi->get_event_signals_state_for_reloading(state.event_signals);
+ Dictionary properties;
+
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_SerializeState(
+ csi->get_gchandle_intptr(), &properties, &state.event_signals);
+
+ for (const Variant *s = properties.next(nullptr); s != nullptr; s = properties.next(s)) {
+ StringName name = *s;
+ Variant value = properties[*s];
+ state.properties.push_back(Pair<StringName, Variant>(name, value));
+ }
owners_map[obj->get_instance_id()] = state;
}
@@ -930,11 +855,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
}
+ script->was_tool_before_reload = script->tool;
script->_clear();
}
// Do domain reload
- if (gdmono->reload_scripts_domain() != OK) {
+ if (gdmono->reload_project_assemblies() != OK) {
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
for (Ref<CSharpScript> &scr : to_reload) {
@@ -965,6 +891,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
scr->pending_reload_state.erase(obj_id);
}
+
+ scr->pending_reload_instances.clear();
+ scr->pending_reload_state.clear();
}
return;
@@ -976,53 +905,27 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#ifdef TOOLS_ENABLED
script->exports_invalidated = true;
#endif
- script->signals_invalidated = true;
if (!script->get_path().is_empty()) {
script->reload(p_soft_reload);
if (!script->valid) {
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
continue;
}
} else {
- const StringName &class_namespace = script->tied_class_namespace_for_reload;
- const StringName &class_name = script->tied_class_name_for_reload;
- GDMonoAssembly *project_assembly = gdmono->get_project_assembly();
-
- // Search in project and tools assemblies first as those are the most likely to have the class
- GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : nullptr);
-
-#ifdef TOOLS_ENABLED
- if (!script_class) {
- GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly();
- script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : nullptr);
- }
-#endif
-
- if (!script_class) {
- script_class = gdmono->get_class(class_namespace, class_name);
- }
+ bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(script.ptr());
- if (!script_class) {
- // The class was removed, can't reload
+ if (!success) {
+ // Couldn't reload
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
continue;
}
-
- bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class);
- if (!obj_type) {
- // The class no longer inherits Godot.Object, can't reload
- script->pending_reload_instances.clear();
- continue;
- }
-
- GDMonoClass *native = GDMonoUtils::get_class_native_base(script_class);
-
- CSharpScript::initialize_for_managed_type(script, script_class, native);
}
- StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
+ StringName native_name = script->get_instance_base_type();
{
for (const ObjectID &obj_id : script->pending_reload_instances) {
@@ -1041,24 +944,34 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ScriptInstance *si = obj->get_script_instance();
+ // Check if the script must be instantiated or kept as a placeholder
+ // when the script may not be a tool (see #65266)
+ bool replace_placeholder = script->pending_replace_placeholders.has(obj->get_instance_id());
+ if (!script->is_tool() && script->was_tool_before_reload) {
+ // The script was a tool before the rebuild so the removal was intentional.
+ replace_placeholder = false;
+ script->pending_replace_placeholders.erase(obj->get_instance_id());
+ }
+
#ifdef TOOLS_ENABLED
if (si) {
// If the script instance is not null, then it must be a placeholder.
// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
CRASH_COND(!si->is_placeholder());
- if (script->is_tool() || ScriptServer::is_scripting_enabled()) {
- // Replace placeholder with a script instance
+ if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
+ // Replace placeholder with a script instance.
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- // Backup placeholder script instance state before replacing it with a script instance
+ // Backup placeholder script instance state before replacing it with a script instance.
si->get_property_state(state_backup.properties);
ScriptInstance *script_instance = script->instance_create(obj);
if (script_instance) {
script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
+ script->pending_replace_placeholders.erase(obj->get_instance_id());
obj->set_script_instance(script_instance);
}
}
@@ -1068,8 +981,24 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#else
CRASH_COND(si != nullptr);
#endif
- // Re-create script instance
- obj->set_script(script); // will create the script instance as well
+
+ // Re-create the script instance.
+ if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
+ // Create script instance or replace placeholder with a script instance.
+ ScriptInstance *script_instance = script->instance_create(obj);
+
+ if (script_instance) {
+ script->pending_replace_placeholders.erase(obj->get_instance_id());
+ obj->set_script_instance(script_instance);
+ continue;
+ }
+ }
+ // The script instance could not be instantiated or wasn't in the list of placeholders to replace.
+ obj->set_script(script);
+#if DEBUG_ENABLED
+ // If we reached here, the instantiated script must be a placeholder.
+ CRASH_COND(!obj->get_script_instance()->is_placeholder());
+#endif
}
}
@@ -1087,57 +1016,25 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ERR_CONTINUE(!obj->get_script_instance());
- // TODO: Restore serialized state
-
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- for (const Pair<StringName, Variant> &G : state_backup.properties) {
- obj->get_script_instance()->set(G.first, G.second);
- }
-
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
if (csi) {
- for (const Pair<StringName, Array> &G : state_backup.event_signals) {
- const StringName &name = G.first;
- const Array &serialized_data = G.second;
-
- 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;
+ Dictionary properties;
- MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
- MonoDelegate *delegate = nullptr;
-
- MonoException *exc = nullptr;
- bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
-
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
-
- if (success) {
- ERR_CONTINUE(delegate == nullptr);
- event_signal.field->set_value(csi->get_mono_object(), (MonoObject *)delegate);
- } else if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Failed to deserialize event signal delegate\n");
- }
+ for (const Pair<StringName, Variant> &G : state_backup.properties) {
+ properties[G.first] = G.second;
}
- // Call OnAfterDeserialization
- if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener))) {
- obj->get_script_instance()->call(string_names.on_after_deserialize);
- }
+ // Restore serialized state and call OnAfterDeserialization
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_DeserializeState(
+ csi->get_gchandle_intptr(), &properties, &state_backup.event_signals);
}
}
script->pending_reload_instances.clear();
+ script->pending_reload_state.clear();
}
// Deserialize managed callables
@@ -1148,20 +1045,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ManagedCallable *managed_callable = elem.key;
const Array &serialized_data = elem.value;
- MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
- MonoDelegate *delegate = nullptr;
-
- MonoException *exc = nullptr;
- bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TryDeserializeDelegate).invoke(managed_serialized_data, &delegate, &exc);
+ GCHandleIntPtr delegate = { nullptr };
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool success = GDMonoCache::managed_callbacks.DelegateUtils_TryDeserializeDelegateWithGCHandle(
+ &serialized_data, &delegate);
if (success) {
- ERR_CONTINUE(delegate == nullptr);
- managed_callable->set_delegate(delegate);
+ ERR_CONTINUE(delegate.value == nullptr);
+ managed_callable->delegate_handle = delegate;
} else if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print("Failed to deserialize delegate\n");
}
@@ -1180,60 +1071,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#endif
-void CSharpLanguage::lookup_script_for_class(GDMonoClass *p_class) {
- if (!p_class->has_attribute(CACHED_CLASS(ScriptPathAttribute))) {
- return;
- }
-
- MonoObject *attr = p_class->get_attribute(CACHED_CLASS(ScriptPathAttribute));
- String path = CACHED_FIELD(ScriptPathAttribute, path)->get_string_value(attr);
-
- dotnet_script_lookup_map[path] = DotNetScriptLookupInfo(
- p_class->get_namespace(), p_class->get_name(), p_class);
-}
-
-void CSharpLanguage::lookup_scripts_in_assembly(GDMonoAssembly *p_assembly) {
- if (p_assembly->has_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute))) {
- MonoObject *attr = p_assembly->get_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute));
- bool requires_lookup = CACHED_FIELD(AssemblyHasScriptsAttribute, requiresLookup)->get_bool_value(attr);
-
- if (requires_lookup) {
- // This is supported for scenarios where specifying all types would be cumbersome,
- // such as when disabling C# source generators (for whatever reason) or when using a
- // language other than C# that has nothing similar to source generators to automate it.
- MonoImage *image = p_assembly->get_image();
-
- int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
-
- for (int i = 1; i < rows; i++) {
- // We don't search inner classes, only top-level.
- MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
-
- if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) {
- continue;
- }
-
- GDMonoClass *current = p_assembly->get_class(mono_class);
- if (current) {
- lookup_script_for_class(current);
- }
- }
- } else {
- // This is the most likely scenario as we use C# source generators
- MonoArray *script_types = (MonoArray *)CACHED_FIELD(AssemblyHasScriptsAttribute, scriptTypes)->get_value(attr);
-
- int length = mono_array_length(script_types);
-
- for (int i = 0; i < length; i++) {
- MonoReflectionType *reftype = mono_array_get(script_types, MonoReflectionType *, i);
- ManagedType type = ManagedType::from_reftype(reftype);
- ERR_CONTINUE(!type.type_class);
- lookup_script_for_class(type.type_class);
- }
- }
- }
-}
-
void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("cs");
}
@@ -1248,22 +1085,6 @@ bool CSharpLanguage::overrides_external_editor() {
}
#endif
-void CSharpLanguage::thread_enter() {
-#if 0
- if (gdmono->is_runtime_initialized()) {
- GDMonoUtils::attach_current_thread();
- }
-#endif
-}
-
-void CSharpLanguage::thread_exit() {
-#if 0
- if (gdmono->is_runtime_initialized()) {
- GDMonoUtils::detach_current_thread();
- }
-#endif
-}
-
bool CSharpLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {
// Not a parser error in our case, but it's still used for other type of errors
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
@@ -1289,49 +1110,35 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
}
}
-void CSharpLanguage::_on_scripts_domain_unloaded() {
- for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
- CSharpScriptBinding &script_binding = E.value;
- script_binding.gchandle.release();
- script_binding.inited = false;
- }
-
+void CSharpLanguage::_on_scripts_domain_about_to_unload() {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(ManagedCallable::instances_mutex);
for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
ManagedCallable *managed_callable = elem->self();
- managed_callable->delegate_handle.release();
- managed_callable->delegate_invoke = nullptr;
+ managed_callable->release_delegate_handle();
}
}
#endif
-
- dotnet_script_lookup_map.clear();
}
#ifdef TOOLS_ENABLED
void CSharpLanguage::_editor_init_callback() {
- register_editor_internal_calls();
-
- // Initialize GodotSharpEditor
-
- GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor");
- CRASH_COND(editor_klass == nullptr);
+ // Load GodotTools and initialize GodotSharpEditor
- MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr());
- CRASH_COND(mono_object == nullptr);
+ int32_t interop_funcs_size = 0;
+ const void **interop_funcs = godotsharp::get_editor_interop_funcs(interop_funcs_size);
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
- UNHANDLED_EXCEPTION(exc);
+ Object *editor_plugin_obj = GDMono::get_singleton()->get_plugin_callbacks().LoadToolsAssemblyCallback(
+ GodotSharpDirs::get_data_editor_tools_dir().path_join("GodotTools.dll").utf16(),
+ interop_funcs, interop_funcs_size);
+ CRASH_COND(editor_plugin_obj == nullptr);
- EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(
- GDMonoMarshal::mono_object_to_variant(mono_object).operator Object *());
+ EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(editor_plugin_obj);
CRASH_COND(godotsharp_editor == nullptr);
- // Enable it as a plugin
+ // Add plugin to EditorNode and enable it
EditorNode::add_editor_plugin(godotsharp_editor);
ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B);
godotsharp_editor->enable_plugin();
@@ -1352,24 +1159,24 @@ void CSharpLanguage::release_script_gchandle(MonoGCHandleData &p_gchandle) {
}
}
-void CSharpLanguage::release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle) {
- uint32_t pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(p_expected_obj); // We might lock after this, so pin it
-
- if (!p_gchandle.is_released()) { // Do not lock unnecessarily
+void CSharpLanguage::release_script_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, MonoGCHandleData &r_gchandle) {
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
MutexLock lock(get_singleton()->script_gchandle_release_mutex);
-
- MonoObject *target = p_gchandle.get_target();
-
- // We release the gchandle if it points to the MonoObject* we expect (otherwise it was
- // already released and could have been replaced) or if we can't get its target MonoObject*
- // (which doesn't necessarily mean it was released, and we want it released in order to
- // avoid locking other threads unnecessarily).
- if (target == p_expected_obj || target == nullptr) {
- p_gchandle.release();
+ if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) {
+ r_gchandle.release();
}
}
+}
- GDMonoUtils::free_gchandle(pinned_gchandle);
+void CSharpLanguage::release_binding_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, CSharpScriptBinding &r_script_binding) {
+ MonoGCHandleData &gchandle = r_script_binding.gchandle;
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
+ MutexLock lock(get_singleton()->script_gchandle_release_mutex);
+ if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) {
+ gchandle.release();
+ r_script_binding.inited = false; // Here too, to be thread safe
+ }
+ }
}
CSharpLanguage::CSharpLanguage() {
@@ -1401,18 +1208,23 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
ERR_FAIL_NULL_V(classinfo, false);
type_name = classinfo->name;
- GDMonoClass *type_class = GDMonoUtils::type_get_proxy_class(type_name);
+ bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), type_name);
+ ERR_FAIL_COND_V_MSG(!parent_is_object_class, false,
+ "Type inherits from native type '" + type_name + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
- ERR_FAIL_NULL_V(type_class, false);
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!r_script_binding.gchandle.is_released());
+#endif
- MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(type_class, type_name, p_object);
+ GCHandleIntPtr strong_gchandle =
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(
+ &type_name, p_object);
- ERR_FAIL_NULL_V(mono_object, false);
+ ERR_FAIL_NULL_V(strong_gchandle.value, false);
r_script_binding.inited = true;
r_script_binding.type_name = type_name;
- r_script_binding.wrapper_class = type_class; // cache
- r_script_binding.gchandle = MonoGCHandleData::new_strong_handle(mono_object);
+ r_script_binding.gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
r_script_binding.owner = p_object;
// Tie managed to unmanaged
@@ -1455,7 +1267,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
if (GDMono::get_singleton() == nullptr) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!csharp_lang->script_bindings.is_empty());
+ CRASH_COND(csharp_lang && !csharp_lang->script_bindings.is_empty());
#endif
// Mono runtime finalized, all the gchandle bindings were already released
return;
@@ -1465,8 +1277,6 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
}
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
{
MutexLock lock(csharp_lang->language_bind_mutex);
@@ -1477,18 +1287,18 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
if (script_binding.inited) {
// Set the native instance field to IntPtr.Zero, if not yet garbage collected.
// This is done to avoid trying to dispose the native instance from Dispose(bool).
- MonoObject *mono_object = script_binding.gchandle.get_target();
- if (mono_object) {
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, nullptr);
- }
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_SetGodotObjectPtr(
+ script_binding.gchandle.get_intptr(), nullptr);
+
script_binding.gchandle.release();
+ script_binding.inited = false;
}
csharp_lang->script_bindings.erase(data);
}
}
-GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
+GDExtensionBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDExtensionBool p_reference) {
CRASH_COND(!p_binding);
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
@@ -1501,7 +1311,7 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token,
MonoGCHandleData &gchandle = script_binding.gchandle;
- int refcount = rc_owner->reference_get_count();
+ int refcount = rc_owner->get_reference_count();
if (!script_binding.inited) {
return refcount == 0;
@@ -1510,41 +1320,49 @@ GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token,
if (p_reference) {
// Refcount incremented
if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
- MonoObject *target = gchandle.get_target();
- if (!target) {
+ // Release the current weak handle and replace it with a strong handle.
+
+ GCHandleIntPtr old_gchandle = gchandle.get_intptr();
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = false;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ if (!target_alive) {
return false; // Called after the managed side was collected, so nothing to do here
}
- // Release the current weak handle and replace it with a strong handle.
- MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
- gchandle.release();
- gchandle = strong_gchandle;
+ gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
}
return false;
} else {
// Refcount decremented
if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
- MonoObject *target = gchandle.get_target();
- if (!target) {
+ // Release the current strong handle and replace it with a weak handle.
+
+ GCHandleIntPtr old_gchandle = gchandle.get_intptr();
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = true;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ if (!target_alive) {
return refcount == 0; // Called after the managed side was collected, so nothing to do here
}
- // Release the current strong handle and replace it with a weak handle.
- MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
- gchandle.release();
- gchandle = weak_gchandle;
+ gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE);
return false;
}
@@ -1589,250 +1407,189 @@ void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) {
bool CSharpLanguage::has_instance_binding(Object *p_object) {
return p_object->has_instance_binding(get_singleton());
}
+void CSharpLanguage::tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) {
+ // This method should not fail
-CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
- CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
+ CRASH_COND(!p_unmanaged);
- RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
+ // All mono objects created from the managed world (e.g.: 'new Player()')
+ // need to have a CSharpScript in order for their methods to be callable from the unmanaged side
- instance->base_ref_counted = rc != nullptr;
- instance->owner = p_owner;
- instance->gchandle = p_gchandle;
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
- if (instance->base_ref_counted) {
- instance->_reference_owner_unsafe();
- }
-
- p_script->instances.insert(p_owner);
+ CRASH_COND(p_ref_counted != (bool)rc);
- return instance;
-}
+ MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr,
+ p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE);
-MonoObject *CSharpInstance::get_mono_object() const {
- ERR_FAIL_COND_V(gchandle.is_released(), nullptr);
- return gchandle.get_target();
-}
+ // If it's just a wrapper Godot class and not a custom inheriting class, then attach a
+ // script binding instead. One of the advantages of this is that if a script is attached
+ // later and it's not a C# script, then the managed object won't have to be disposed.
+ // Another reason for doing this is that this instance could outlive CSharpLanguage, which would
+ // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
-Object *CSharpInstance::get_owner() {
- return owner;
-}
-
-bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
- ERR_FAIL_COND_V(!script.is_valid(), false);
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL_V(mono_object, false);
-
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoField *field = top->get_field(p_name);
+ CSharpScriptBinding script_binding;
- if (field) {
- field->set_value_from_variant(mono_object, p_value);
- return true;
- }
+ script_binding.inited = true;
+ script_binding.type_name = *p_native_name;
+ script_binding.gchandle = gchandle;
+ script_binding.owner = p_unmanaged;
- GDMonoProperty *property = top->get_property(p_name);
+ if (p_ref_counted) {
+ // Unsafe refcount increment. The managed instance also counts as a reference.
+ // This way if the unmanaged world has no references to our owner
+ // but the managed instance is alive, the refcount will be 1 instead of 0.
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
- if (property) {
- property->set_value_from_variant(mono_object, p_value);
- return true;
+ // May not me referenced yet, so we must use init_ref() instead of reference()
+ if (rc->init_ref()) {
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
-
- top = top->get_parent_class();
}
- // Call _set
+ // The object was just created, no script instance binding should have been attached
+ CRASH_COND(CSharpLanguage::has_instance_binding(p_unmanaged));
- top = script->script_class;
+ void *data;
+ {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
+ data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(p_unmanaged, script_binding);
+ }
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_set), 2);
+ // Should be thread safe because the object was just created and nothing else should be referencing it
+ CSharpLanguage::set_instance_binding(p_unmanaged, data);
+}
- if (method) {
- Variant name = p_name;
- const Variant *args[2] = { &name, &p_value };
+void CSharpLanguage::tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted) {
+ // This method should not fail
- MonoObject *ret = method->invoke(mono_object, args);
+ Ref<CSharpScript> script = *p_script;
+ // We take care of destructing this reference here, so the managed code won't need to do another P/Invoke call
+ p_script->~Ref();
- if (ret && GDMonoMarshal::unbox<MonoBoolean>(ret)) {
- return true;
- }
+ CRASH_COND(!p_unmanaged);
- break;
- }
+ // All mono objects created from the managed world (e.g.: 'new Player()')
+ // need to have a CSharpScript in order for their methods to be callable from the unmanaged side
- top = top->get_parent_class();
- }
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
- return false;
-}
+ CRASH_COND(p_ref_counted != (bool)rc);
-bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
- ERR_FAIL_COND_V(!script.is_valid(), false);
+ MonoGCHandleData gchandle = MonoGCHandleData(p_gchandle_intptr,
+ p_ref_counted ? gdmono::GCHandleType::WEAK_HANDLE : gdmono::GCHandleType::STRONG_HANDLE);
- GD_MONO_SCOPE_THREAD_ATTACH;
+ CRASH_COND(script.is_null());
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL_V(mono_object, false);
+ CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(p_unmanaged, script.ptr(), gchandle);
- GDMonoClass *top = script->script_class;
+ p_unmanaged->set_script_and_instance(script, csharp_instance);
- while (top && top != script->native) {
- GDMonoField *field = top->get_field(p_name);
+ csharp_instance->connect_event_signals();
+}
- if (field) {
- MonoObject *value = field->get_value(mono_object);
- r_ret = GDMonoMarshal::mono_object_to_variant(value);
- return true;
- }
+void CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
+ // This method should not fail
- GDMonoProperty *property = top->get_property(p_name);
+ CRASH_COND(!p_unmanaged);
- if (property) {
- MonoException *exc = nullptr;
- MonoObject *value = property->get_value(mono_object, &exc);
- if (exc) {
- r_ret = Variant();
- GDMonoUtils::set_pending_exception(exc);
- } else {
- r_ret = GDMonoMarshal::mono_object_to_variant(value);
- }
- return true;
- }
+ CSharpInstance *instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
- top = top->get_parent_class();
+ if (!instance) {
+ // Native bindings don't need post-setup
+ return;
}
- // Call _get
-
- top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get), 1);
+ CRASH_COND(!instance->gchandle.is_released());
- if (method) {
- Variant name = p_name;
- const Variant *args[1] = { &name };
-
- MonoObject *ret = method->invoke(mono_object, args);
-
- if (ret) {
- r_ret = GDMonoMarshal::mono_object_to_variant(ret);
- return true;
- }
+ // Tie managed to unmanaged
+ instance->gchandle = MonoGCHandleData(p_gchandle_intptr, gdmono::GCHandleType::STRONG_HANDLE);
- break;
- }
+ if (instance->base_ref_counted) {
+ instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ }
- top = top->get_parent_class();
+ {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_script_instances_mutex());
+ // instances is a set, so it's safe to insert multiple times (e.g.: from _internal_new_managed)
+ instance->script->instances.insert(instance->owner);
}
- return false;
+ instance->connect_event_signals();
}
-void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state) {
- List<PropertyInfo> property_list;
- get_property_list(&property_list);
-
- for (const PropertyInfo &prop_info : property_list) {
- Pair<StringName, Variant> state_pair;
- state_pair.first = prop_info.name;
+CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
+ CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
- ManagedType managedType;
+ RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
- GDMonoField *field = nullptr;
- GDMonoClass *top = script->script_class;
- while (top && top != script->native) {
- field = top->get_field(state_pair.first);
- if (field) {
- break;
- }
+ instance->base_ref_counted = rc != nullptr;
+ instance->owner = p_owner;
+ instance->gchandle = p_gchandle;
- top = top->get_parent_class();
- }
- if (!field) {
- continue; // Properties ignored. We get the property baking fields instead.
- }
+ if (instance->base_ref_counted) {
+ instance->_reference_owner_unsafe();
+ }
- managedType = field->get_type();
+ p_script->instances.insert(p_owner);
- if (GDMonoMarshal::managed_to_variant_type(managedType) != Variant::NIL) { // If we can marshal it
- if (get(state_pair.first, state_pair.second)) {
- r_state.push_back(state_pair);
- }
- }
- }
+ return instance;
}
-void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state) {
- MonoObject *owner_managed = get_mono_object();
- ERR_FAIL_NULL(owner_managed);
+Object *CSharpInstance::get_owner() {
+ return owner;
+}
- for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
- const CSharpScript::EventSignal &event_signal = E.value;
+bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
- if (!delegate_field_value) {
- continue; // Empty
- }
+ return GDMonoCache::managed_callbacks.CSharpInstanceBridge_Set(
+ gchandle.get_intptr(), &p_name, &p_value);
+}
- Array serialized_data;
- MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
+bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- MonoException *exc = nullptr;
- bool success = (bool)CACHED_METHOD_THUNK(DelegateUtils, TrySerializeDelegate).invoke(delegate_field_value, managed_serialized_data, &exc);
+ Variant ret_value;
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- continue;
- }
+ bool ret = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Get(
+ gchandle.get_intptr(), &p_name, &ret_value);
- if (success) {
- r_state.push_back(Pair<StringName, Array>(event_signal.field->get_name(), serialized_data));
- } else if (OS::get_singleton()->is_stdout_verbose()) {
- OS::get_singleton()->print("Failed to serialize event signal delegate\n");
- }
+ if (ret) {
+ r_ret = ret_value;
+ return true;
}
+
+ return false;
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
List<PropertyInfo> props;
- for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
- props.push_front(E.value);
- }
+ script->get_script_property_list(&props);
// Call _get_property_list
ERR_FAIL_COND(!script.is_valid());
- GD_MONO_SCOPE_THREAD_ATTACH;
+ StringName method = SNAME("_get_property_list");
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL(mono_object);
+ Variant ret;
+ Callable::CallError call_error;
+ bool ok = GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &method, nullptr, 0, &call_error, &ret);
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(CACHED_STRING_NAME(_get_property_list), 0);
-
- if (method) {
- MonoObject *ret = method->invoke(mono_object);
-
- if (ret) {
- Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
- for (int i = 0, size = array.size(); i < size; i++) {
- props.push_back(PropertyInfo::from_dict(array.get(i)));
- }
+ // CALL_ERROR_INVALID_METHOD would simply mean it was not overridden
+ if (call_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling '_get_property_list': " + Variant::get_call_error_text(method, nullptr, 0, call_error));
+ } else if (!ok) {
+ ERR_PRINT("Unexpected error calling '_get_property_list'");
+ } else {
+ Array array = ret;
+ for (int i = 0, size = array.size(); i < size; i++) {
+ p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
}
-
- break;
}
-
- top = top->get_parent_class();
}
for (const PropertyInfo &prop : props) {
@@ -1855,84 +1612,79 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
return Variant::NIL;
}
-void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
- if (!script->is_valid() || !script->script_class) {
- return;
- }
+bool CSharpInstance::property_can_revert(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- GD_MONO_SCOPE_THREAD_ATTACH;
+ Variant name_arg = p_name;
+ const Variant *args[1] = { &name_arg };
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script->script_class;
+ Variant ret;
+ Callable::CallError call_error;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &CACHED_STRING_NAME(_property_can_revert), args, 1, &call_error, &ret);
- while (top && top != script->native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(minfo);
- }
- }
-
- top = top->get_parent_class();
- }
-}
-
-bool CSharpInstance::has_method(const StringName &p_method) const {
- if (!script.is_valid()) {
+ if (call_error.error != Callable::CallError::CALL_OK) {
return false;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
+ return (bool)ret;
+}
- GDMonoClass *top = script->script_class;
+bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
+ ERR_FAIL_COND_V(!script.is_valid(), false);
- while (top && top != script->native) {
- if (top->has_fetched_method_unknown_params(p_method)) {
- return true;
- }
+ Variant name_arg = p_name;
+ const Variant *args[1] = { &name_arg };
+
+ Variant ret;
+ Callable::CallError call_error;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &CACHED_STRING_NAME(_property_get_revert), args, 1, &call_error, &ret);
- top = top->get_parent_class();
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ return false;
}
- return false;
+ r_ret = ret;
+ return true;
}
-Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- ERR_FAIL_COND_V(!script.is_valid(), Variant());
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- MonoObject *mono_object = get_mono_object();
-
- if (!mono_object) {
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- ERR_FAIL_V(Variant());
+void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
+ if (!script->is_valid() || !script->valid) {
+ return;
}
- GDMonoClass *top = script->script_class;
+ const CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (const CSharpScript::CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
+ }
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
+ top = top->base_script.ptr();
+ }
+}
- if (method) {
- MonoObject *return_value = method->invoke(mono_object, p_args);
+bool CSharpInstance::has_method(const StringName &p_method) const {
+ if (!script.is_valid()) {
+ return false;
+ }
- r_error.error = Callable::CallError::CALL_OK;
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
+ }
- if (return_value) {
- return GDMonoMarshal::mono_object_to_variant(return_value);
- } else {
- return Variant();
- }
- }
+ return GDMonoCache::managed_callbacks.CSharpInstanceBridge_HasMethodUnknownParams(
+ gchandle.get_intptr(), &p_method);
+}
- top = top->get_parent_class();
- }
+Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ ERR_FAIL_COND_V(!script.is_valid(), Variant());
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ Variant ret;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &p_method, p_args, p_argcount, &r_error, &ret);
- return Variant();
+ return ret;
}
bool CSharpInstance::_reference_owner_unsafe() {
@@ -1978,48 +1730,29 @@ bool CSharpInstance::_unreference_owner_unsafe() {
return static_cast<RefCounted *>(owner)->unreference();
}
-MonoObject *CSharpInstance::_internal_new_managed() {
- // Search the constructor first, to fail with an error if it's not found before allocating anything else.
- GDMonoMethod *ctor = script->script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
- ERR_FAIL_NULL_V_MSG(ctor, nullptr,
- "Cannot create script instance because the class does not define a parameterless constructor: '" + script->get_path() + "'.");
-
+bool CSharpInstance::_internal_new_managed() {
CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
- ERR_FAIL_NULL_V(owner, nullptr);
- ERR_FAIL_COND_V(script.is_null(), nullptr);
+ ERR_FAIL_NULL_V(owner, false);
+ ERR_FAIL_COND_V(script.is_null(), false);
- MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr());
+ bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance(
+ script.ptr(), owner, nullptr, 0);
- if (!mono_object) {
+ if (!ok) {
// Important to clear this before destroying the script instance here
script = Ref<CSharpScript>();
-
- bool die = _unreference_owner_unsafe();
- // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die);
-
owner = nullptr;
- ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object.");
- }
-
- // Tie managed to unmanaged
- gchandle = MonoGCHandleData::new_strong_handle(mono_object);
-
- if (base_ref_counted) {
- _reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ return false;
}
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, owner);
-
- // Construct
- ctor->invoke_raw(mono_object, nullptr);
+ CRASH_COND(gchandle.is_released());
- return mono_object;
+ return true;
}
-void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+void CSharpInstance::mono_object_disposed(GCHandleIntPtr p_gchandle_to_free) {
// Must make sure event signals are not left dangling
disconnect_event_signals();
@@ -2027,10 +1760,10 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
CRASH_COND(base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
- CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
+ CSharpLanguage::get_singleton()->release_script_gchandle_thread_safe(p_gchandle_to_free, gchandle);
}
-void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
+void CSharpInstance::mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
#ifdef DEBUG_ENABLED
CRASH_COND(!base_ref_counted);
CRASH_COND(gchandle.is_released());
@@ -2046,20 +1779,20 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
r_delete_owner = true;
} else {
r_delete_owner = false;
- CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
+ CSharpLanguage::get_singleton()->release_script_gchandle_thread_safe(p_gchandle_to_free, gchandle);
if (!p_is_finalizer) {
// If the native instance is still alive and Dispose() was called
// (instead of the finalizer), then we remove the script instance.
r_remove_script_instance = true;
+ // TODO: Last usage of 'is_finalizing_scripts_domain'. It should be replaced with a check to determine if the load context is being unloaded.
} else if (!GDMono::get_singleton()->is_finalizing_scripts_domain()) {
// If the native instance is still alive and this is called from the finalizer,
// then it was referenced from another thread before the finalizer could
// unreference and delete it, so we want to keep it.
// GC.ReRegisterForFinalize(this) is not safe because the objects referenced by 'this'
// could have already been collected. Instead we will create a new managed instance here.
- MonoObject *new_managed = _internal_new_managed();
- if (!new_managed) {
+ if (!_internal_new_managed()) {
r_remove_script_instance = true;
}
}
@@ -2067,13 +1800,12 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
}
void CSharpInstance::connect_event_signals() {
- for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
- const CSharpScript::EventSignal &event_signal = E.value;
-
- StringName signal_name = event_signal.field->get_name();
+ // The script signals list includes the signals declared in base scripts.
+ for (CSharpScript::EventSignalInfo &signal : script->get_script_event_signals()) {
+ String signal_name = signal.name;
// TODO: Use pooling for ManagedCallable instances.
- EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+ EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, signal_name));
Callable callable(event_signal_callable);
connected_event_signals.push_back(callable);
@@ -2098,17 +1830,26 @@ void CSharpInstance::refcount_incremented() {
RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
+ if (rc_owner->get_reference_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
// The reference count was increased after the managed side was the only one referencing our owner.
// This means the owner is being referenced again by the unmanaged side,
// so the owner must hold the managed side alive again to avoid it from being GCed.
// Release the current weak handle and replace it with a strong handle.
- MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(gchandle.get_target());
- gchandle.release();
- gchandle = strong_gchandle;
+
+ GCHandleIntPtr old_gchandle = gchandle.get_intptr();
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = false;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ if (!target_alive) {
+ return; // Called after the managed side was collected, so nothing to do here
+ }
+
+ gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
}
}
@@ -2120,18 +1861,27 @@ bool CSharpInstance::refcount_decremented() {
RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- int refcount = rc_owner->reference_get_count();
+ int refcount = rc_owner->get_reference_count();
if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
-
// If owner owner is no longer referenced by the unmanaged side,
// the managed instance takes responsibility of deleting the owner when GCed.
// Release the current strong handle and replace it with a weak handle.
- MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(gchandle.get_target());
- gchandle.release();
- gchandle = weak_gchandle;
+
+ GCHandleIntPtr old_gchandle = gchandle.get_intptr();
+ gchandle.handle = { nullptr }; // No longer owns the handle (released by swap function)
+
+ GCHandleIntPtr new_gchandle = { nullptr };
+ bool create_weak = true;
+ bool target_alive = GDMonoCache::managed_callbacks.ScriptManagerBridge_SwapGCHandleForType(
+ old_gchandle, &new_gchandle, create_weak);
+
+ if (!target_alive) {
+ return refcount == 0; // Called after the managed side was collected, so nothing to do here
+ }
+
+ gchandle = MonoGCHandleData(new_gchandle, gdmono::GCHandleType::WEAK_HANDLE);
return false;
}
@@ -2146,8 +1896,6 @@ const Variant CSharpInstance::get_rpc_config() const {
}
void CSharpInstance::notification(int p_notification) {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
if (p_notification == Object::NOTIFICATION_PREDELETE) {
// When NOTIFICATION_PREDELETE is sent, we also take the chance to call Dispose().
// It's safe to call Dispose() multiple times and NOTIFICATION_PREDELETE is guaranteed
@@ -2167,15 +1915,8 @@ void CSharpInstance::notification(int p_notification) {
_call_notification(p_notification);
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL(mono_object);
-
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ gchandle.get_intptr(), /* okIfNull */ false);
return;
}
@@ -2184,62 +1925,29 @@ void CSharpInstance::notification(int p_notification) {
}
void CSharpInstance::_call_notification(int p_notification) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- MonoObject *mono_object = get_mono_object();
- ERR_FAIL_NULL(mono_object);
-
- // Custom version of _call_multilevel, optimized for _notification
+ Variant arg = p_notification;
+ const Variant *args[1] = { &arg };
+ StringName method_name = SNAME("_notification");
- int32_t arg = p_notification;
- void *args[1] = { &arg };
- StringName method_name = CACHED_STRING_NAME(_notification);
+ Callable::CallError call_error;
- GDMonoClass *top = script->script_class;
-
- while (top && top != script->native) {
- GDMonoMethod *method = top->get_method(method_name, 1);
-
- if (method) {
- method->invoke_raw(mono_object, args);
- return;
- }
-
- top = top->get_parent_class();
- }
+ Variant ret;
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+ gchandle.get_intptr(), &method_name, args, 1, &call_error, &ret);
}
String CSharpInstance::to_string(bool *r_valid) {
- GD_MONO_SCOPE_THREAD_ATTACH;
+ String res;
+ bool valid;
- MonoObject *mono_object = get_mono_object();
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallToString(
+ gchandle.get_intptr(), &res, &valid);
- if (mono_object == nullptr) {
- if (r_valid) {
- *r_valid = false;
- }
- return String();
- }
-
- MonoException *exc = nullptr;
- MonoString *result = GDMonoUtils::object_to_string(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- if (r_valid) {
- *r_valid = false;
- }
- return String();
- }
-
- if (result == nullptr) {
- if (r_valid) {
- *r_valid = false;
- }
- return String();
+ if (r_valid) {
+ *r_valid = valid;
}
- return GDMonoMarshal::mono_string_to_godot(result);
+ return res;
}
Ref<Script> CSharpInstance::get_script() const {
@@ -2255,8 +1963,6 @@ CSharpInstance::CSharpInstance(const Ref<CSharpScript> &p_script) :
}
CSharpInstance::~CSharpInstance() {
- GD_MONO_SCOPE_THREAD_ATTACH;
-
destructing_script_instance = true;
// Must make sure event signals are not left dangling
@@ -2270,16 +1976,8 @@ CSharpInstance::~CSharpInstance() {
// we must call Dispose here, because Dispose calls owner->set_script_instance(nullptr)
// and that would mess up with the new script instance if called later.
- MonoObject *mono_object = gchandle.get_target();
-
- if (mono_object) {
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ gchandle.get_intptr(), /* okIfNull */ true);
}
gchandle.release(); // Make sure the gchandle is released
@@ -2309,7 +2007,7 @@ CSharpInstance::~CSharpInstance() {
#ifdef DEBUG_ENABLED
// The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope
- CRASH_COND(rc_owner->reference_get_count() <= 1);
+ CRASH_COND(rc_owner->get_reference_count() <= 1);
#endif
}
@@ -2335,10 +2033,6 @@ void CSharpScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#ifdef TOOLS_ENABLED
void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
- if (base_cache.is_valid()) {
- base_cache->_update_exports_values(values, propnames);
- }
-
for (const KeyValue<StringName, Variant> &E : exported_members_defval_cache) {
values[E.key] = E.value;
}
@@ -2346,52 +2040,55 @@ void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values,
for (const PropertyInfo &prop_info : exported_members_cache) {
propnames.push_back(prop_info);
}
-}
-void CSharpScript::_update_member_info_no_exports() {
- if (exports_invalidated) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- exports_invalidated = false;
+ if (base_script.is_valid()) {
+ base_script->_update_exports_values(values, propnames);
+ }
+}
+#endif
- member_info.clear();
+void GD_CLR_STDCALL CSharpScript::_add_property_info_list_callback(CSharpScript *p_script, const String *p_current_class_name, void *p_props, int32_t p_count) {
+ GDMonoCache::godotsharp_property_info *props = (GDMonoCache::godotsharp_property_info *)p_props;
- GDMonoClass *top = script_class;
+#ifdef TOOLS_ENABLED
+ p_script->exported_members_cache.push_back(PropertyInfo(
+ Variant::NIL, *p_current_class_name, PROPERTY_HINT_NONE,
+ p_script->get_path(), PROPERTY_USAGE_CATEGORY));
+#endif
- while (top && top != native) {
- PropertyInfo prop_info;
- bool exported;
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_info &prop = props[i];
- const Vector<GDMonoField *> &fields = top->get_all_fields();
+ StringName name = *reinterpret_cast<const StringName *>(&prop.name);
+ String hint_string = *reinterpret_cast<const String *>(&prop.hint_string);
- for (int i = fields.size() - 1; i >= 0; i--) {
- GDMonoField *field = fields[i];
+ PropertyInfo pinfo(prop.type, name, prop.hint, hint_string, prop.usage);
- if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = field->get_name();
+ p_script->member_info[name] = pinfo;
- member_info[member_name] = prop_info;
- exported_members_cache.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
+ if (prop.exported) {
+#ifdef TOOLS_ENABLED
+ p_script->exported_members_cache.push_back(pinfo);
+#endif
- const Vector<GDMonoProperty *> &properties = top->get_all_properties();
+#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
+ p_script->exported_members_names.insert(name);
+#endif
+ }
+ }
+}
- for (int i = properties.size() - 1; i >= 0; i--) {
- GDMonoProperty *property = properties[i];
+#ifdef TOOLS_ENABLED
+void GD_CLR_STDCALL CSharpScript::_add_property_default_values_callback(CSharpScript *p_script, void *p_def_vals, int32_t p_count) {
+ GDMonoCache::godotsharp_property_def_val_pair *def_vals = (GDMonoCache::godotsharp_property_def_val_pair *)p_def_vals;
- if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) {
- StringName member_name = property->get_name();
+ for (int i = 0; i < p_count; i++) {
+ const GDMonoCache::godotsharp_property_def_val_pair &def_val_pair = def_vals[i];
- member_info[member_name] = prop_info;
- exported_members_cache.push_front(prop_info);
- exported_members_defval_cache[member_name] = Variant();
- }
- }
+ StringName name = *reinterpret_cast<const StringName *>(&def_val_pair.name);
+ Variant value = *reinterpret_cast<const Variant *>(&def_val_pair.value);
- top = top->get_parent_class();
- }
+ p_script->exported_members_defval_cache[name] = value;
}
}
#endif
@@ -2413,155 +2110,26 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
if (exports_invalidated)
#endif
{
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- changed = true;
-
- member_info.clear();
-
#ifdef TOOLS_ENABLED
- MonoObject *tmp_object = nullptr;
- Object *tmp_native = nullptr;
- uint32_t tmp_pinned_gchandle = 0;
-
- if (is_editor) {
- exports_invalidated = false;
-
- exported_members_cache.clear();
- exported_members_defval_cache.clear();
-
- // Here we create a temporary managed instance of the class to get the initial values
- tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
-
- if (!tmp_object) {
- ERR_PRINT("Failed to allocate temporary MonoObject.");
- return false;
- }
-
- tmp_pinned_gchandle = GDMonoUtils::new_strong_gchandle_pinned(tmp_object); // pin it (not sure if needed)
-
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
-
- ERR_FAIL_NULL_V_MSG(ctor, false,
- "Cannot construct temporary MonoObject because the class does not define a parameterless constructor: '" + get_path() + "'.");
-
- MonoException *ctor_exc = nullptr;
- ctor->invoke(tmp_object, nullptr, &ctor_exc);
-
- tmp_native = GDMonoMarshal::unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(tmp_object));
-
- if (ctor_exc) {
- // TODO: Should we free 'tmp_native' if the exception was thrown after its creation?
-
- GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
- tmp_object = nullptr;
-
- ERR_PRINT("Exception thrown from constructor of temporary MonoObject:");
- GDMonoUtils::debug_print_unhandled_exception(ctor_exc);
- return false;
- }
- }
+ exports_invalidated = false;
#endif
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- PropertyInfo prop_info;
- bool exported;
-
- const Vector<GDMonoField *> &fields = top->get_all_fields();
-
- for (int i = fields.size() - 1; i >= 0; i--) {
- GDMonoField *field = fields[i];
-
- if (_get_member_export(field, /* inspect export: */ true, prop_info, exported)) {
- StringName member_name = field->get_name();
+ changed = true;
- member_info[member_name] = prop_info;
+ member_info.clear();
- if (exported) {
#ifdef TOOLS_ENABLED
- if (is_editor) {
- exported_members_cache.push_front(prop_info);
-
- if (tmp_object) {
- exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
- }
- }
+ exported_members_cache.clear();
+ exported_members_defval_cache.clear();
#endif
-#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- exported_members_names.insert(member_name);
-#endif
- }
- }
- }
-
- const Vector<GDMonoProperty *> &properties = top->get_all_properties();
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyInfoList(this, &_add_property_info_list_callback);
- for (int i = properties.size() - 1; i >= 0; i--) {
- GDMonoProperty *property = properties[i];
-
- if (_get_member_export(property, /* inspect export: */ true, prop_info, exported)) {
- StringName member_name = property->get_name();
-
- member_info[member_name] = prop_info;
-
- if (exported) {
#ifdef TOOLS_ENABLED
- if (is_editor) {
- exported_members_cache.push_front(prop_info);
- if (tmp_object) {
- MonoException *exc = nullptr;
- MonoObject *ret = property->get_value(tmp_object, &exc);
- if (exc) {
- exported_members_defval_cache[member_name] = Variant();
- GDMonoUtils::debug_print_unhandled_exception(exc);
- } else {
- exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(ret);
- }
- }
- }
-#endif
-
-#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- exported_members_names.insert(member_name);
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetPropertyDefaultValues(this, &_add_property_default_values_callback);
#endif
- }
- }
- }
-
- top = top->get_parent_class();
- }
-
-#ifdef TOOLS_ENABLED
- if (is_editor) {
- // Need to check this here, before disposal
- bool base_ref_counted = Object::cast_to<RefCounted>(tmp_native) != nullptr;
-
- // Dispose the temporary managed instance
-
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(tmp_object, &exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown from method Dispose() of temporary MonoObject:");
- GDMonoUtils::debug_print_unhandled_exception(exc);
- }
-
- GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
- tmp_object = nullptr;
-
- if (tmp_native && !base_ref_counted) {
- Node *node = Object::cast_to<Node>(tmp_native);
- if (node && node->is_inside_tree()) {
- ERR_PRINT("Temporary instance was added to the scene tree.");
- } else {
- memdelete(tmp_native);
- }
- }
}
-#endif
}
#ifdef TOOLS_ENABLED
@@ -2588,374 +2156,6 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
return changed;
}
-void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class) {
- // no need to load the script's signals more than once
- if (!signals_invalidated) {
- return;
- }
-
- // make sure this classes signals are empty when loading for the first time
- _signals.clear();
- event_signals.clear();
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = p_class;
- while (top && top != p_native_class) {
- const Vector<GDMonoClass *> &delegates = top->get_all_delegates();
- for (int i = delegates.size() - 1; i >= 0; --i) {
- GDMonoClass *delegate = delegates[i];
-
- if (!delegate->has_attribute(CACHED_CLASS(SignalAttribute))) {
- continue;
- }
-
- // Arguments are accessibles as arguments of .Invoke method
- GDMonoMethod *invoke_method = delegate->get_method(mono_get_delegate_invoke(delegate->get_mono_ptr()));
-
- Vector<SignalParameter> parameters;
- if (_get_signal(top, invoke_method, parameters)) {
- _signals[delegate->get_name()] = parameters;
- }
- }
-
- List<StringName> found_event_signals;
-
- void *iter = nullptr;
- MonoEvent *raw_event = nullptr;
- while ((raw_event = mono_class_get_events(top->get_mono_ptr(), &iter)) != nullptr) {
- MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
- if (event_attrs) {
- if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
- String event_name = String::utf8(mono_event_get_name(raw_event));
- found_event_signals.push_back(StringName(event_name));
- }
-
- mono_custom_attrs_free(event_attrs);
- }
- }
-
- const Vector<GDMonoField *> &fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- GDMonoField *field = fields[i];
-
- GDMonoClass *field_class = field->get_type().type_class;
-
- if (!mono_class_is_delegate(field_class->get_mono_ptr())) {
- continue;
- }
-
- if (!found_event_signals.find(field->get_name())) {
- continue;
- }
-
- GDMonoMethod *invoke_method = field_class->get_method(mono_get_delegate_invoke(field_class->get_mono_ptr()));
-
- Vector<SignalParameter> parameters;
- if (_get_signal(top, invoke_method, parameters)) {
- event_signals[field->get_name()] = { field, invoke_method, parameters };
- }
- }
-
- top = top->get_parent_class();
- }
-
- signals_invalidated = false;
-}
-
-bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- Vector<StringName> names;
- Vector<ManagedType> types;
- p_delegate_invoke->get_parameter_names(names);
- p_delegate_invoke->get_parameter_types(types);
-
- for (int i = 0; i < names.size(); ++i) {
- SignalParameter arg;
- arg.name = names[i];
-
- bool nil_is_variant = false;
- arg.type = GDMonoMarshal::managed_to_variant_type(types[i], &nil_is_variant);
-
- if (arg.type == Variant::NIL) {
- if (nil_is_variant) {
- arg.nil_is_variant = true;
- } else {
- ERR_PRINT("Unknown type of signal parameter: '" + arg.name + "' in '" + p_class->get_full_name() + "'.");
- return false;
- }
- }
-
- params.push_back(arg);
- }
-
- return true;
-}
-
-/**
- * Returns false if there was an error, otherwise true.
- * If there was an error, r_prop_info and r_exported are not assigned any value.
- */
-bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
- // Goddammit, C++. All I wanted was some nested functions.
-#define MEMBER_FULL_QUALIFIED_NAME(m_member) \
- (m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name())
-
- if (p_member->is_static()) {
-#ifdef TOOLS_ENABLED
- if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
- ERR_PRINT("Cannot export member because it is static: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
-
- if (member_info.has(p_member->get_name())) {
- return false;
- }
-
- ManagedType type;
-
- if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_FIELD) {
- type = static_cast<GDMonoField *>(p_member)->get_type();
- } else if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
- type = static_cast<GDMonoProperty *>(p_member)->get_type();
- } else {
- CRASH_NOW();
- }
-
- bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute));
-
- if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
- GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
- if (!property->has_getter()) {
-#ifdef TOOLS_ENABLED
- if (exported) {
- ERR_PRINT("Cannot export a property without a getter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
- if (!property->has_setter()) {
-#ifdef TOOLS_ENABLED
- if (exported) {
- ERR_PRINT("Cannot export a property without a setter: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
- }
-#endif
- return false;
- }
- }
-
- bool nil_is_variant = false;
- Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &nil_is_variant);
-
- if (!p_inspect_export || !exported) {
- r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
- r_exported = false;
- return true;
- }
-
-#ifdef TOOLS_ENABLED
- MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
-#endif
-
- PropertyHint hint = PROPERTY_HINT_NONE;
- String hint_string;
-
- if (variant_type == Variant::NIL && !nil_is_variant) {
-#ifdef TOOLS_ENABLED
- ERR_PRINT("Unknown exported member type: '" + MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
-#endif
- return false;
- }
-
-#ifdef TOOLS_ENABLED
- int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string);
-
- ERR_FAIL_COND_V_MSG(hint_res == -1, false,
- "Error while trying to determine information about the exported member: '" +
- MEMBER_FULL_QUALIFIED_NAME(p_member) + "'.");
-
- if (hint_res == 0) {
- hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr));
- hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
- }
-#endif
-
- uint32_t prop_usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE;
-
- if (variant_type == Variant::NIL) {
- // System.Object (Variant)
- prop_usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
-
- r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, prop_usage);
- r_exported = true;
-
- return true;
-
-#undef MEMBER_FULL_QUALIFIED_NAME
-}
-
-#ifdef TOOLS_ENABLED
-int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) {
- if (p_variant_type == Variant::NIL) {
- // System.Object (Variant)
- return 1;
- }
-
- 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())) {
- 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();
-
- MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr());
-
- String name_only_hint_string;
-
- // True: enum Foo { Bar, Baz, Quux }
- // True: enum Foo { Bar = 0, Baz = 1, Quux = 2 }
- // False: enum Foo { Bar = 0, Baz = 7, Quux = 5 }
- bool uses_default_values = true;
-
- for (int i = 0; i < fields.size(); i++) {
- MonoClassField *field = fields[i];
-
- if (i > 0) {
- r_hint_string += ",";
- name_only_hint_string += ",";
- }
-
- String enum_field_name = String::utf8(mono_field_get_name(field));
- r_hint_string += enum_field_name;
- name_only_hint_string += enum_field_name;
-
- // TODO:
- // Instead of using mono_field_get_value_object, we can do this without boxing. Check the
- // internal mono functions: ves_icall_System_Enum_GetEnumValuesAndNames and the get_enum_field.
-
- MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, nullptr);
-
- ERR_FAIL_NULL_V_MSG(val_obj, -1, "Failed to get '" + enum_field_name + "' constant enum value.");
-
- bool r_error;
- 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.");
-
- unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
- if (val != expected_val) {
- uses_default_values = false;
- }
-
- r_hint_string += ":";
- r_hint_string += String::num_uint64(val);
- }
-
- if (uses_default_values) {
- // If we use the format NAME:VAL, that's what the editor displays.
- // That's annoying if the user is not using custom values for the enum constants.
- // This may not be needed in the future if the editor is changed to not display values.
- r_hint_string = name_only_hint_string;
- }
- } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->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_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
-
- ManagedType elem_type;
-
- if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type)) {
- return 0;
- }
-
- Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type);
-
- PropertyHint elem_hint = PROPERTY_HINT_NONE;
- String elem_hint_string;
-
- ERR_FAIL_COND_V_MSG(elem_variant_type == Variant::NIL, -1, "Unknown array element type.");
-
- bool preset_hint = false;
- if (elem_variant_type == Variant::STRING) {
- MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
- if (PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)) == PROPERTY_HINT_ENUM) {
- r_hint_string = itos(elem_variant_type) + "/" + itos(PROPERTY_HINT_ENUM) + ":" + CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
- preset_hint = true;
- }
- }
-
- if (!preset_hint) {
- int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
-
- ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
-
- // Format: type/hint:hint_string
- r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
- }
-
- r_hint = PROPERTY_HINT_TYPE_STRING;
-
- } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
- // TODO: Dictionaries are not supported in the inspector
- } else {
- return 0;
- }
-
- return 1;
-}
-#endif
-
-Variant CSharpScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (unlikely(GDMono::get_singleton() == nullptr)) {
- // Probably not the best error but eh.
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- return Variant();
- }
-
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- GDMonoMethod *method = top->get_method(p_method, p_argcount);
-
- if (method && method->is_static()) {
- MonoObject *result = method->invoke(nullptr, p_args);
-
- if (result) {
- return GDMonoMarshal::mono_object_to_variant(result);
- } else {
- return Variant();
- }
- }
-
- top = top->get_parent_class();
- }
-
- // No static method found. Try regular instance calls
- return Script::callp(p_method, p_args, p_argcount, r_error);
-}
-
-void CSharpScript::_resource_path_changed() {
- _update_name();
-}
-
bool CSharpScript::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == CSharpLanguage::singleton->string_names._script_source) {
r_ret = get_source_code();
@@ -2983,107 +2183,107 @@ void CSharpScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &CSharpScript::_new, MethodInfo("new"));
}
-Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native) {
- // This method should not fail, only assertions allowed
+void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
+ // IMPORTANT:
+ // This method must be called only after the CSharpScript and its associated type
+ // have been added to the script bridge map in the ScriptManagerBridge C# class.
+ // Other than that, it's the same as `CSharpScript::reload`.
- CRASH_COND(p_class == nullptr);
+ // This method should not fail, only assertions allowed.
- // TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
- Ref<CSharpScript> script = memnew(CSharpScript);
-
- initialize_for_managed_type(script, p_class, p_native);
-
- return script;
-}
-
-void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native) {
- // This method should not fail, only assertions allowed
-
- CRASH_COND(p_class == nullptr);
-
- p_script->name = p_class->get_name();
- p_script->script_class = p_class;
- p_script->native = p_native;
-
- CRASH_COND(p_script->native == nullptr);
+ // Unlike `reload`, we print an error rather than silently returning,
+ // as we can assert this won't be called a second time until invalidated.
+ ERR_FAIL_COND(!p_script->reload_invalidated);
p_script->valid = true;
p_script->reload_invalidated = false;
update_script_class_info(p_script);
-#ifdef TOOLS_ENABLED
- p_script->_update_member_info_no_exports();
-#endif
+ p_script->_update_exports();
}
// Extract information about the script using the mono class.
void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
- GDMonoClass *base = p_script->script_class->get_parent_class();
+ bool tool = false;
- // `base` should only be set if the script is a user defined type.
- if (base != p_script->native) {
- p_script->base = base;
- }
+ // TODO: Use GDExtension godot_dictionary
+ Array methods_array;
+ methods_array.~Array();
+ Dictionary rpc_functions_dict;
+ rpc_functions_dict.~Dictionary();
+ Dictionary signals_dict;
+ signals_dict.~Dictionary();
- p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
+ Ref<CSharpScript> base_script;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
+ p_script.ptr(), &tool, &methods_array, &rpc_functions_dict, &signals_dict, &base_script);
- if (!p_script->tool) {
- GDMonoClass *nesting_class = p_script->script_class->get_nesting_class();
- p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
- }
+ p_script->tool = tool;
-#ifdef TOOLS_ENABLED
- if (!p_script->tool) {
- p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
- }
-#endif
+ p_script->rpc_config.clear();
+ p_script->rpc_config = rpc_functions_dict;
-#ifdef DEBUG_ENABLED
- // For debug builds, we must fetch from all native base methods as well.
- // Native base methods must be fetched before the current class.
- // Not needed if the script class itself is a native class.
+ // Methods
- if (p_script->script_class != p_script->native) {
- GDMonoClass *native_top = p_script->native;
- while (native_top) {
- native_top->fetch_methods_with_godot_api_checks(p_script->native);
+ p_script->methods.clear();
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
+ p_script->methods.resize(methods_array.size());
+ int push_index = 0;
- native_top = native_top->get_parent_class();
+ for (int i = 0; i < methods_array.size(); i++) {
+ Dictionary method_info_dict = methods_array[i];
+
+ StringName name = method_info_dict["name"];
+
+ MethodInfo mi;
+ mi.name = name;
+
+ Array params = method_info_dict["params"];
+
+ for (int j = 0; j < params.size(); j++) {
+ Dictionary param = params[j];
+
+ Variant::Type param_type = (Variant::Type)(int)param["type"];
+ PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
+ arg_info.usage = (uint32_t)param["usage"];
+ mi.arguments.push_back(arg_info);
}
+
+ p_script->methods.set(push_index++, CSharpMethodInfo{ name, mi });
}
-#endif
- p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
+ // Event signals
- p_script->rpc_config.clear();
+ // Performance is not critical here as this will be replaced with source generators.
- GDMonoClass *top = p_script->script_class;
- while (top && top != p_script->native) {
- // Fetch methods from base classes as well
- top->fetch_methods_with_godot_api_checks(p_script->native);
+ p_script->event_signals.clear();
- // Update RPC info
- {
- Vector<GDMonoMethod *> methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); i++) {
- if (!methods[i]->is_static()) {
- 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;
- }
- }
- }
+ // Sigh... can't we just have capacity?
+ p_script->event_signals.resize(signals_dict.size());
+ push_index = 0;
+
+ for (const Variant *s = signals_dict.next(nullptr); s != nullptr; s = signals_dict.next(s)) {
+ StringName name = *s;
+
+ MethodInfo mi;
+ mi.name = name;
+
+ Array params = signals_dict[*s];
+
+ for (int i = 0; i < params.size(); i++) {
+ Dictionary param = params[i];
+
+ Variant::Type param_type = (Variant::Type)(int)param["type"];
+ PropertyInfo arg_info = PropertyInfo(param_type, (String)param["name"]);
+ arg_info.usage = (uint32_t)param["usage"];
+ mi.arguments.push_back(arg_info);
}
- top = top->get_parent_class();
+ p_script->event_signals.set(push_index++, EventSignalInfo{ name, mi });
}
- p_script->load_script_signals(p_script->script_class, p_script->native);
+ p_script->base_script = base_script;
}
bool CSharpScript::can_instantiate() const {
@@ -3096,43 +2296,22 @@ bool CSharpScript::can_instantiate() const {
// FIXME Need to think this through better.
// For tool scripts, this will never fire if the class is not found. That's because we
// don't know if it's a tool script if we can't find the class to access the attributes.
- if (extra_cond && !script_class) {
- if (GDMono::get_singleton()->get_project_assembly() == nullptr) {
- // The project assembly is not loaded
- ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
- } else {
- // The project assembly is loaded, but the class could not found
- ERR_FAIL_V_MSG(false, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
- }
+ if (extra_cond && !valid) {
+ ERR_FAIL_V_MSG(false, "Cannot instance script because the associated class could not be found. Script: '" + get_path() + "'. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive).");
}
return valid && extra_cond;
}
StringName CSharpScript::get_instance_base_type() const {
- if (native) {
- return native->get_name();
- } else {
- return StringName();
- }
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
+ return native_name;
}
CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
- GD_MONO_ASSERT_THREAD_ATTACHED;
-
/* STEP 1, CREATE */
- // Search the constructor first, to fail with an error if it's not found before allocating anything else.
- GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
- if (ctor == nullptr) {
- ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr,
- "Cannot create script instance. The class '" + script_class->get_full_name() +
- "' does not define a parameterless constructor." +
- (get_path().is_empty() ? String() : " Path: '" + get_path() + "'."));
-
- ERR_FAIL_V_MSG(nullptr, "Constructor not found.");
- }
-
Ref<RefCounted> ref;
if (p_is_ref_counted) {
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
@@ -3146,15 +2325,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
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) {
- MonoException *exc = nullptr;
- GDMonoUtils::dispose(mono_object, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
+ GDMonoCache::managed_callbacks.CSharpInstanceBridge_CallDispose(
+ script_binding.gchandle.get_intptr(), /* okIfNull */ true);
script_binding.gchandle.release(); // Just in case
script_binding.inited = false;
@@ -3168,38 +2340,19 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
/* STEP 2, INITIALIZE AND CONSTRUCT */
- MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
+ bool ok = GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance(
+ this, p_owner, p_args, p_argcount);
- if (!mono_object) {
+ if (!ok) {
// Important to clear this before destroying the script instance here
instance->script = Ref<CSharpScript>();
instance->owner = nullptr;
-
- bool die = instance->_unreference_owner_unsafe();
- // Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
- CRASH_COND(die);
-
p_owner->set_script_instance(nullptr);
- r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
- ERR_FAIL_V_MSG(nullptr, "Failed to allocate memory for the object.");
- }
-
- // Tie managed to unmanaged
- instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (instance->base_ref_counted) {
- instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
+ return nullptr;
}
- {
- MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
- instances.insert(instance->owner);
- }
-
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, instance->owner);
-
- // Construct
- ctor->invoke(mono_object, p_args);
+ CRASH_COND(instance->gchandle.is_released());
/* STEP 3, PARTY */
@@ -3215,11 +2368,12 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_NULL_V(native, Variant());
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
- GD_MONO_SCOPE_THREAD_ATTACH;
+ ERR_FAIL_COND_V(native_name == StringName(), Variant());
- Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native));
+ Object *owner = ClassDB::instantiate(native_name);
Ref<RefCounted> ref;
RefCounted *r = Object::cast_to<RefCounted>(owner);
@@ -3247,18 +2401,18 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
- GD_MONO_SCOPE_THREAD_ATTACH;
+ StringName native_name;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name);
- if (native) {
- StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
- if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
- if (EngineDebugger::is_active()) {
- CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
- "Script inherits from native type '" + String(native_name) +
- "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
- }
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'.");
+ ERR_FAIL_COND_V(native_name == StringName(), nullptr);
+
+ if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
+ if (EngineDebugger::is_active()) {
+ CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
+ "Script inherits from native type '" + String(native_name) +
+ "', so it can't be assigned to an object of type: '" + p_this->get_class() + "'");
}
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be assigned to an object of type: '" + p_this->get_class() + "'.");
}
Callable::CallError unchecked_error;
@@ -3300,54 +2454,43 @@ void CSharpScript::set_source_code(const String &p_code) {
}
void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
- if (!script_class) {
+ if (!valid) {
return;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- const Vector<GDMonoMethod *> &methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- MethodInfo minfo = methods[i]->get_method_info();
- if (minfo.name != CACHED_STRING_NAME(dotctor)) {
- p_list->push_back(methods[i]->get_method_info());
- }
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ for (const CSharpMethodInfo &E : top->methods) {
+ p_list->push_back(E.method_info);
}
- top = top->get_parent_class();
+ top = top->base_script.ptr();
}
}
bool CSharpScript::has_method(const StringName &p_method) const {
- if (!script_class) {
+ if (!valid) {
return false;
}
- GD_MONO_SCOPE_THREAD_ATTACH;
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return true;
+ }
+ }
- return script_class->has_fetched_method_unknown_params(p_method);
+ return false;
}
MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
- if (!script_class) {
+ if (!valid) {
return MethodInfo();
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- GDMonoClass *top = script_class;
-
- while (top && top != native) {
- GDMonoMethod *params = top->get_fetched_method_unknown_params(p_method);
- if (params) {
- return params->get_method_info();
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ return E.method_info;
}
-
- top = top->get_parent_class();
}
return MethodInfo();
@@ -3362,30 +2505,15 @@ Error CSharpScript::reload(bool p_keep_state) {
// That's done separately via domain reloading.
reload_invalidated = false;
- GD_MONO_SCOPE_THREAD_ATTACH;
-
- const DotNetScriptLookupInfo *lookup_info =
- CSharpLanguage::get_singleton()->lookup_dotnet_script(get_path());
-
- if (lookup_info) {
- GDMonoClass *klass = lookup_info->script_class;
- if (klass) {
- ERR_FAIL_COND_V(!CACHED_CLASS(GodotObject)->is_assignable_from(klass), FAILED);
- script_class = klass;
- }
- }
+ String script_path = get_path();
- valid = script_class != nullptr;
+ valid = GDMonoCache::managed_callbacks.ScriptManagerBridge_AddScriptBridge(this, &script_path);
- if (script_class) {
+ if (valid) {
#ifdef DEBUG_ENABLED
- print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
+ print_verbose("Found class for script " + get_path());
#endif
- native = GDMonoUtils::get_class_native_base(script_class);
-
- CRASH_COND(native == nullptr);
-
update_script_class_info(this);
_update_exports();
@@ -3407,8 +2535,8 @@ bool CSharpScript::get_property_default_value(const StringName &p_property, Vari
return true;
}
- if (base_cache.is_valid()) {
- return base_cache->get_property_default_value(p_property, r_value);
+ if (base_script.is_valid()) {
+ return base_script->get_property_default_value(p_property, r_value);
}
#endif
@@ -3422,48 +2550,39 @@ void CSharpScript::update_exports() {
}
bool CSharpScript::has_script_signal(const StringName &p_signal) const {
- return event_signals.has(p_signal) || _signals.has(p_signal);
-}
-
-void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const KeyValue<StringName, Vector<SignalParameter>> &E : _signals) {
- MethodInfo mi;
- mi.name = E.key;
-
- const Vector<SignalParameter> &params = E.value;
- for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = params[i];
+ if (!valid) {
+ return false;
+ }
- PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
+ }
- mi.arguments.push_back(arg_info);
+ for (const EventSignalInfo &signal : event_signals) {
+ if (signal.name == p_signal) {
+ return true;
}
-
- r_signals->push_back(mi);
}
- for (const KeyValue<StringName, EventSignal> &E : event_signals) {
- MethodInfo mi;
- mi.name = E.key;
-
- const EventSignal &event_signal = E.value;
- const Vector<SignalParameter> &params = event_signal.parameters;
- for (int i = 0; i < params.size(); i++) {
- const SignalParameter &param = params[i];
+ return false;
+}
- PropertyInfo arg_info = PropertyInfo(param.type, param.name);
- if (param.type == Variant::NIL && param.nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
+void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ if (!valid) {
+ return;
+ }
- mi.arguments.push_back(arg_info);
- }
+ for (const EventSignalInfo &signal : get_script_event_signals()) {
+ r_signals->push_back(signal.method_info);
+ }
+}
- r_signals->push_back(mi);
+Vector<CSharpScript::EventSignalInfo> CSharpScript::get_script_event_signals() const {
+ if (!valid) {
+ return Vector<EventSignalInfo>();
}
+
+ return event_signals;
}
bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
@@ -3472,32 +2591,47 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
return false;
}
- if (script_class == nullptr || cs->script_class == nullptr) {
+ if (!valid || !cs->valid) {
return false;
}
- if (script_class == cs->script_class) {
- return true;
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
}
- return cs->script_class->is_assignable_from(script_class);
+ return GDMonoCache::managed_callbacks.ScriptManagerBridge_ScriptIsOrInherits(this, cs.ptr());
}
Ref<Script> CSharpScript::get_base_script() const {
- // TODO search in metadata file once we have it, not important any way?
- return Ref<Script>();
+ return base_script;
}
void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
- List<PropertyInfo> props;
+#ifdef TOOLS_ENABLED
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ for (const PropertyInfo &E : top->exported_members_cache) {
+ r_list->push_back(E);
+ }
- for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
- props.push_front(E.value);
+ top = top->base_script.ptr();
}
+#else
+ const CSharpScript *top = this;
+ while (top != nullptr) {
+ List<PropertyInfo> props;
- for (const PropertyInfo &prop : props) {
- r_list->push_back(prop);
+ for (const KeyValue<StringName, PropertyInfo> &E : top->member_info) {
+ props.push_front(E.value);
+ }
+
+ for (const PropertyInfo &prop : props) {
+ r_list->push_back(prop);
+ }
+
+ top = top->base_script.ptr();
}
+#endif
}
int CSharpScript::get_member_line(const StringName &p_member) const {
@@ -3505,22 +2639,6 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
-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 out;
-}
-
const Variant CSharpScript::get_rpc_config() const {
return rpc_config;
}
@@ -3541,29 +2659,15 @@ Error CSharpScript::load_source_code(const String &p_path) {
return OK;
}
-void CSharpScript::_update_name() {
- String path = get_path();
-
- if (!path.is_empty()) {
- name = get_path().get_file().get_basename();
- }
-}
-
void CSharpScript::_clear() {
tool = false;
valid = false;
reload_invalidated = true;
-
- base = nullptr;
- native = nullptr;
- script_class = nullptr;
}
CSharpScript::CSharpScript() {
_clear();
- _update_name();
-
#ifdef DEBUG_ENABLED
{
MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
@@ -3577,6 +2681,10 @@ CSharpScript::~CSharpScript() {
MutexLock lock(CSharpLanguage::get_singleton()->script_instances_mutex);
CSharpLanguage::get_singleton()->script_list.remove(&this->script_list);
#endif
+
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_RemoveScriptBridge(this);
+ }
}
void CSharpScript::get_members(HashSet<StringName> *p_members) {
@@ -3598,9 +2706,13 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
// TODO ignore anything inside bin/ and obj/ in tools builds?
- CSharpScript *script = memnew(CSharpScript);
+ Ref<CSharpScript> script;
- Ref<CSharpScript> scriptres(script);
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &script);
+ } else {
+ script = Ref<CSharpScript>(memnew(CSharpScript));
+ }
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
Error err = script->load_source_code(p_path);
@@ -3615,7 +2727,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
*r_error = OK;
}
- return scriptres;
+ return script;
}
void ResourceFormatLoaderCSharpScript::get_recognized_extensions(List<String> *p_extensions) const {
@@ -3678,14 +2790,7 @@ bool ResourceFormatSaverCSharpScript::recognize(const Ref<Resource> &p_resource)
}
CSharpLanguage::StringNameCache::StringNameCache() {
- _signal_callback = StaticCString::create("_signal_callback");
- _set = StaticCString::create("_set");
- _get = StaticCString::create("_get");
- _get_property_list = StaticCString::create("_get_property_list");
- _notification = StaticCString::create("_notification");
+ _property_can_revert = StaticCString::create("_property_can_revert");
+ _property_get_revert = StaticCString::create("_property_get_revert");
_script_source = StaticCString::create("script/source");
- on_before_serialize = StaticCString::create("OnBeforeSerialize");
- on_after_deserialize = StaticCString::create("OnAfterDeserialize");
- dotctor = StaticCString::create(".ctor");
- delegate_invoke_method_name = StaticCString::create("Invoke");
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 48129e69cb..ef381fd76a 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* csharp_script.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* csharp_script.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CSHARP_SCRIPT_H
#define CSHARP_SCRIPT_H
@@ -39,8 +39,6 @@
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
-#include "mono_gd/gd_mono_header.h"
-#include "mono_gd/gd_mono_internals.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_plugin.h"
@@ -50,65 +48,24 @@ class CSharpScript;
class CSharpInstance;
class CSharpLanguage;
-#ifdef NO_SAFE_CAST
-template <typename TScriptInstance, typename TScriptLanguage>
-TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
- if (!p_inst) {
- return nullptr;
- }
- return p_inst->get_language() == TScriptLanguage::get_singleton() ? static_cast<TScriptInstance *>(p_inst) : nullptr;
-}
-#else
template <typename TScriptInstance, typename TScriptLanguage>
TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
return dynamic_cast<TScriptInstance *>(p_inst);
}
-#endif
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
-struct DotNetScriptLookupInfo {
- String class_namespace;
- String class_name;
- GDMonoClass *script_class = nullptr;
-
- DotNetScriptLookupInfo() {} // Required by HashMap...
-
- DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) :
- class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) {
- }
-};
-
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
-public:
- struct SignalParameter {
- String name;
- Variant::Type type;
- bool nil_is_variant = false;
- };
-
- struct EventSignal {
- GDMonoField *field = nullptr;
- GDMonoMethod *invoke_method = nullptr;
- Vector<SignalParameter> parameters;
- };
-
-private:
friend class CSharpInstance;
friend class CSharpLanguage;
- friend struct CSharpScriptDepSort;
bool tool = false;
bool valid = false;
bool reload_invalidated = false;
- GDMonoClass *base = nullptr;
- GDMonoClass *native = nullptr;
- GDMonoClass *script_class = nullptr;
-
- Ref<CSharpScript> base_cache; // TODO what's this for?
+ Ref<CSharpScript> base_script;
HashSet<Object *> instances;
@@ -118,26 +75,35 @@ private:
// Replace with buffer containing the serialized state of managed scripts.
// Keep variant state backup to use only with script instance placeholders.
List<Pair<StringName, Variant>> properties;
- List<Pair<StringName, Array>> event_signals;
+ Dictionary event_signals;
};
HashSet<ObjectID> pending_reload_instances;
RBMap<ObjectID, StateBackup> pending_reload_state;
- StringName tied_class_name_for_reload;
- StringName tied_class_namespace_for_reload;
+
+ bool was_tool_before_reload = false;
+ HashSet<ObjectID> pending_replace_placeholders;
#endif
String source;
- StringName name;
SelfList<CSharpScript> script_list = this;
- HashMap<StringName, Vector<SignalParameter>> _signals;
- HashMap<StringName, EventSignal> event_signals;
- bool signals_invalidated = true;
-
Dictionary rpc_config;
+ struct EventSignalInfo {
+ StringName name; // MethodInfo stores a string...
+ MethodInfo method_info;
+ };
+
+ struct CSharpMethodInfo {
+ StringName name; // MethodInfo stores a string...
+ MethodInfo method_info;
+ };
+
+ Vector<EventSignalInfo> event_signals;
+ Vector<CSharpMethodInfo> methods;
+
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
@@ -146,7 +112,6 @@ private:
bool placeholder_fallback_enabled = false;
bool exports_invalidated = true;
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
@@ -158,39 +123,28 @@ private:
void _clear();
- void _update_name();
-
- void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
- bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
-
- bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
-
- bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
+ static void GD_CLR_STDCALL _add_property_info_list_callback(CSharpScript *p_script, const String *p_current_class_name, void *p_props, int32_t p_count);
#ifdef TOOLS_ENABLED
- static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
+ static void GD_CLR_STDCALL _add_property_default_values_callback(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
#endif
+ bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
// Do not use unless you know what you are doing
- friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
- static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
static void update_script_class_info(Ref<CSharpScript> p_script);
- static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
-
- Variant _member_get_rpc_config(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
- Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void _resource_path_changed() override;
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_properties) const;
public:
+ static void reload_registered_script(Ref<CSharpScript> p_script);
+
bool can_instantiate() const override;
StringName get_instance_base_type() const override;
ScriptInstance *instance_create(Object *p_this) override;
@@ -214,14 +168,20 @@ public:
bool has_script_signal(const StringName &p_signal) const override;
void get_script_signal_list(List<MethodInfo> *r_signals) const override;
+ Vector<EventSignalInfo> get_script_event_signals() const;
+
bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
void get_members(HashSet<StringName> *p_members) override;
- bool is_tool() const override { return tool; }
- bool is_valid() const override { return valid; }
+ bool is_tool() const override {
+ return tool;
+ }
+ bool is_valid() const override {
+ return valid;
+ }
bool inherits_script(const Ref<Script> &p_script) const override;
@@ -237,7 +197,9 @@ public:
const Variant get_rpc_config() const override;
#ifdef TOOLS_ENABLED
- bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
+ bool is_placeholder_fallback_enabled() const override {
+ return placeholder_fallback_enabled;
+ }
#endif
Error load_source_code(const String &p_path);
@@ -270,22 +232,18 @@ class CSharpInstance : public ScriptInstance {
bool _unreference_owner_unsafe();
/*
- * If nullptr is returned, the caller must destroy the script instance by removing it from its owner.
+ * If false is returned, the caller must destroy the script instance by removing it from its owner.
*/
- MonoObject *_internal_new_managed();
+ bool _internal_new_managed();
// Do not use unless you know what you are doing
- friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static CSharpInstance *create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle);
- void get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state);
- void get_event_signals_state_for_reloading(List<Pair<StringName, Array>> &r_state);
-
public:
- MonoObject *get_mono_object() const;
-
_FORCE_INLINE_ bool is_destructing_script_instance() { return destructing_script_instance; }
+ _FORCE_INLINE_ GCHandleIntPtr get_gchandle_intptr() { return gchandle.get_intptr(); }
+
Object *get_owner() override;
bool set(const StringName &p_name, const Variant &p_value) override;
@@ -293,17 +251,20 @@ public:
void get_property_list(List<PropertyInfo> *p_properties) const override;
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override;
+ bool property_can_revert(const StringName &p_name) const override;
+ bool property_get_revert(const StringName &p_name, Variant &r_ret) const override;
+
void get_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
- void mono_object_disposed(MonoObject *p_obj);
+ void mono_object_disposed(GCHandleIntPtr p_gchandle_to_free);
/*
* If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
- void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
+ void mono_object_disposed_baseref(GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
void connect_event_signals();
void disconnect_event_signals();
@@ -329,7 +290,6 @@ public:
struct CSharpScriptBinding {
bool inited = false;
StringName type_name;
- GDMonoClass *wrapper_class = nullptr;
MonoGCHandleData gchandle;
Object *owner = nullptr;
@@ -367,33 +327,22 @@ class CSharpLanguage : public ScriptLanguage {
ManagedCallableMiddleman *managed_callable_middleman = memnew(ManagedCallableMiddleman);
struct StringNameCache {
- StringName _signal_callback;
- StringName _set;
- StringName _get;
- StringName _get_property_list;
- StringName _notification;
+ StringName _property_can_revert;
+ StringName _property_get_revert;
StringName _script_source;
- StringName dotctor; // .ctor
- StringName on_before_serialize; // OnBeforeSerialize
- StringName on_after_deserialize; // OnAfterDeserialize
- StringName delegate_invoke_method_name;
StringNameCache();
};
int lang_idx = -1;
- HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map;
-
- void lookup_script_for_class(GDMonoClass *p_class);
-
// For debug_break and debug_break_parse
int _debug_parse_err_line = -1;
String _debug_parse_err_file;
String _debug_error;
friend class GDMono;
- void _on_scripts_domain_unloaded();
+ void _on_scripts_domain_about_to_unload();
#ifdef TOOLS_ENABLED
EditorPlugin *godotsharp_editor = nullptr;
@@ -403,9 +352,9 @@ class CSharpLanguage : public ScriptLanguage {
static void *_instance_binding_create_callback(void *p_token, void *p_instance);
static void _instance_binding_free_callback(void *p_token, void *p_instance, void *p_binding);
- static GDNativeBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference);
+ static GDExtensionBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDExtensionBool p_reference);
- static GDNativeInstanceBindingCallbacks _instance_binding_callbacks;
+ static GDExtensionInstanceBindingCallbacks _instance_binding_callbacks;
public:
static void *get_instance_binding(Object *p_object);
@@ -415,21 +364,35 @@ public:
StringNameCache string_names;
- const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
+ const Mutex &get_language_bind_mutex() {
+ return language_bind_mutex;
+ }
+ const Mutex &get_script_instances_mutex() {
+ return script_instances_mutex;
+ }
- _FORCE_INLINE_ int get_language_index() { return lang_idx; }
+ _FORCE_INLINE_ int get_language_index() {
+ return lang_idx;
+ }
void set_language_index(int p_idx);
- _FORCE_INLINE_ const StringNameCache &get_string_names() { return string_names; }
+ _FORCE_INLINE_ const StringNameCache &get_string_names() {
+ return string_names;
+ }
- _FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static CSharpLanguage *get_singleton() {
+ return singleton;
+ }
#ifdef TOOLS_ENABLED
- _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; }
+ _FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const {
+ return godotsharp_editor;
+ }
#endif
static void release_script_gchandle(MonoGCHandleData &p_gchandle);
- static void release_script_gchandle(MonoObject *p_expected_obj, MonoGCHandleData &p_gchandle);
+ static void release_script_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, MonoGCHandleData &r_gchandle);
+ static void release_binding_gchandle_thread_safe(GCHandleIntPtr p_gchandle_to_free, CSharpScriptBinding &r_script_binding);
bool debug_break(const String &p_error, bool p_allow_continue = true);
bool debug_break_parse(const String &p_file, int p_line, const String &p_error);
@@ -439,12 +402,8 @@ public:
void reload_assemblies(bool p_soft_reload);
#endif
- _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
-
- void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly);
-
- const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const {
- return dotnet_script_lookup_map.getptr(p_script_path);
+ _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const {
+ return managed_callable_middleman;
}
String get_name() const override;
@@ -474,7 +433,9 @@ public:
Script *create_script() const override;
bool has_named_classes() const override;
bool supports_builtin_mode() const override;
- /* TODO? */ int find_function(const String &p_function, const String &p_code) const override { return -1; }
+ /* TODO? */ int find_function(const String &p_function, const String &p_code) const override {
+ return -1;
+ }
String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
virtual String _get_indentation() const;
/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
@@ -489,14 +450,20 @@ public:
/* TODO */ void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
/* TODO */ void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
/* TODO */ void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) override {}
- /* TODO */ String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) override { return ""; }
+ /* TODO */ String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) override {
+ return "";
+ }
Vector<StackInfo> debug_get_current_stack_info() override;
/* PROFILING FUNCTIONS */
/* TODO */ void profiling_start() override {}
/* TODO */ void profiling_stop() override {}
- /* TODO */ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override { return 0; }
- /* TODO */ int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override { return 0; }
+ /* TODO */ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override {
+ return 0;
+ }
+ /* TODO */ int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override {
+ return 0;
+ }
void frame() override;
@@ -515,16 +482,12 @@ public:
bool overrides_external_editor() override;
#endif
- /* THREAD ATTACHING */
- void thread_enter() override;
- void thread_exit() override;
-
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
- Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
-#endif
+ static void tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted);
+ static void tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted);
+ static void tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged);
void post_unsafe_reference(Object *p_obj);
void pre_unsafe_unreference(Object *p_obj);
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 9de6b48e9e..faf3512da7 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -10,55 +10,10 @@
<tutorials>
</tutorials>
<methods>
- <method name="attach_thread">
- <return type="void" />
- <description>
- Attaches the current thread to the Mono runtime.
- </description>
- </method>
- <method name="detach_thread">
- <return type="void" />
- <description>
- Detaches the current thread from the Mono runtime.
- </description>
- </method>
- <method name="get_domain_id">
- <return type="int" />
- <description>
- Returns the current MonoDomain ID.
- [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
- </description>
- </method>
- <method name="get_scripts_domain_id">
- <return type="int" />
- <description>
- Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
- [b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
- </description>
- </method>
- <method name="is_domain_finalizing_for_unload">
- <return type="bool" />
- <argument index="0" name="domain_id" type="int" />
- <description>
- Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
- </description>
- </method>
<method name="is_runtime_initialized">
<return type="bool" />
<description>
- Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
- </description>
- </method>
- <method name="is_runtime_shutting_down">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
- </description>
- </method>
- <method name="is_scripts_domain_loaded">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
+ Returns [code]true[/code] if the .NET runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
</methods>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
index d1868f52ef..03a7dc453c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
EndProject
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index 4e9e7184da..013b210ff4 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -26,16 +26,8 @@
<None Include="Sdk\Sdk.props" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\Sdk.targets" Pack="true" PackagePath="Sdk" />
<!-- SdkPackageVersions.props -->
- <None Include="..\..\..\SdkPackageVersions.props" Pack="true" PackagePath="Sdk">
+ <None Include="$(GodotSdkPackageVersionsFilePath)" Pack="true" PackagePath="Sdk">
<Link>Sdk\SdkPackageVersions.props</Link>
</None>
</ItemGroup>
-
- <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
- </PropertyGroup>
- <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
- </Target>
</Project>
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 5a499742e9..0d0889c491 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
@@ -12,8 +12,7 @@
<Configurations>Debug;ExportDebug;ExportRelease</Configurations>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <GodotProjectDir Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)</GodotProjectDir>
- <GodotProjectDir Condition=" '$(SolutionDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
+ <GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
@@ -62,7 +61,7 @@
</PropertyGroup>
<PropertyGroup>
- <GodotRealTIsDouble Condition=" '$(GodotRealTIsDouble)' == '' ">false</GodotRealTIsDouble>
+ <GodotFloat64 Condition=" '$(GodotFloat64)' == '' ">false</GodotFloat64>
</PropertyGroup>
<!-- Godot DefineConstants. -->
@@ -77,12 +76,11 @@
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;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)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
- <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
+ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'web' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
</PropertyGroup>
@@ -95,21 +93,4 @@
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
</PropertyGroup>
-
- <!-- Godot API references -->
- <ItemGroup>
- <!--
- TODO:
- We should consider a nuget package for reference assemblies. This is difficult because the
- Godot scripting API is continuaslly breaking backwards compatibility even in patch releases.
- -->
- <Reference Include="GodotSharp">
- <Private>false</Private>
- <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharp.dll</HintPath>
- </Reference>
- <Reference Include="GodotSharpEditor" Condition=" '$(Configuration)' == 'Debug' ">
- <Private>false</Private>
- <HintPath>$(GodotProjectDir).godot\mono\assemblies\$(GodotApiConfiguration)\GodotSharpEditor.dll</HintPath>
- </Reference>
- </ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
index 397ede9644..859ea52c93 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -10,13 +10,19 @@
<!--
Define constant to determine whether the real_t type in Godot is double precision or not.
By default this is false, like the official Godot builds. If someone is using a custom
- Godot build where real_t is double, they can override the GodotRealTIsDouble property.
+ Godot build where real_t is double, they can override the GodotFloat64 property.
-->
- <DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition=" '$(GodotFloat64)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<!-- C# source generators -->
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
- <PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
+ <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
+ </ItemGroup>
+
+ <!-- Godot API references -->
+ <ItemGroup Condition=" '$(DisableImplicitGodotSharpReferences)' != 'true' ">
+ <PackageReference Include="GodotSharp" Version="$(PackageVersion_GodotSharp)" />
+ <PackageReference Include="GodotSharpEditor" Version="$(PackageVersion_GodotSharp)" Condition=" '$(Configuration)' == 'Debug' " />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs
new file mode 100644
index 0000000000..764ba8f121
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/EventSignals.cs
@@ -0,0 +1,7 @@
+namespace Godot.SourceGenerators.Sample;
+
+public partial class EventSignals : Godot.Object
+{
+ [Signal]
+ public delegate void MySignalEventHandler(string str, int num);
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs
new file mode 100644
index 0000000000..ccaba4d727
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedFields.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS0169
+#pragma warning disable CS0414
+
+namespace Godot.SourceGenerators.Sample
+{
+ [SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public partial class ExportedFields : Godot.Object
+ {
+ [Export] private Boolean field_Boolean = true;
+ [Export] private Char field_Char = 'f';
+ [Export] private SByte field_SByte = 10;
+ [Export] private Int16 field_Int16 = 10;
+ [Export] private Int32 field_Int32 = 10;
+ [Export] private Int64 field_Int64 = 10;
+ [Export] private Byte field_Byte = 10;
+ [Export] private UInt16 field_UInt16 = 10;
+ [Export] private UInt32 field_UInt32 = 10;
+ [Export] private UInt64 field_UInt64 = 10;
+ [Export] private Single field_Single = 10;
+ [Export] private Double field_Double = 10;
+ [Export] private String field_String = "foo";
+
+ // Godot structs
+ [Export] private Vector2 field_Vector2 = new(10f, 10f);
+ [Export] private Vector2i field_Vector2i = Vector2i.Up;
+ [Export] private Rect2 field_Rect2 = new(new Vector2(10f, 10f), new Vector2(10f, 10f));
+ [Export] private Rect2i field_Rect2i = new(new Vector2i(10, 10), new Vector2i(10, 10));
+ [Export] private Transform2D field_Transform2D = Transform2D.Identity;
+ [Export] private Vector3 field_Vector3 = new(10f, 10f, 10f);
+ [Export] private Vector3i field_Vector3i = Vector3i.Back;
+ [Export] private Basis field_Basis = new Basis(Quaternion.Identity);
+ [Export] private Quaternion field_Quaternion = new Quaternion(Basis.Identity);
+ [Export] private Transform3D field_Transform3D = Transform3D.Identity;
+ [Export] private Vector4 field_Vector4 = new(10f, 10f, 10f, 10f);
+ [Export] private Vector4i field_Vector4i = Vector4i.One;
+ [Export] private Projection field_Projection = Projection.Identity;
+ [Export] private AABB field_AABB = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f));
+ [Export] private Color field_Color = Colors.Aquamarine;
+ [Export] private Plane field_Plane = Plane.PlaneXZ;
+ [Export] private Callable field_Callable = new Callable(Engine.GetMainLoop(), "_process");
+ [Export] private Signal field_Signal = new Signal(Engine.GetMainLoop(), "property_list_changed");
+
+ // Enums
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyEnum field_Enum = MyEnum.C;
+
+ [Flags]
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyFlagsEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyFlagsEnum field_FlagsEnum = MyFlagsEnum.C;
+
+ // Arrays
+ [Export] private Byte[] field_ByteArray = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int32[] field_Int32Array = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int64[] field_Int64Array = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Single[] field_SingleArray = { 0f, 1f, 2f, 3f, 4f, 5f, 6f };
+ [Export] private Double[] field_DoubleArray = { 0d, 1d, 2d, 3d, 4d, 5d, 6d };
+ [Export] private String[] field_StringArray = { "foo", "bar" };
+ [Export(PropertyHint.Enum, "A,B,C")] private String[] field_StringArrayEnum = { "foo", "bar" };
+ [Export] private Vector2[] field_Vector2Array = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right };
+ [Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
+ [Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
+ [Export] private Godot.Object[] field_GodotObjectOrDerivedArray = { null };
+ [Export] private StringName[] field_StringNameArray = { "foo", "bar" };
+ [Export] private NodePath[] field_NodePathArray = { "foo", "bar" };
+ [Export] private RID[] field_RIDArray = { default, default, default };
+ // Note we use Array and not System.Array. This tests the generated namespace qualification.
+ [Export] private Int32[] field_empty_Int32Array = Array.Empty<Int32>();
+ // Note we use List and not System.Collections.Generic.
+ [Export] private int[] field_array_from_list = new List<int>(Array.Empty<int>()).ToArray();
+
+ // Variant
+ [Export] private Variant field_Variant = "foo";
+
+ // Classes
+ [Export] private Godot.Object field_GodotObjectOrDerived;
+ [Export] private Godot.Texture field_GodotResourceTexture;
+ [Export] private StringName field_StringName = new StringName("foo");
+ [Export] private NodePath field_NodePath = new NodePath("foo");
+ [Export] private RID field_RID;
+
+ [Export]
+ private Godot.Collections.Dictionary field_GodotDictionary =
+ new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
+
+ [Export]
+ private Godot.Collections.Array field_GodotArray =
+ new() { "foo", 10, Vector2.Up, Colors.Chocolate };
+
+ [Export]
+ private Godot.Collections.Dictionary<string, bool> field_GodotGenericDictionary =
+ new() { { "foo", true }, { "bar", false } };
+
+ [Export]
+ private Godot.Collections.Array<int> field_GodotGenericArray =
+ new() { 0, 1, 2, 3, 4, 5, 6 };
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs
new file mode 100644
index 0000000000..5afaeb736f
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ExportedProperties.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS0169
+#pragma warning disable CS0414
+
+namespace Godot.SourceGenerators.Sample
+{
+ [SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public partial class ExportedProperties : Godot.Object
+ {
+ // Do not generate default value
+ private String _notGenerate_Property_String = new string("not generate");
+ [Export]
+ public String NotGenerate_Complex_Lamda_Property
+ {
+ get => _notGenerate_Property_String + Convert.ToInt32("1");
+ set => _notGenerate_Property_String = value;
+ }
+
+ [Export]
+ public String NotGenerate_Lamda_NoField_Property
+ {
+ get => new string("not generate");
+ set => _notGenerate_Property_String = value;
+ }
+
+ [Export]
+ public String NotGenerate_Complex_Return_Property
+ {
+ get
+ {
+ return _notGenerate_Property_String + Convert.ToInt32("1");
+ }
+ set
+ {
+ _notGenerate_Property_String = value;
+ }
+ }
+
+ private int _notGenerate_Property_Int = 1;
+ [Export]
+ public string NotGenerate_Returns_Property
+ {
+ get
+ {
+ if (_notGenerate_Property_Int == 1)
+ {
+ return "a";
+ }
+ else
+ {
+ return "b";
+ }
+ }
+ set
+ {
+ _notGenerate_Property_Int = value == "a" ? 1 : 2;
+ }
+ }
+
+ // Full Property
+ private String _fullProperty_String = "FullProperty_String";
+ [Export]
+ public String FullProperty_String
+ {
+ get
+ {
+ return _fullProperty_String;
+ }
+ set
+ {
+ _fullProperty_String = value;
+ }
+ }
+
+ private String _fullProperty_String_Complex = new string("FullProperty_String_Complex") + Convert.ToInt32("1");
+ [Export]
+ public String FullProperty_String_Complex
+ {
+ get
+ {
+ return _fullProperty_String_Complex;
+ }
+ set
+ {
+ _fullProperty_String_Complex = value;
+ }
+ }
+
+ // Lambda Property
+ private String _lamdaProperty_String = "LamdaProperty_String";
+ [Export]
+ public String LamdaProperty_String
+ {
+ get => _lamdaProperty_String;
+ set => _lamdaProperty_String = value;
+ }
+
+ // Auto Property
+ [Export] private Boolean property_Boolean { get; set; } = true;
+ [Export] private Char property_Char { get; set; } = 'f';
+ [Export] private SByte property_SByte { get; set; } = 10;
+ [Export] private Int16 property_Int16 { get; set; } = 10;
+ [Export] private Int32 property_Int32 { get; set; } = 10;
+ [Export] private Int64 property_Int64 { get; set; } = 10;
+ [Export] private Byte property_Byte { get; set; } = 10;
+ [Export] private UInt16 property_UInt16 { get; set; } = 10;
+ [Export] private UInt32 property_UInt32 { get; set; } = 10;
+ [Export] private UInt64 property_UInt64 { get; set; } = 10;
+ [Export] private Single property_Single { get; set; } = 10;
+ [Export] private Double property_Double { get; set; } = 10;
+ [Export] private String property_String { get; set; } = "foo";
+
+ // Godot structs
+ [Export] private Vector2 property_Vector2 { get; set; } = new(10f, 10f);
+ [Export] private Vector2i property_Vector2i { get; set; } = Vector2i.Up;
+ [Export] private Rect2 property_Rect2 { get; set; } = new(new Vector2(10f, 10f), new Vector2(10f, 10f));
+ [Export] private Rect2i property_Rect2i { get; set; } = new(new Vector2i(10, 10), new Vector2i(10, 10));
+ [Export] private Transform2D property_Transform2D { get; set; } = Transform2D.Identity;
+ [Export] private Vector3 property_Vector3 { get; set; } = new(10f, 10f, 10f);
+ [Export] private Vector3i property_Vector3i { get; set; } = Vector3i.Back;
+ [Export] private Basis property_Basis { get; set; } = new Basis(Quaternion.Identity);
+ [Export] private Quaternion property_Quaternion { get; set; } = new Quaternion(Basis.Identity);
+ [Export] private Transform3D property_Transform3D { get; set; } = Transform3D.Identity;
+ [Export] private Vector4 property_Vector4 { get; set; } = new(10f, 10f, 10f, 10f);
+ [Export] private Vector4i property_Vector4i { get; set; } = Vector4i.One;
+ [Export] private Projection property_Projection { get; set; } = Projection.Identity;
+ [Export] private AABB property_AABB { get; set; } = new AABB(10f, 10f, 10f, new Vector3(1f, 1f, 1f));
+ [Export] private Color property_Color { get; set; } = Colors.Aquamarine;
+ [Export] private Plane property_Plane { get; set; } = Plane.PlaneXZ;
+ [Export] private Callable property_Callable { get; set; } = new Callable(Engine.GetMainLoop(), "_process");
+ [Export] private Signal property_Signal { get; set; } = new Signal(Engine.GetMainLoop(), "property_list_changed");
+
+ // Enums
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyEnum property_Enum { get; set; } = MyEnum.C;
+
+ [Flags]
+ [SuppressMessage("ReSharper", "UnusedMember.Local")]
+ enum MyFlagsEnum
+ {
+ A,
+ B,
+ C
+ }
+
+ [Export] private MyFlagsEnum property_FlagsEnum { get; set; } = MyFlagsEnum.C;
+
+ // Arrays
+ [Export] private Byte[] property_ByteArray { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int32[] property_Int32Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Int64[] property_Int64Array { get; set; } = { 0, 1, 2, 3, 4, 5, 6 };
+ [Export] private Single[] property_SingleArray { get; set; } = { 0f, 1f, 2f, 3f, 4f, 5f, 6f };
+ [Export] private Double[] property_DoubleArray { get; set; } = { 0d, 1d, 2d, 3d, 4d, 5d, 6d };
+ [Export] private String[] property_StringArray { get; set; } = { "foo", "bar" };
+ [Export(PropertyHint.Enum, "A,B,C")] private String[] property_StringArrayEnum { get; set; } = { "foo", "bar" };
+ [Export] private Vector2[] property_Vector2Array { get; set; } = { Vector2.Up, Vector2.Down, Vector2.Left, Vector2.Right };
+ [Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
+ [Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
+ [Export] private Godot.Object[] property_GodotObjectOrDerivedArray { get; set; } = { null };
+ [Export] private StringName[] field_StringNameArray { get; set; } = { "foo", "bar" };
+ [Export] private NodePath[] field_NodePathArray { get; set; } = { "foo", "bar" };
+ [Export] private RID[] field_RIDArray { get; set; } = { default, default, default };
+
+ // Variant
+ [Export] private Variant property_Variant { get; set; } = "foo";
+
+ // Classes
+ [Export] private Godot.Object property_GodotObjectOrDerived { get; set; }
+ [Export] private Godot.Texture property_GodotResourceTexture { get; set; }
+ [Export] private StringName property_StringName { get; set; } = new StringName("foo");
+ [Export] private NodePath property_NodePath { get; set; } = new NodePath("foo");
+ [Export] private RID property_RID { get; set; }
+
+ [Export]
+ private Godot.Collections.Dictionary property_GodotDictionary { get; set; } =
+ new() { { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
+
+ [Export]
+ private Godot.Collections.Array property_GodotArray { get; set; } =
+ new() { "foo", 10, Vector2.Up, Colors.Chocolate };
+
+ [Export]
+ private Godot.Collections.Dictionary<string, bool> property_GodotGenericDictionary { get; set; } =
+ new() { { "foo", true }, { "bar", false } };
+
+ [Export]
+ private Godot.Collections.Array<int> property_GodotGenericArray { get; set; } =
+ new() { 0, 1, 2, 3, 4, 5, 6 };
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
index 2ddb8880c2..b21b035b4d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
@@ -1,16 +1,21 @@
+#pragma warning disable CS0169
+
namespace Godot.SourceGenerators.Sample
{
partial class Generic<T> : Godot.Object
{
+ private int _field;
}
// Generic again but different generic parameters
partial class Generic<T, R> : Godot.Object
{
+ private int _field;
}
// Generic again but without generic parameters
partial class Generic : Godot.Object
{
+ private int _field;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
index 24f7909861..8e78e0385d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -1,12 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
+ <!-- For compiling GetGodotPropertyDefaultValues. -->
+ <DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
</PropertyGroup>
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
new file mode 100644
index 0000000000..618ba24abc
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Methods.cs
@@ -0,0 +1,31 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Sample;
+
+[SuppressMessage("ReSharper", "RedundantNameQualifier")]
+public partial class Methods : Godot.Object
+{
+ private void MethodWithOverload()
+ {
+ }
+
+ private void MethodWithOverload(int a)
+ {
+ }
+
+ private void MethodWithOverload(int a, int b)
+ {
+ }
+
+ // Should be ignored. The previous one is picked.
+ // ReSharper disable once UnusedMember.Local
+ private void MethodWithOverload(float a, float b)
+ {
+ }
+
+ // Generic methods should be ignored.
+ // ReSharper disable once UnusedMember.Local
+ private void GenericMethod<T>(T t)
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs
new file mode 100644
index 0000000000..a6c8e52667
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/MoreExportedFields.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+#pragma warning disable CS0169
+#pragma warning disable CS0414
+
+namespace Godot.SourceGenerators.Sample
+{
+ [SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ [SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeEvident")]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ // We split the definition of ExportedFields to verify properties work across multiple files.
+ public partial class ExportedFields : Godot.Object
+ {
+ // Note we use Array and not System.Array. This tests the generated namespace qualification.
+ [Export] private Int64[] field_empty_Int64Array = Array.Empty<Int64>();
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
new file mode 100644
index 0000000000..e43a3469ae
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/ScriptBoilerplate.cs
@@ -0,0 +1,36 @@
+#pragma warning disable CS0169
+
+namespace Godot.SourceGenerators.Sample
+{
+ public partial class ScriptBoilerplate : Node
+ {
+ private NodePath _nodePath;
+ private int _velocity;
+
+ public override void _Process(double delta)
+ {
+ _ = delta;
+
+ base._Process(delta);
+ }
+
+ public int Bazz(StringName name)
+ {
+ _ = name;
+ return 1;
+ }
+
+ public void IgnoreThisMethodWithByRefParams(ref int a)
+ {
+ _ = a;
+ }
+ }
+
+ partial struct OuterClass
+ {
+ public partial class NesterClass : RefCounted
+ {
+ public override Variant _Get(StringName property) => default;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 4867c986e6..4eed2d7b7b 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -1,5 +1,7 @@
+using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
@@ -12,14 +14,13 @@ namespace Godot.SourceGenerators
{
string message =
"Missing partial modifier on declaration of type '" +
- $"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
+ $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.Object}'";
- string description = $"{message}. Subclasses of '{GodotClasses.Object}' must be " +
- "declared with the partial modifier or annotated with the " +
- $"attribute '{GodotClasses.DisableGodotGeneratorsAttr}'.";
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
+ "must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GODOT-G0001",
+ new DiagnosticDescriptor(id: "GD0001",
title: message,
messageFormat: message,
category: "Usage",
@@ -29,5 +30,333 @@ namespace Godot.SourceGenerators
cds.GetLocation(),
cds.SyntaxTree.FilePath));
}
+
+ public static void ReportNonPartialGodotScriptOuterClass(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax outerTypeDeclSyntax
+ )
+ {
+ var outerSymbol = context.Compilation
+ .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
+ .GetDeclaredSymbol(outerTypeDeclSyntax);
+
+ string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
+ namedTypeSymbol.FullQualifiedNameOmitGlobal() :
+ "type not found";
+
+ string message =
+ $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
+ $"which contains one or more subclasses of '{GodotClasses.Object}'";
+
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' and their " +
+ "containing types must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0002",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ outerTypeDeclSyntax.GetLocation(),
+ outerTypeDeclSyntax.SyntaxTree.FilePath));
+ }
+
+ public static void ReportExportedMemberIsStatic(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"Attempted to export static {(isField ? "field" : "property")}: " +
+ $"'{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Only instance fields and properties can be exported." +
+ " Remove the 'static' modifier or the '[Export]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0101",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberTypeNotSupported(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"The type of the exported {(isField ? "field" : "property")} " +
+ $"is not supported: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0102",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberIsReadOnly(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ bool isField = exportedMemberSymbol is IFieldSymbol;
+
+ string message = $"The exported {(isField ? "field" : "property")} " +
+ $"is read-only: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = isField ?
+ $"{message}. Exported fields cannot be read-only." :
+ $"{message}. Exported properties must be writable.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0103",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberIsWriteOnly(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = $"The exported property is write-only: '{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Exported properties must be readable.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0104",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportExportedMemberIsIndexer(
+ GeneratorExecutionContext context,
+ ISymbol exportedMemberSymbol
+ )
+ {
+ var locations = exportedMemberSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = $"Attempted to export indexer property: " +
+ $"'{exportedMemberSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Indexer properties can't be exported." +
+ " Remove the '[Export]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0105",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalDelegateMissingSuffix(
+ GeneratorExecutionContext context,
+ INamedTypeSymbol delegateSymbol)
+ {
+ var locations = delegateSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The name of the delegate must end with 'EventHandler': " +
+ delegateSymbol.ToDisplayString() +
+ $". Did you mean '{delegateSymbol.Name}EventHandler'?";
+
+ string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0201",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalParameterTypeNotSupported(
+ GeneratorExecutionContext context,
+ IParameterSymbol parameterSymbol)
+ {
+ var locations = parameterSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The parameter of the delegate signature of the signal " +
+ $"is not supported: '{parameterSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0202",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static void ReportSignalDelegateSignatureMustReturnVoid(
+ GeneratorExecutionContext context,
+ INamedTypeSymbol delegateSymbol)
+ {
+ var locations = delegateSymbol.Locations;
+ var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+
+ string message = "The delegate signature of the signal " +
+ $"must return void: '{delegateSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Return void or remove the '[Signal]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0203",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ location,
+ location?.SourceTree?.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
+ new DiagnosticDescriptor(id: "GD0301",
+ title: "The generic type argument must be a Variant compatible type",
+ messageFormat: "The generic type argument must be a Variant compatible type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
+
+ public static void ReportGenericTypeArgumentMustBeVariant(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol typeArgumentSymbol)
+ {
+ string message = "The generic type argument " +
+ $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
+
+ string description = $"{message}. Use a Variant compatible type as the generic type argument.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0301",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
+ new DiagnosticDescriptor(id: "GD0302",
+ title: "The generic type parameter must be annotated with the MustBeVariant attribute",
+ messageFormat: "The generic type argument must be a Variant type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+
+ public static void ReportGenericTypeParameterMustBeVariantAnnotated(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol typeArgumentSymbol)
+ {
+ string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
+
+ string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0302",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
+ new DiagnosticDescriptor(id: "GD0303",
+ title: "The generic type parameter must be annotated with the MustBeVariant attribute",
+ messageFormat: "The generic type argument must be a Variant type: {0}",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
+
+ public static void ReportTypeArgumentParentSymbolUnhandled(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode typeArgumentSyntax,
+ ISymbol parentSymbol)
+ {
+ string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
+ "that must be Variant compatible was not handled.";
+
+ string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0303",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ typeArgumentSyntax.GetLocation(),
+ typeArgumentSyntax.SyntaxTree.FilePath));
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/EventHandlerSuffixSuppressor.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/EventHandlerSuffixSuppressor.cs
new file mode 100644
index 0000000000..ddde730fa2
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/EventHandlerSuffixSuppressor.cs
@@ -0,0 +1,53 @@
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class EventHandlerSuffixSuppressor : DiagnosticSuppressor
+ {
+ private static readonly SuppressionDescriptor _descriptor = new(
+ id: "GDSP0001",
+ suppressedDiagnosticId: "CA1711",
+ justification: "Signal delegates are used in events so the naming follows the guidelines.");
+
+ public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions =>
+ ImmutableArray.Create(_descriptor);
+
+ public override void ReportSuppressions(SuppressionAnalysisContext context)
+ {
+ foreach (var diagnostic in context.ReportedDiagnostics)
+ {
+ AnalyzeDiagnostic(context, diagnostic, context.CancellationToken);
+ }
+ }
+
+ private static void AnalyzeDiagnostic(SuppressionAnalysisContext context, Diagnostic diagnostic, CancellationToken cancellationToken = default)
+ {
+ var location = diagnostic.Location;
+ var root = location.SourceTree?.GetRoot(cancellationToken);
+ var dds = root?
+ .FindNode(location.SourceSpan)
+ .DescendantNodesAndSelf()
+ .OfType<DelegateDeclarationSyntax>()
+ .FirstOrDefault();
+
+ if (dds == null)
+ return;
+
+ var semanticModel = context.GetSemanticModel(dds.SyntaxTree);
+ var delegateSymbol = semanticModel.GetDeclaredSymbol(dds, cancellationToken);
+ if (delegateSymbol == null)
+ return;
+
+ if (delegateSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false))
+ {
+ context.ReportSuppression(Suppression.Create(_descriptor, diagnostic));
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index e16f72f43a..8852b7ebad 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -1,5 +1,8 @@
+using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
+using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -13,30 +16,65 @@ namespace Godot.SourceGenerators
) => context.AnalyzerConfigOptions.GlobalOptions
.TryGetValue("build_property." + property, out value);
- private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
- {
- if (symbol == null)
- return false;
+ public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context)
+ => context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) &&
+ toggle != null &&
+ toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase);
- while (true)
+ public static bool IsGodotToolsProject(this GeneratorExecutionContext context)
+ => context.TryGetGlobalAnalyzerProperty("IsGodotToolsProject", out string? toggle) &&
+ toggle != null &&
+ toggle.Equals("true", StringComparison.OrdinalIgnoreCase);
+
+ public static bool InheritsFrom(this INamedTypeSymbol? symbol, string assemblyName, string typeFullName)
+ {
+ while (symbol != null)
{
- if (symbol.ToString() == baseName)
+ if (symbol.ContainingAssembly?.Name == assemblyName &&
+ symbol.ToString() == typeFullName)
{
return true;
}
- if (symbol.BaseType != null)
- {
- symbol = symbol.BaseType;
- continue;
- }
-
- break;
+ symbol = symbol.BaseType;
}
return false;
}
+ public static INamedTypeSymbol? GetGodotScriptNativeClass(this INamedTypeSymbol classTypeSymbol)
+ {
+ var symbol = classTypeSymbol;
+
+ while (symbol != null)
+ {
+ if (symbol.ContainingAssembly?.Name == "GodotSharp")
+ return symbol;
+
+ symbol = symbol.BaseType;
+ }
+
+ return null;
+ }
+
+ public static string? GetGodotScriptNativeClassName(this INamedTypeSymbol classTypeSymbol)
+ {
+ var nativeType = classTypeSymbol.GetGodotScriptNativeClass();
+
+ if (nativeType == null)
+ return null;
+
+ var godotClassNameAttr = nativeType.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGodotClassNameAttribute() ?? false);
+
+ string? godotClassName = null;
+
+ if (godotClassNameAttr is { ConstructorArguments: { Length: > 0 } })
+ godotClassName = godotClassNameAttr.ConstructorArguments[0].Value?.ToString();
+
+ return godotClassName ?? nativeType.Name;
+ }
+
private static bool IsGodotScriptClass(
this ClassDeclarationSyntax cds, Compilation compilation,
out INamedTypeSymbol? symbol
@@ -47,7 +85,7 @@ namespace Godot.SourceGenerators
var classTypeSymbol = sm.GetDeclaredSymbol(cds);
if (classTypeSymbol?.BaseType == null
- || !classTypeSymbol.BaseType.InheritsFrom(GodotClasses.Object))
+ || !classTypeSymbol.BaseType.InheritsFrom("GodotSharp", GodotClasses.Object))
{
symbol = null;
return false;
@@ -69,21 +107,234 @@ namespace Godot.SourceGenerators
}
}
- public static bool IsPartial(this ClassDeclarationSyntax cds)
+ public static bool IsNested(this TypeDeclarationSyntax cds)
+ => cds.Parent is TypeDeclarationSyntax;
+
+ public static bool IsPartial(this TypeDeclarationSyntax cds)
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
- public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
- => symbol.GetAttributes().Any(attr =>
- attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
+ public static bool AreAllOuterTypesPartial(
+ this TypeDeclarationSyntax cds,
+ out TypeDeclarationSyntax? typeMissingPartial
+ )
+ {
+ SyntaxNode? outerSyntaxNode = cds.Parent;
+
+ while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
+ {
+ if (!outerTypeDeclSyntax.IsPartial())
+ {
+ typeMissingPartial = outerTypeDeclSyntax;
+ return false;
+ }
+
+ outerSyntaxNode = outerSyntaxNode.Parent;
+ }
+
+ typeMissingPartial = null;
+ return true;
+ }
+
+ public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
+ .OfType<TypeDeclarationSyntax>().FirstOrDefault()?
+ .Keyword.Text;
+
+ return keyword ?? namedTypeSymbol.TypeKind switch
+ {
+ TypeKind.Interface => "interface",
+ TypeKind.Struct => "struct",
+ _ => "class"
+ };
+ }
+
+ public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsGenericType ?
+ string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
+ symbol.Name;
+ }
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
- public static string FullQualifiedName(this INamedTypeSymbol symbol)
+ private static SymbolDisplayFormat FullyQualifiedFormatIncludeGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Included);
+
+ public static string FullQualifiedNameOmitGlobal(this ITypeSymbol symbol)
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
- public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
+ public static string FullQualifiedNameOmitGlobal(this INamespaceSymbol namespaceSymbol)
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+
+ public static string FullQualifiedNameIncludeGlobal(this ITypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatIncludeGlobal);
+
+ public static string FullQualifiedNameIncludeGlobal(this INamespaceSymbol namespaceSymbol)
+ => namespaceSymbol.ToDisplayString(FullyQualifiedFormatIncludeGlobal);
+
+ public static string FullQualifiedSyntax(this SyntaxNode node, SemanticModel sm)
+ {
+ StringBuilder sb = new();
+ FullQualifiedSyntax(node, sm, sb, true);
+ return sb.ToString();
+ }
+
+ private static void FullQualifiedSyntax(SyntaxNode node, SemanticModel sm, StringBuilder sb, bool isFirstNode)
+ {
+ if (node is NameSyntax ns && isFirstNode)
+ {
+ SymbolInfo nameInfo = sm.GetSymbolInfo(ns);
+ sb.Append(nameInfo.Symbol?.ToDisplayString(FullyQualifiedFormatIncludeGlobal) ?? ns.ToString());
+ return;
+ }
+
+ bool innerIsFirstNode = true;
+ foreach (var child in node.ChildNodesAndTokens())
+ {
+ if (child.HasLeadingTrivia)
+ {
+ sb.Append(child.GetLeadingTrivia());
+ }
+
+ if (child.IsNode)
+ {
+ FullQualifiedSyntax(child.AsNode()!, sm, sb, isFirstNode: innerIsFirstNode);
+ innerIsFirstNode = false;
+ }
+ else
+ {
+ sb.Append(child);
+ }
+
+ if (child.HasTrailingTrivia)
+ {
+ sb.Append(child.GetTrailingTrivia());
+ }
+ }
+ }
+
+ public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
+ => qualifiedName
+ // AddSource() doesn't support angle brackets
+ .Replace("<", "(Of ")
+ .Replace(">", ")");
+
+ public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.ExportAttr;
+
+ public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.SignalAttr;
+
+ public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.MustBeVariantAttr;
+
+ public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.GodotClassNameAttr;
+
+ public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GodotClasses.SystemFlagsAttr;
+
+ public static GodotMethodData? HasGodotCompatibleSignature(
+ this IMethodSymbol method,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ if (method.IsGenericMethod)
+ return null;
+
+ var retSymbol = method.ReturnType;
+ var retType = method.ReturnsVoid ?
+ null :
+ MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache);
+
+ if (retType == null && !method.ReturnsVoid)
+ return null;
+
+ var parameters = method.Parameters;
+
+ var paramTypes = parameters
+ // Currently we don't support `ref`, `out`, `in`, `ref readonly` parameters (and we never may)
+ .Where(p => p.RefKind == RefKind.None)
+ // Attempt to determine the variant type
+ .Select(p => MarshalUtils.ConvertManagedTypeToMarshalType(p.Type, typeCache))
+ // Discard parameter types that couldn't be determined (null entries)
+ .Where(t => t != null).Cast<MarshalType>().ToImmutableArray();
+
+ // If any parameter type was incompatible, it was discarded so the length won't match
+ if (parameters.Length > paramTypes.Length)
+ return null; // Ignore incompatible method
+
+ return new GodotMethodData(method, paramTypes,
+ parameters.Select(p => p.Type).ToImmutableArray(),
+ retType != null ? (retType.Value, retSymbol) : null);
+ }
+
+ public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature(
+ this IEnumerable<IMethodSymbol> methods,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var method in methods)
+ {
+ var methodData = HasGodotCompatibleSignature(method, typeCache);
+
+ if (methodData != null)
+ yield return methodData.Value;
+ }
+ }
+
+ public static IEnumerable<GodotPropertyData> WhereIsGodotCompatibleType(
+ this IEnumerable<IPropertySymbol> properties,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var property in properties)
+ {
+ // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter, without a setter or with an init-only setter. Godot properties must be both readable and writable.
+ if (property.IsWriteOnly || property.IsReadOnly || property.SetMethod!.IsInitOnly)
+ continue;
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(property.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotPropertyData(property, marshalType.Value);
+ }
+ }
+
+ public static IEnumerable<GodotFieldData> WhereIsGodotCompatibleType(
+ this IEnumerable<IFieldSymbol> fields,
+ MarshalUtils.TypeCache typeCache
+ )
+ {
+ foreach (var field in fields)
+ {
+ // TODO: We should still restore read-only fields after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (field.IsReadOnly)
+ continue;
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(field.Type, typeCache);
+
+ if (marshalType == null)
+ continue;
+
+ yield return new GodotFieldData(field, marshalType.Value);
+ }
+ }
+
+ public static string Path(this Location location)
+ => location.SourceTree?.GetLineSpan(location.SourceSpan).Path
+ ?? location.GetLineSpan().Path;
+
+ public static int StartLine(this Location location)
+ => location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line
+ ?? location.GetLineSpan().StartLinePosition.Line;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
index 11d8e0f72b..f51b5970c3 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
- <LangVersion>8.0</LangVersion>
+ <LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
@@ -21,21 +21,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
- <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.1" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<!-- Package the generator in the analyzer directory of the nuget package -->
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<!-- Package the props file -->
- <None Include="Godot.SourceGenerators.props" Pack="true" PackagePath="build" Visible="false" />
+ <None Include="Godot.SourceGenerators.props" Pack="true" PackagePath="build" Visible="true" />
</ItemGroup>
-
- <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
- </PropertyGroup>
- <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
- </Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
index f9b47ad5b1..7881ed0a8c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
@@ -2,6 +2,7 @@
<ItemGroup>
<!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
<CompilerVisibleProperty Include="GodotProjectDir" />
- <CompilerVisibleProperty Include="GodotScriptPathAttributeGenerator" />
+ <CompilerVisibleProperty Include="GodotSourceGenerators" />
+ <CompilerVisibleProperty Include="IsGodotToolsProject" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
index 29e41d155a..1d8ddbabf2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
@@ -3,7 +3,14 @@ namespace Godot.SourceGenerators
public static class GodotClasses
{
public const string Object = "Godot.Object";
- public const string DisableGodotGeneratorsAttr = "Godot.DisableGodotGeneratorsAttribute";
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
+ public const string ExportAttr = "Godot.ExportAttribute";
+ public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute";
+ public const string ExportGroupAttr = "Godot.ExportGroupAttribute";
+ public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute";
+ public const string SignalAttr = "Godot.SignalAttribute";
+ public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
+ public const string GodotClassNameAttr = "Godot.GodotClassName";
+ public const string SystemFlagsAttr = "System.FlagsAttribute";
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
new file mode 100644
index 0000000000..5fb29b86da
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotEnums.cs
@@ -0,0 +1,139 @@
+using System;
+
+namespace Godot.SourceGenerators
+{
+ // TODO: May need to think about compatibility here. Could Godot change these values between minor versions?
+
+ internal enum VariantType
+ {
+ Nil = 0,
+ Bool = 1,
+ Int = 2,
+ Float = 3,
+ String = 4,
+ Vector2 = 5,
+ Vector2i = 6,
+ Rect2 = 7,
+ Rect2i = 8,
+ Vector3 = 9,
+ Vector3i = 10,
+ Transform2d = 11,
+ Vector4 = 12,
+ Vector4i = 13,
+ Plane = 14,
+ Quaternion = 15,
+ Aabb = 16,
+ Basis = 17,
+ Transform3d = 18,
+ Projection = 19,
+ Color = 20,
+ StringName = 21,
+ NodePath = 22,
+ Rid = 23,
+ Object = 24,
+ Callable = 25,
+ Signal = 26,
+ Dictionary = 27,
+ Array = 28,
+ PackedByteArray = 29,
+ PackedInt32Array = 30,
+ PackedInt64Array = 31,
+ PackedFloat32Array = 32,
+ PackedFloat64Array = 33,
+ PackedStringArray = 34,
+ PackedVector2Array = 35,
+ PackedVector3Array = 36,
+ PackedColorArray = 37,
+ Max = 38
+ }
+
+ internal enum PropertyHint
+ {
+ None = 0,
+ Range = 1,
+ Enum = 2,
+ EnumSuggestion = 3,
+ ExpEasing = 4,
+ Link = 5,
+ Flags = 6,
+ Layers2dRender = 7,
+ Layers2dPhysics = 8,
+ Layers2dNavigation = 9,
+ Layers3dRender = 10,
+ Layers3dPhysics = 11,
+ Layers3dNavigation = 12,
+ File = 13,
+ Dir = 14,
+ GlobalFile = 15,
+ GlobalDir = 16,
+ ResourceType = 17,
+ MultilineText = 18,
+ Expression = 19,
+ PlaceholderText = 20,
+ ColorNoAlpha = 21,
+ ObjectId = 22,
+ TypeString = 23,
+ NodePathToEditedNode = 24,
+ ObjectTooBig = 25,
+ NodePathValidTypes = 26,
+ SaveFile = 27,
+ GlobalSaveFile = 28,
+ IntIsObjectid = 29,
+ IntIsPointer = 30,
+ ArrayType = 31,
+ LocaleId = 32,
+ LocalizableString = 33,
+ NodeType = 34,
+ HideQuaternionEdit = 35,
+ Password = 36,
+ Max = 37
+ }
+
+ [Flags]
+ internal enum PropertyUsageFlags
+ {
+ None = 0,
+ Storage = 2,
+ Editor = 4,
+ Internal = 8,
+ Checkable = 16,
+ Checked = 32,
+ Group = 64,
+ Category = 128,
+ Subgroup = 256,
+ ClassIsBitfield = 512,
+ NoInstanceState = 1024,
+ RestartIfChanged = 2048,
+ ScriptVariable = 4096,
+ StoreIfNull = 8192,
+ UpdateAllIfModified = 16384,
+ ScriptDefaultValue = 32768,
+ ClassIsEnum = 65536,
+ NilIsVariant = 131072,
+ Array = 262144,
+ DoNotShareOnDuplicate = 524288,
+ HighEndGfx = 1048576,
+ NodePathFromSceneRoot = 2097152,
+ ResourceNotPersistent = 4194304,
+ KeyingIncrements = 8388608,
+ DeferredSetResource = 16777216,
+ EditorInstantiateObject = 33554432,
+ EditorBasicSetting = 67108864,
+ ReadOnly = 134217728,
+ Default = 6,
+ NoEditor = 2
+ }
+
+ [Flags]
+ public enum MethodFlags
+ {
+ Normal = 1,
+ Editor = 2,
+ Const = 4,
+ Virtual = 8,
+ Vararg = 16,
+ Static = 32,
+ ObjectCore = 64,
+ Default = 1
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
new file mode 100644
index 0000000000..0760ea11bb
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs
@@ -0,0 +1,82 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ public readonly struct GodotMethodData
+ {
+ public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
+ ImmutableArray<ITypeSymbol> paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType)
+ {
+ Method = method;
+ ParamTypes = paramTypes;
+ ParamTypeSymbols = paramTypeSymbols;
+ RetType = retType;
+ }
+
+ public IMethodSymbol Method { get; }
+ public ImmutableArray<MarshalType> ParamTypes { get; }
+ public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
+ public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; }
+ }
+
+ public readonly struct GodotSignalDelegateData
+ {
+ public GodotSignalDelegateData(string name, INamedTypeSymbol delegateSymbol, GodotMethodData invokeMethodData)
+ {
+ Name = name;
+ DelegateSymbol = delegateSymbol;
+ InvokeMethodData = invokeMethodData;
+ }
+
+ public string Name { get; }
+ public INamedTypeSymbol DelegateSymbol { get; }
+ public GodotMethodData InvokeMethodData { get; }
+ }
+
+ public readonly struct GodotPropertyData
+ {
+ public GodotPropertyData(IPropertySymbol propertySymbol, MarshalType type)
+ {
+ PropertySymbol = propertySymbol;
+ Type = type;
+ }
+
+ public IPropertySymbol PropertySymbol { get; }
+ public MarshalType Type { get; }
+ }
+
+ public readonly struct GodotFieldData
+ {
+ public GodotFieldData(IFieldSymbol fieldSymbol, MarshalType type)
+ {
+ FieldSymbol = fieldSymbol;
+ Type = type;
+ }
+
+ public IFieldSymbol FieldSymbol { get; }
+ public MarshalType Type { get; }
+ }
+
+ public struct GodotPropertyOrFieldData
+ {
+ public GodotPropertyOrFieldData(ISymbol symbol, MarshalType type)
+ {
+ Symbol = symbol;
+ Type = type;
+ }
+
+ public GodotPropertyOrFieldData(GodotPropertyData propertyData)
+ : this(propertyData.PropertySymbol, propertyData.Type)
+ {
+ }
+
+ public GodotPropertyOrFieldData(GodotFieldData fieldData)
+ : this(fieldData.FieldSymbol, fieldData.Type)
+ {
+ }
+
+ public ISymbol Symbol { get; }
+ public MarshalType Type { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs
new file mode 100644
index 0000000000..19fdd51dab
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs
@@ -0,0 +1,63 @@
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class GodotPluginsInitializerGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.IsGodotToolsProject())
+ return;
+
+ string source =
+ @"using System;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+namespace GodotPlugins.Game
+{
+ internal static partial class Main
+ {
+ [UnmanagedCallersOnly(EntryPoint = ""godotsharp_game_main_init"")]
+ private static godot_bool InitializeFromGameProject(IntPtr godotDllHandle, IntPtr outManagedCallbacks,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ DllImportResolver dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
+
+ var coreApiAssembly = typeof(Godot.Object).Assembly;
+
+ NativeLibrary.SetDllImportResolver(coreApiAssembly, dllImportResolver);
+
+ NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+
+ ManagedCallbacks.Create(outManagedCallbacks);
+
+ ScriptManagerBridge.LookupScriptsInAssembly(typeof(GodotPlugins.Game.Main).Assembly);
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return false.ToGodotBool();
+ }
+ }
+ }
+}
+";
+
+ context.AddSource("GodotPlugins.Game.generated",
+ SourceText.From(source, Encoding.UTF8));
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
new file mode 100644
index 0000000000..ee1374d0b9
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalType.cs
@@ -0,0 +1,73 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum MarshalType
+ {
+ Boolean,
+ Char,
+ SByte,
+ Int16,
+ Int32,
+ Int64,
+ Byte,
+ UInt16,
+ UInt32,
+ UInt64,
+ Single,
+ Double,
+ String,
+
+ // Godot structs
+ Vector2,
+ Vector2i,
+ Rect2,
+ Rect2i,
+ Transform2D,
+ Vector3,
+ Vector3i,
+ Basis,
+ Quaternion,
+ Transform3D,
+ Vector4,
+ Vector4i,
+ Projection,
+ AABB,
+ Color,
+ Plane,
+ Callable,
+ Signal,
+
+ // Enums
+ Enum,
+
+ // Arrays
+ ByteArray,
+ Int32Array,
+ Int64Array,
+ Float32Array,
+ Float64Array,
+ StringArray,
+ Vector2Array,
+ Vector3Array,
+ ColorArray,
+ GodotObjectOrDerivedArray,
+ SystemArrayOfStringName,
+ SystemArrayOfNodePath,
+ SystemArrayOfRID,
+
+ // Variant
+ Variant,
+
+ // Classes
+ GodotObjectOrDerived,
+ StringName,
+ NodePath,
+ RID,
+ GodotDictionary,
+ GodotArray,
+ GodotGenericDictionary,
+ GodotGenericArray,
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
new file mode 100644
index 0000000000..f0a6a72281
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
@@ -0,0 +1,383 @@
+using System;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ internal static class MarshalUtils
+ {
+ public class TypeCache
+ {
+ public INamedTypeSymbol GodotObjectType { get; }
+
+ public TypeCache(Compilation compilation)
+ {
+ INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
+ {
+ return compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
+ throw new InvalidOperationException($"Type not found: '{fullyQualifiedMetadataName}'.");
+ }
+
+ GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object");
+ }
+ }
+
+ public static VariantType? ConvertMarshalTypeToVariantType(MarshalType marshalType)
+ => marshalType switch
+ {
+ MarshalType.Boolean => VariantType.Bool,
+ MarshalType.Char => VariantType.Int,
+ MarshalType.SByte => VariantType.Int,
+ MarshalType.Int16 => VariantType.Int,
+ MarshalType.Int32 => VariantType.Int,
+ MarshalType.Int64 => VariantType.Int,
+ MarshalType.Byte => VariantType.Int,
+ MarshalType.UInt16 => VariantType.Int,
+ MarshalType.UInt32 => VariantType.Int,
+ MarshalType.UInt64 => VariantType.Int,
+ MarshalType.Single => VariantType.Float,
+ MarshalType.Double => VariantType.Float,
+ MarshalType.String => VariantType.String,
+ MarshalType.Vector2 => VariantType.Vector2,
+ MarshalType.Vector2i => VariantType.Vector2i,
+ MarshalType.Rect2 => VariantType.Rect2,
+ MarshalType.Rect2i => VariantType.Rect2i,
+ MarshalType.Transform2D => VariantType.Transform2d,
+ MarshalType.Vector3 => VariantType.Vector3,
+ MarshalType.Vector3i => VariantType.Vector3i,
+ MarshalType.Basis => VariantType.Basis,
+ MarshalType.Quaternion => VariantType.Quaternion,
+ MarshalType.Transform3D => VariantType.Transform3d,
+ MarshalType.Vector4 => VariantType.Vector4,
+ MarshalType.Vector4i => VariantType.Vector4i,
+ MarshalType.Projection => VariantType.Projection,
+ MarshalType.AABB => VariantType.Aabb,
+ MarshalType.Color => VariantType.Color,
+ MarshalType.Plane => VariantType.Plane,
+ MarshalType.Callable => VariantType.Callable,
+ MarshalType.Signal => VariantType.Signal,
+ MarshalType.Enum => VariantType.Int,
+ MarshalType.ByteArray => VariantType.PackedByteArray,
+ MarshalType.Int32Array => VariantType.PackedInt32Array,
+ MarshalType.Int64Array => VariantType.PackedInt64Array,
+ MarshalType.Float32Array => VariantType.PackedFloat32Array,
+ MarshalType.Float64Array => VariantType.PackedFloat64Array,
+ MarshalType.StringArray => VariantType.PackedStringArray,
+ MarshalType.Vector2Array => VariantType.PackedVector2Array,
+ MarshalType.Vector3Array => VariantType.PackedVector3Array,
+ MarshalType.ColorArray => VariantType.PackedColorArray,
+ MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
+ MarshalType.SystemArrayOfStringName => VariantType.Array,
+ MarshalType.SystemArrayOfNodePath => VariantType.Array,
+ MarshalType.SystemArrayOfRID => VariantType.Array,
+ MarshalType.Variant => VariantType.Nil,
+ MarshalType.GodotObjectOrDerived => VariantType.Object,
+ MarshalType.StringName => VariantType.StringName,
+ MarshalType.NodePath => VariantType.NodePath,
+ MarshalType.RID => VariantType.Rid,
+ MarshalType.GodotDictionary => VariantType.Dictionary,
+ MarshalType.GodotArray => VariantType.Array,
+ MarshalType.GodotGenericDictionary => VariantType.Dictionary,
+ MarshalType.GodotGenericArray => VariantType.Array,
+ _ => null
+ };
+
+ public static MarshalType? ConvertManagedTypeToMarshalType(ITypeSymbol type, TypeCache typeCache)
+ {
+ var specialType = type.SpecialType;
+
+ switch (specialType)
+ {
+ case SpecialType.System_Boolean:
+ return MarshalType.Boolean;
+ case SpecialType.System_Char:
+ return MarshalType.Char;
+ case SpecialType.System_SByte:
+ return MarshalType.SByte;
+ case SpecialType.System_Int16:
+ return MarshalType.Int16;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64;
+ case SpecialType.System_Byte:
+ return MarshalType.Byte;
+ case SpecialType.System_UInt16:
+ return MarshalType.UInt16;
+ case SpecialType.System_UInt32:
+ return MarshalType.UInt32;
+ case SpecialType.System_UInt64:
+ return MarshalType.UInt64;
+ case SpecialType.System_Single:
+ return MarshalType.Single;
+ case SpecialType.System_Double:
+ return MarshalType.Double;
+ case SpecialType.System_String:
+ return MarshalType.String;
+ default:
+ {
+ var typeKind = type.TypeKind;
+
+ if (typeKind == TypeKind.Enum)
+ return MarshalType.Enum;
+
+ if (typeKind == TypeKind.Struct)
+ {
+ if (type.ContainingAssembly?.Name == "GodotSharp" &&
+ type.ContainingNamespace?.Name == "Godot")
+ {
+ return type switch
+ {
+ { Name: "Vector2" } => MarshalType.Vector2,
+ { Name: "Vector2i" } => MarshalType.Vector2i,
+ { Name: "Rect2" } => MarshalType.Rect2,
+ { Name: "Rect2i" } => MarshalType.Rect2i,
+ { Name: "Transform2D" } => MarshalType.Transform2D,
+ { Name: "Vector3" } => MarshalType.Vector3,
+ { Name: "Vector3i" } => MarshalType.Vector3i,
+ { Name: "Basis" } => MarshalType.Basis,
+ { Name: "Quaternion" } => MarshalType.Quaternion,
+ { Name: "Transform3D" } => MarshalType.Transform3D,
+ { Name: "Vector4" } => MarshalType.Vector4,
+ { Name: "Vector4i" } => MarshalType.Vector4i,
+ { Name: "Projection" } => MarshalType.Projection,
+ { Name: "AABB" } => MarshalType.AABB,
+ { Name: "Color" } => MarshalType.Color,
+ { Name: "Plane" } => MarshalType.Plane,
+ { Name: "RID" } => MarshalType.RID,
+ { Name: "Callable" } => MarshalType.Callable,
+ { Name: "Signal" } => MarshalType.Signal,
+ { Name: "Variant" } => MarshalType.Variant,
+ _ => null
+ };
+ }
+ }
+ else if (typeKind == TypeKind.Array)
+ {
+ var arrayType = (IArrayTypeSymbol)type;
+
+ if (arrayType.Rank != 1)
+ return null;
+
+ var elementType = arrayType.ElementType;
+
+ switch (elementType.SpecialType)
+ {
+ case SpecialType.System_Byte:
+ return MarshalType.ByteArray;
+ case SpecialType.System_Int32:
+ return MarshalType.Int32Array;
+ case SpecialType.System_Int64:
+ return MarshalType.Int64Array;
+ case SpecialType.System_Single:
+ return MarshalType.Float32Array;
+ case SpecialType.System_Double:
+ return MarshalType.Float64Array;
+ case SpecialType.System_String:
+ return MarshalType.StringArray;
+ }
+
+ if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerivedArray;
+
+ if (elementType.ContainingAssembly?.Name == "GodotSharp" &&
+ elementType.ContainingNamespace?.Name == "Godot")
+ {
+ switch (elementType)
+ {
+ case { Name: "Vector2" }:
+ return MarshalType.Vector2Array;
+ case { Name: "Vector3" }:
+ return MarshalType.Vector3Array;
+ case { Name: "Color" }:
+ return MarshalType.ColorArray;
+ case { Name: "StringName" }:
+ return MarshalType.SystemArrayOfStringName;
+ case { Name: "NodePath" }:
+ return MarshalType.SystemArrayOfNodePath;
+ case { Name: "RID" }:
+ return MarshalType.SystemArrayOfRID;
+ }
+ }
+
+ return null;
+ }
+ else
+ {
+ if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
+ return MarshalType.GodotObjectOrDerived;
+
+ if (type.ContainingAssembly?.Name == "GodotSharp")
+ {
+ switch (type.ContainingNamespace?.Name)
+ {
+ case "Godot":
+ return type switch
+ {
+ { Name: "StringName" } => MarshalType.StringName,
+ { Name: "NodePath" } => MarshalType.NodePath,
+ _ => null
+ };
+ case "Collections"
+ when type.ContainingNamespace?.FullQualifiedNameOmitGlobal() == "Godot.Collections":
+ return type switch
+ {
+ { Name: "Dictionary" } =>
+ type is INamedTypeSymbol { IsGenericType: false } ?
+ MarshalType.GodotDictionary :
+ MarshalType.GodotGenericDictionary,
+ { Name: "Array" } =>
+ type is INamedTypeSymbol { IsGenericType: false } ?
+ MarshalType.GodotArray :
+ MarshalType.GodotGenericArray,
+ _ => null
+ };
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
+ {
+ while (type != null)
+ {
+ if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
+ return true;
+
+ type = type.BaseType;
+ }
+
+ return false;
+ }
+
+ public static ITypeSymbol? GetArrayElementType(ITypeSymbol typeSymbol)
+ {
+ if (typeSymbol.TypeKind == TypeKind.Array)
+ {
+ var arrayType = (IArrayTypeSymbol)typeSymbol;
+ return arrayType.ElementType;
+ }
+
+ if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
+ return genericType.TypeArguments.FirstOrDefault();
+
+ return null;
+ }
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b)
+ => source.Append(a).Append(b);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b, string c)
+ => source.Append(a).Append(b).Append(c);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d)
+ => source.Append(a).Append(b).Append(c).Append(d);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f, string g)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g);
+
+ private static StringBuilder Append(this StringBuilder source, string a, string b,
+ string c, string d, string e, string f, string g, string h)
+ => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h);
+
+ private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils";
+
+ public static StringBuilder AppendNativeVariantToManagedExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ // We need a special case for GodotObjectOrDerived[], because it's not supported by VariantUtils.ConvertTo<T>
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
+ ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
+ // We need a special case for generic Godot collections and GodotObjectOrDerived[], because VariantUtils.ConvertTo<T> is slower
+ MarshalType.GodotGenericDictionary =>
+ source.Append(VariantUtils, ".ConvertToDictionary<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
+ MarshalType.GodotGenericArray =>
+ source.Append(VariantUtils, ".ConvertToArray<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(",
+ inputExpr, ")"),
+ _ => source.Append(VariantUtils, ".ConvertTo<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
+ };
+ }
+
+ public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ // We need a special case for GodotObjectOrDerived[], because it's not supported by VariantUtils.CreateFrom<T>
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
+ // We need a special case for generic Godot collections and GodotObjectOrDerived[], because VariantUtils.CreateFrom<T> is slower
+ MarshalType.GodotGenericDictionary =>
+ source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
+ MarshalType.GodotGenericArray =>
+ source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
+ _ => source.Append(VariantUtils, ".CreateFrom<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
+ };
+ }
+
+ public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ // We need a special case for GodotObjectOrDerived[], because it's not supported by Variant.As<T>
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append(inputExpr, ".AsGodotObjectArray<",
+ ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"),
+ // We need a special case for generic Godot collections and GodotObjectOrDerived[], because Variant.As<T> is slower
+ MarshalType.GodotGenericDictionary =>
+ source.Append(inputExpr, ".AsGodotDictionary<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
+ MarshalType.GodotGenericArray =>
+ source.Append(inputExpr, ".AsGodotArray<",
+ ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
+ _ => source.Append(inputExpr, ".As<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">()")
+ };
+ }
+
+ public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
+ string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
+ {
+ return marshalType switch
+ {
+ // We need a special case for GodotObjectOrDerived[], because it's not supported by Variant.From<T>
+ MarshalType.GodotObjectOrDerivedArray =>
+ source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
+ // We need a special case for generic Godot collections, because Variant.From<T> is slower
+ MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray =>
+ source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
+ _ => source.Append("global::Godot.Variant.From<",
+ typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")")
+ };
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs
new file mode 100644
index 0000000000..bb9be862c4
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MethodInfo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Godot.SourceGenerators
+{
+ internal readonly struct MethodInfo
+ {
+ public MethodInfo(string name, PropertyInfo returnVal, MethodFlags flags,
+ List<PropertyInfo>? arguments,
+ List<string?>? defaultArguments)
+ {
+ Name = name;
+ ReturnVal = returnVal;
+ Flags = flags;
+ Arguments = arguments;
+ DefaultArguments = defaultArguments;
+ }
+
+ public string Name { get; }
+ public PropertyInfo ReturnVal { get; }
+ public MethodFlags Flags { get; }
+ public List<PropertyInfo>? Arguments { get; }
+ public List<string?>? DefaultArguments { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
new file mode 100644
index 0000000000..98ca534c66
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -0,0 +1,105 @@
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class MustBeVariantAnalyzer : DiagnosticAnalyzer
+ {
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
+ => ImmutableArray.Create(
+ Common.GenericTypeArgumentMustBeVariantRule,
+ Common.GenericTypeParameterMustBeVariantAnnotatedRule,
+ Common.TypeArgumentParentSymbolUnhandledRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.TypeArgumentList);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var typeArgListSyntax = (TypeArgumentListSyntax)context.Node;
+
+ // Method invocation or variable declaration that contained the type arguments
+ var parentSyntax = context.Node.Parent;
+ Debug.Assert(parentSyntax != null);
+
+ var sm = context.SemanticModel;
+
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++)
+ {
+ var typeSyntax = typeArgListSyntax.Arguments[i];
+
+ // Ignore omitted type arguments, e.g.: List<>, Dictionary<,>, etc
+ if (typeSyntax is OmittedTypeArgumentSyntax)
+ continue;
+
+ var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol;
+ Debug.Assert(typeSymbol != null);
+
+ var parentSymbol = sm.GetSymbolInfo(parentSyntax).Symbol;
+
+ if (!ShouldCheckTypeArgument(context, parentSyntax, parentSymbol, typeSyntax, typeSymbol, i))
+ {
+ return;
+ }
+
+ if (typeSymbol is ITypeParameterSymbol typeParamSymbol)
+ {
+ if (!typeParamSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false))
+ {
+ Common.ReportGenericTypeParameterMustBeVariantAnnotated(context, typeSyntax, typeSymbol);
+ }
+ continue;
+ }
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(typeSymbol, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportGenericTypeArgumentMustBeVariant(context, typeSyntax, typeSymbol);
+ continue;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Check if the given type argument is being used in a type parameter that contains
+ /// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute.
+ /// </summary>
+ /// <param name="context">Context for a syntax node action.</param>
+ /// <param name="parentSyntax">The parent node syntax that contains the type node syntax.</param>
+ /// <param name="parentSymbol">The symbol retrieved for the parent node syntax.</param>
+ /// <param name="typeArgumentSyntax">The type node syntax of the argument type to check.</param>
+ /// <param name="typeArgumentSymbol">The symbol retrieved for the type node syntax.</param>
+ /// <returns><see langword="true"/> if the type must be variant and must be analyzed.</returns>
+ private bool ShouldCheckTypeArgument(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntax, ISymbol parentSymbol, TypeSyntax typeArgumentSyntax, ITypeSymbol typeArgumentSymbol, int typeArgumentIndex)
+ {
+ var typeParamSymbol = parentSymbol switch
+ {
+ IMethodSymbol methodSymbol => methodSymbol.TypeParameters[typeArgumentIndex],
+ INamedTypeSymbol typeSymbol => typeSymbol.TypeParameters[typeArgumentIndex],
+ _ => null,
+ };
+
+ if (typeParamSymbol == null)
+ {
+ Common.ReportTypeArgumentParentSymbolUnhandled(context, typeArgumentSyntax, parentSymbol);
+ return false;
+ }
+
+ return typeParamSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false);
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs
new file mode 100644
index 0000000000..2b89633ef6
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/PropertyInfo.cs
@@ -0,0 +1,23 @@
+namespace Godot.SourceGenerators
+{
+ internal readonly struct PropertyInfo
+ {
+ public PropertyInfo(VariantType type, string name, PropertyHint hint,
+ string? hintString, PropertyUsageFlags usage, bool exported)
+ {
+ Type = type;
+ Name = name;
+ Hint = hint;
+ HintString = hintString;
+ Usage = usage;
+ Exported = exported;
+ }
+
+ public VariantType Type { get; }
+ public string Name { get; }
+ public PropertyHint Hint { get; }
+ public string? HintString { get; }
+ public PropertyUsageFlags Usage { get; }
+ public bool Exported { get; }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
new file mode 100644
index 0000000000..f79909589e
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -0,0 +1,410 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptMethodsGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private class MethodOverloadEqualityComparer : IEqualityComparer<GodotMethodData>
+ {
+ public bool Equals(GodotMethodData x, GodotMethodData y)
+ => x.ParamTypes.Length == y.ParamTypes.Length && x.Method.Name == y.Method.Name;
+
+ public int GetHashCode(GodotMethodData obj)
+ {
+ unchecked
+ {
+ return (obj.ParamTypes.Length.GetHashCode() * 397) ^ obj.Method.Name.GetHashCode();
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptMethods.generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var methodSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Method && !s.IsImplicitlyDeclared)
+ .Cast<IMethodSymbol>()
+ .Where(m => m.MethodKind == MethodKind.Ordinary);
+
+ var godotClassMethods = methodSymbols.WhereHasGodotCompatibleSignature(typeCache)
+ .Distinct(new MethodOverloadEqualityComparer())
+ .ToArray();
+
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append(
+ $" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ var distinctMethodNames = godotClassMethods
+ .Select(m => m.Method.Name)
+ .Distinct()
+ .ToArray();
+
+ foreach (string methodName in distinctMethodNames)
+ {
+ source.Append(" public new static readonly global::Godot.StringName ");
+ source.Append(methodName);
+ source.Append(" = \"");
+ source.Append(methodName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ // Generate GetGodotMethodList
+
+ if (godotClassMethods.Length > 0)
+ {
+ const string listType = "global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+
+ source.Append(" internal new static ")
+ .Append(listType)
+ .Append(" GetGodotMethodList()\n {\n");
+
+ source.Append(" var methods = new ")
+ .Append(listType)
+ .Append("(")
+ .Append(godotClassMethods.Length)
+ .Append(");\n");
+
+ foreach (var method in godotClassMethods)
+ {
+ var methodInfo = DetermineMethodInfo(method);
+ AppendMethodInfo(source, methodInfo);
+ }
+
+ source.Append(" return methods;\n");
+ source.Append(" }\n");
+ }
+
+ source.Append("#pragma warning restore CS0109\n");
+
+ // Generate InvokeGodotClassMethod
+
+ if (godotClassMethods.Length > 0)
+ {
+ source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
+ source.Append("NativeVariantPtrArgs args, out godot_variant ret)\n {\n");
+
+ foreach (var method in godotClassMethods)
+ {
+ GenerateMethodInvoker(method, source);
+ }
+
+ source.Append(" return base.InvokeGodotClassMethod(method, args, out ret);\n");
+
+ source.Append(" }\n");
+ }
+
+ // Generate HasGodotClassMethod
+
+ if (distinctMethodNames.Length > 0)
+ {
+ source.Append(" protected override bool HasGodotClassMethod(in godot_string_name method)\n {\n");
+
+ bool isFirstEntry = true;
+ foreach (string methodName in distinctMethodNames)
+ {
+ GenerateHasMethodEntry(methodName, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.HasGodotClassMethod(method);\n");
+
+ source.Append(" }\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+ {
+ source.Append(" methods.Add(new(name: MethodName.")
+ .Append(methodInfo.Name)
+ .Append(", returnVal: ");
+
+ AppendPropertyInfo(source, methodInfo.ReturnVal);
+
+ source.Append(", flags: (global::Godot.MethodFlags)")
+ .Append((int)methodInfo.Flags)
+ .Append(", arguments: ");
+
+ if (methodInfo.Arguments is { Count: > 0 })
+ {
+ source.Append("new() { ");
+
+ foreach (var param in methodInfo.Arguments)
+ {
+ AppendPropertyInfo(source, param);
+
+ // C# allows colon after the last element
+ source.Append(", ");
+ }
+
+ source.Append(" }");
+ }
+ else
+ {
+ source.Append("null");
+ }
+
+ source.Append(", defaultArguments: null));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append("new(type: (global::Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (global::Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (global::Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append(")");
+ }
+
+ private static MethodInfo DetermineMethodInfo(GodotMethodData method)
+ {
+ PropertyInfo returnVal;
+
+ if (method.RetType != null)
+ {
+ returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty);
+ }
+ else
+ {
+ returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.Default, exported: false);
+ }
+
+ int paramCount = method.ParamTypes.Length;
+
+ List<PropertyInfo>? arguments;
+
+ if (paramCount > 0)
+ {
+ arguments = new(capacity: paramCount);
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ arguments.Add(DeterminePropertyInfo(method.ParamTypes[i],
+ name: method.Method.Parameters[i].Name));
+ }
+ }
+ else
+ {
+ arguments = null;
+ }
+
+ return new MethodInfo(method.Method.Name, returnVal, MethodFlags.Default, arguments,
+ defaultArguments: null);
+ }
+
+ private static PropertyInfo DeterminePropertyInfo(MarshalType marshalType, string name)
+ {
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+
+ var propUsage = PropertyUsageFlags.Default;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, name,
+ PropertyHint.None, string.Empty, propUsage, exported: false);
+ }
+
+ private static void GenerateHasMethodEntry(
+ string methodName,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+ if (!isFirstEntry)
+ source.Append("else ");
+ source.Append("if (method == MethodName.");
+ source.Append(methodName);
+ source.Append(") {\n return true;\n }\n");
+ }
+
+ private static void GenerateMethodInvoker(
+ GodotMethodData method,
+ StringBuilder source
+ )
+ {
+ string methodName = method.Method.Name;
+
+ source.Append(" if (method == MethodName.");
+ source.Append(methodName);
+ source.Append(" && args.Count == ");
+ source.Append(method.ParamTypes.Length);
+ source.Append(") {\n");
+
+ if (method.RetType != null)
+ source.Append(" var callRet = ");
+ else
+ source.Append(" ");
+
+ source.Append(methodName);
+ source.Append("(");
+
+ for (int i = 0; i < method.ParamTypes.Length; i++)
+ {
+ if (i != 0)
+ source.Append(", ");
+
+ source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
+ method.ParamTypeSymbols[i], method.ParamTypes[i]);
+ }
+
+ source.Append(");\n");
+
+ if (method.RetType != null)
+ {
+ source.Append(" ret = ");
+
+ source.AppendManagedToNativeVariantExpr("callRet",
+ method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType);
+ source.Append(";\n");
+
+ source.Append(" return true;\n");
+ }
+ else
+ {
+ source.Append(" ret = default;\n");
+ source.Append(" return true;\n");
+ }
+
+ source.Append(" }\n");
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index fa65595290..eae7e41da8 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -14,13 +14,13 @@ namespace Godot.SourceGenerators
{
public void Execute(GeneratorExecutionContext context)
{
- if (context.TryGetGlobalAnalyzerProperty("GodotScriptPathAttributeGenerator", out string? toggle)
- && toggle == "disabled")
- {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ if (context.IsGodotToolsProject())
return;
- }
- // NOTE: IsNullOrEmpty doesn't work well with nullable checks
+ // NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
|| godotProjectDir!.Length == 0)
@@ -28,24 +28,28 @@ namespace Godot.SourceGenerators
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
}
- var godotClasses = context.Compilation.SyntaxTrees
+ Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses = context
+ .Compilation.SyntaxTrees
.SelectMany(tree =>
tree.GetRoot().DescendantNodes()
.OfType<ClassDeclarationSyntax>()
// Ignore inner classes
- .Where(cds => !(cds.Parent is ClassDeclarationSyntax))
+ .Where(cds => !cds.IsNested())
.SelectGodotScriptClasses(context.Compilation)
// Report and skip non-partial classes
.Where(x =>
{
- if (x.cds.IsPartial() || x.symbol.HasDisableGeneratorsAttribute())
+ if (x.cds.IsPartial())
return true;
Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
return false;
})
)
- // Ignore classes whose name is not the same as the file name
- .Where(x => Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name)
+ .Where(x =>
+ // Ignore classes whose name is not the same as the file name
+ Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name &&
+ // Ignore generic classes
+ !x.symbol.IsGenericType)
.GroupBy(x => x.symbol)
.ToDictionary(g => g.Key, g => g.Select(x => x.cds));
@@ -89,21 +93,14 @@ namespace Godot.SourceGenerators
attributes.Append(@""")]");
}
- string className = symbol.Name;
-
INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
- namespaceSymbol.FullQualifiedName() :
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
string.Empty;
bool hasNamespace = classNs.Length != 0;
- var uniqueName = new StringBuilder();
- if (hasNamespace)
- uniqueName.Append($"{classNs}.");
- uniqueName.Append(className);
- if (symbol.IsGenericType)
- uniqueName.Append($"Of{string.Join(string.Empty, symbol.TypeParameters)}");
- uniqueName.Append("_ScriptPath_Generated");
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptPath.generated";
var source = new StringBuilder();
@@ -123,10 +120,8 @@ namespace Godot.SourceGenerators
}
source.Append(attributes);
- source.Append("\n partial class ");
- source.Append(className);
- if (symbol.IsGenericType)
- source.Append($"<{string.Join(", ", symbol.TypeParameters)}>");
+ source.Append("\npartial class ");
+ source.Append(symbol.NameWithTypeParameters());
source.Append("\n{\n}\n");
if (hasNamespace)
@@ -134,7 +129,7 @@ namespace Godot.SourceGenerators
source.Append("\n}\n");
}
- context.AddSource(uniqueName.ToString(), SourceText.From(source.ToString(), Encoding.UTF8));
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
}
private static void AddScriptTypesAssemblyAttr(GeneratorExecutionContext context,
@@ -158,14 +153,12 @@ namespace Godot.SourceGenerators
first = false;
sourceBuilder.Append("typeof(");
sourceBuilder.Append(qualifiedName);
- if (godotClass.Key.IsGenericType)
- sourceBuilder.Append($"<{new string(',', godotClass.Key.TypeParameters.Count() - 1)}>");
sourceBuilder.Append(")");
}
sourceBuilder.Append("})]\n");
- context.AddSource("AssemblyScriptTypes_Generated",
+ context.AddSource("AssemblyScriptTypes.generated",
SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
new file mode 100644
index 0000000000..b64b843b7c
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -0,0 +1,662 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptPropertiesGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptProperties.generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var propertySymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>()
+ .Where(s => !s.IsIndexer);
+
+ var fieldSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>();
+
+ var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+ var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append(
+ $" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+ source.Append(" public new static readonly global::Godot.StringName ");
+ source.Append(propertyName);
+ source.Append(" = \"");
+ source.Append(propertyName);
+ source.Append("\";\n");
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+ source.Append(" public new static readonly global::Godot.StringName ");
+ source.Append(fieldName);
+ source.Append(" = \"");
+ source.Append(fieldName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ if (godotClassProperties.Length > 0 || godotClassFields.Length > 0)
+ {
+ bool isFirstEntry;
+
+ // Generate SetGodotClassPropertyValue
+
+ bool allPropertiesAreReadOnly = godotClassFields.All(fi => fi.FieldSymbol.IsReadOnly) &&
+ godotClassProperties.All(pi => pi.PropertySymbol.IsReadOnly || pi.PropertySymbol.SetMethod!.IsInitOnly);
+
+ if (!allPropertiesAreReadOnly)
+ {
+ source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("in godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ if (property.PropertySymbol.IsReadOnly || property.PropertySymbol.SetMethod!.IsInitOnly)
+ continue;
+
+ GeneratePropertySetter(property.PropertySymbol.Name,
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ if (field.FieldSymbol.IsReadOnly)
+ continue;
+
+ GeneratePropertySetter(field.FieldSymbol.Name,
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
+
+ source.Append(" }\n");
+ }
+
+ // Generate GetGodotClassPropertyValue
+
+ source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
+ source.Append("out godot_variant value)\n {\n");
+
+ isFirstEntry = true;
+ foreach (var property in godotClassProperties)
+ {
+ GeneratePropertyGetter(property.PropertySymbol.Name,
+ property.PropertySymbol.Type, property.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ foreach (var field in godotClassFields)
+ {
+ GeneratePropertyGetter(field.FieldSymbol.Name,
+ field.FieldSymbol.Type, field.Type, source, isFirstEntry);
+ isFirstEntry = false;
+ }
+
+ source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
+
+ source.Append(" }\n");
+
+ // Generate GetGodotPropertyList
+
+ string dictionaryType = "global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
+
+ source.Append(" internal new static ")
+ .Append(dictionaryType)
+ .Append(" GetGodotPropertyList()\n {\n");
+
+ source.Append(" var properties = new ")
+ .Append(dictionaryType)
+ .Append("();\n");
+
+ // To retain the definition order (and display categories correctly), we want to
+ // iterate over fields and properties at the same time, sorted by line number.
+ var godotClassPropertiesAndFields = Enumerable.Empty<GodotPropertyOrFieldData>()
+ .Concat(godotClassProperties.Select(propertyData => new GodotPropertyOrFieldData(propertyData)))
+ .Concat(godotClassFields.Select(fieldData => new GodotPropertyOrFieldData(fieldData)))
+ .OrderBy(data => data.Symbol.Locations[0].Path())
+ .ThenBy(data => data.Symbol.Locations[0].StartLine());
+
+ foreach (var member in godotClassPropertiesAndFields)
+ {
+ foreach (var groupingInfo in DetermineGroupingPropertyInfo(member.Symbol))
+ AppendGroupingPropertyInfo(source, groupingInfo);
+
+ var propertyInfo = DeterminePropertyInfo(context, typeCache,
+ member.Symbol, member.Type);
+
+ if (propertyInfo == null)
+ continue;
+
+ AppendPropertyInfo(source, propertyInfo.Value);
+ }
+
+ source.Append(" return properties;\n");
+ source.Append(" }\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void GeneratePropertySetter(
+ string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == PropertyName.")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" this.")
+ .Append(propertyMemberName)
+ .Append(" = ")
+ .AppendNativeVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
+ private static void GeneratePropertyGetter(
+ string propertyMemberName,
+ ITypeSymbol propertyTypeSymbol,
+ MarshalType propertyMarshalType,
+ StringBuilder source,
+ bool isFirstEntry
+ )
+ {
+ source.Append(" ");
+
+ if (!isFirstEntry)
+ source.Append("else ");
+
+ source.Append("if (name == PropertyName.")
+ .Append(propertyMemberName)
+ .Append(") {\n")
+ .Append(" value = ")
+ .AppendManagedToNativeVariantExpr("this." + propertyMemberName,
+ propertyTypeSymbol, propertyMarshalType)
+ .Append(";\n")
+ .Append(" return true;\n")
+ .Append(" }\n");
+ }
+
+ private static void AppendGroupingPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
+ .Append((int)VariantType.Nil)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (Godot.PropertyHint)")
+ .Append((int)PropertyHint.None)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: true));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append(" properties.Add(new(type: (global::Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: PropertyName.")
+ .Append(propertyInfo.Name)
+ .Append(", hint: (global::Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (global::Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append("));\n");
+ }
+
+ private static IEnumerable<PropertyInfo> DetermineGroupingPropertyInfo(ISymbol memberSymbol)
+ {
+ foreach (var attr in memberSymbol.GetAttributes())
+ {
+ PropertyUsageFlags? propertyUsage = attr.AttributeClass?.ToString() switch
+ {
+ GodotClasses.ExportCategoryAttr => PropertyUsageFlags.Category,
+ GodotClasses.ExportGroupAttr => PropertyUsageFlags.Group,
+ GodotClasses.ExportSubgroupAttr => PropertyUsageFlags.Subgroup,
+ _ => null
+ };
+
+ if (propertyUsage is null)
+ continue;
+
+ if (attr.ConstructorArguments.Length > 0 && attr.ConstructorArguments[0].Value is string name)
+ {
+ string? hintString = null;
+ if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1)
+ hintString = attr.ConstructorArguments[1].Value?.ToString();
+
+ yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString,
+ propertyUsage.Value, true);
+ }
+ }
+ }
+
+ private static PropertyInfo? DeterminePropertyInfo(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ ISymbol memberSymbol,
+ MarshalType marshalType
+ )
+ {
+ var exportAttr = memberSymbol.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGodotExportAttribute() ?? false);
+
+ var propertySymbol = memberSymbol as IPropertySymbol;
+ var fieldSymbol = memberSymbol as IFieldSymbol;
+
+ if (exportAttr != null && propertySymbol != null)
+ {
+ if (propertySymbol.GetMethod == null)
+ {
+ // This should never happen, as we filtered WriteOnly properties, but just in case.
+ Common.ReportExportedMemberIsWriteOnly(context, propertySymbol);
+ return null;
+ }
+
+ if (propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
+ {
+ // This should never happen, as we filtered ReadOnly properties, but just in case.
+ Common.ReportExportedMemberIsReadOnly(context, propertySymbol);
+ return null;
+ }
+ }
+
+ var memberType = propertySymbol?.Type ?? fieldSymbol!.Type;
+
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+ string memberName = memberSymbol.Name;
+
+ if (exportAttr == null)
+ {
+ return new PropertyInfo(memberVariantType, memberName, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.ScriptVariable, exported: false);
+ }
+
+ if (!TryGetMemberExportHint(typeCache, memberType, exportAttr, memberVariantType,
+ isTypeArgument: false, out var hint, out var hintString))
+ {
+ var constructorArguments = exportAttr.ConstructorArguments;
+
+ if (constructorArguments.Length > 0)
+ {
+ var hintValue = exportAttr.ConstructorArguments[0].Value;
+
+ hint = hintValue switch
+ {
+ null => PropertyHint.None,
+ int intValue => (PropertyHint)intValue,
+ _ => (PropertyHint)(long)hintValue
+ };
+
+ hintString = constructorArguments.Length > 1 ?
+ exportAttr.ConstructorArguments[1].Value?.ToString() :
+ null;
+ }
+ else
+ {
+ hint = PropertyHint.None;
+ }
+ }
+
+ var propUsage = PropertyUsageFlags.Default | PropertyUsageFlags.ScriptVariable;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, memberName,
+ hint, hintString, propUsage, exported: true);
+ }
+
+ private static bool TryGetMemberExportHint(
+ MarshalUtils.TypeCache typeCache,
+ ITypeSymbol type, AttributeData exportAttr,
+ VariantType variantType, bool isTypeArgument,
+ out PropertyHint hint, out string? hintString
+ )
+ {
+ hint = PropertyHint.None;
+ hintString = null;
+
+ if (variantType == VariantType.Nil)
+ return true; // Variant, no export hint
+
+ if (variantType == VariantType.Int &&
+ type.IsValueType && type.TypeKind == TypeKind.Enum)
+ {
+ bool hasFlagsAttr = type.GetAttributes()
+ .Any(a => a.AttributeClass?.IsSystemFlagsAttribute() ?? false);
+
+ hint = hasFlagsAttr ? PropertyHint.Flags : PropertyHint.Enum;
+
+ var members = type.GetMembers();
+
+ var enumFields = members
+ .Where(s => s.Kind == SymbolKind.Field && s.IsStatic &&
+ s.DeclaredAccessibility == Accessibility.Public &&
+ !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>().ToArray();
+
+ var hintStringBuilder = new StringBuilder();
+ var nameOnlyHintStringBuilder = new StringBuilder();
+
+ // True: enum Foo { Bar, Baz, Qux }
+ // True: enum Foo { Bar = 0, Baz = 1, Qux = 2 }
+ // False: enum Foo { Bar = 0, Baz = 7, Qux = 5 }
+ bool usesDefaultValues = true;
+
+ for (int i = 0; i < enumFields.Length; i++)
+ {
+ var enumField = enumFields[i];
+
+ if (i > 0)
+ {
+ hintStringBuilder.Append(",");
+ nameOnlyHintStringBuilder.Append(",");
+ }
+
+ string enumFieldName = enumField.Name;
+ hintStringBuilder.Append(enumFieldName);
+ nameOnlyHintStringBuilder.Append(enumFieldName);
+
+ long val = enumField.ConstantValue switch
+ {
+ sbyte v => v,
+ short v => v,
+ int v => v,
+ long v => v,
+ byte v => v,
+ ushort v => v,
+ uint v => v,
+ ulong v => (long)v,
+ _ => 0
+ };
+
+ uint expectedVal = (uint)(hint == PropertyHint.Flags ? 1 << i : i);
+ if (val != expectedVal)
+ usesDefaultValues = false;
+
+ hintStringBuilder.Append(":");
+ hintStringBuilder.Append(val);
+ }
+
+ hintString = !usesDefaultValues ?
+ hintStringBuilder.ToString() :
+ // If we use the format NAME:VAL, that's what the editor displays.
+ // That's annoying if the user is not using custom values for the enum constants.
+ // This may not be needed in the future if the editor is changed to not display values.
+ nameOnlyHintStringBuilder.ToString();
+
+ return true;
+ }
+
+ if (variantType == VariantType.Object && type is INamedTypeSymbol memberNamedType)
+ {
+ if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Resource"))
+ {
+ string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
+
+ hint = PropertyHint.ResourceType;
+ hintString = nativeTypeName;
+
+ return true;
+ }
+
+ if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Node"))
+ {
+ string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
+
+ hint = PropertyHint.NodeType;
+ hintString = nativeTypeName;
+
+ return true;
+ }
+ }
+
+ static bool GetStringArrayEnumHint(VariantType elementVariantType,
+ AttributeData exportAttr, out string? hintString)
+ {
+ var constructorArguments = exportAttr.ConstructorArguments;
+
+ if (constructorArguments.Length > 0)
+ {
+ var presetHintValue = exportAttr.ConstructorArguments[0].Value;
+
+ PropertyHint presetHint = presetHintValue switch
+ {
+ null => PropertyHint.None,
+ int intValue => (PropertyHint)intValue,
+ _ => (PropertyHint)(long)presetHintValue
+ };
+
+ if (presetHint == PropertyHint.Enum)
+ {
+ string? presetHintString = constructorArguments.Length > 1 ?
+ exportAttr.ConstructorArguments[1].Value?.ToString() :
+ null;
+
+ hintString = (int)elementVariantType + "/" + (int)PropertyHint.Enum + ":";
+
+ if (presetHintString != null)
+ hintString += presetHintString;
+
+ return true;
+ }
+ }
+
+ hintString = null;
+ return false;
+ }
+
+ if (!isTypeArgument && variantType == VariantType.Array)
+ {
+ var elementType = MarshalUtils.GetArrayElementType(type);
+
+ if (elementType == null)
+ return false; // Non-generic Array, so there's no hint to add
+
+ var elementMarshalType = MarshalUtils.ConvertManagedTypeToMarshalType(elementType, typeCache)!.Value;
+ var elementVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(elementMarshalType)!.Value;
+
+ bool isPresetHint = false;
+
+ if (elementVariantType == VariantType.String)
+ isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString);
+
+ if (!isPresetHint)
+ {
+ bool hintRes = TryGetMemberExportHint(typeCache, elementType,
+ exportAttr, elementVariantType, isTypeArgument: true,
+ out var elementHint, out var elementHintString);
+
+ // Format: type/hint:hint_string
+ if (hintRes)
+ {
+ hintString = (int)elementVariantType + "/" + (int)elementHint + ":";
+
+ if (elementHintString != null)
+ hintString += elementHintString;
+ }
+ else
+ {
+ hintString = (int)elementVariantType + "/" + (int)PropertyHint.None + ":";
+ }
+ }
+
+ hint = PropertyHint.TypeString;
+
+ return hintString != null;
+ }
+
+ if (!isTypeArgument && variantType == VariantType.PackedStringArray)
+ {
+ if (GetStringArrayEnumHint(VariantType.String, exportAttr, out hintString))
+ {
+ hint = PropertyHint.TypeString;
+ return true;
+ }
+ }
+
+ if (!isTypeArgument && variantType == VariantType.Dictionary)
+ {
+ // TODO: Dictionaries are not supported in the inspector
+ return false;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
new file mode 100644
index 0000000000..99a4c95e73
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -0,0 +1,360 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptPropertyDefValGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptPropertyDefVal.generated";
+
+ var source = new StringBuilder();
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var exportedMembers = new List<ExportedPropertyMetadata>();
+
+ var members = symbol.GetMembers();
+
+ var exportedProperties = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>()
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotExportAttribute() ?? false))
+ .ToArray();
+
+ var exportedFields = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>()
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotExportAttribute() ?? false))
+ .ToArray();
+
+ foreach (var property in exportedProperties)
+ {
+ if (property.IsStatic)
+ {
+ Common.ReportExportedMemberIsStatic(context, property);
+ continue;
+ }
+
+ if (property.IsIndexer)
+ {
+ Common.ReportExportedMemberIsIndexer(context, property);
+ continue;
+ }
+
+ // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter, without a setter or with an init-only setter. Godot properties must be both readable and writable.
+ if (property.IsWriteOnly)
+ {
+ Common.ReportExportedMemberIsWriteOnly(context, property);
+ continue;
+ }
+
+ if (property.IsReadOnly || property.SetMethod!.IsInitOnly)
+ {
+ Common.ReportExportedMemberIsReadOnly(context, property);
+ continue;
+ }
+
+ var propertyType = property.Type;
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(propertyType, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportExportedMemberTypeNotSupported(context, property);
+ continue;
+ }
+
+ var propertyDeclarationSyntax = property.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax() as PropertyDeclarationSyntax).FirstOrDefault();
+
+ // Fully qualify the value to avoid issues with namespaces.
+ string? value = null;
+ if (propertyDeclarationSyntax != null)
+ {
+ if (propertyDeclarationSyntax.Initializer != null)
+ {
+ var sm = context.Compilation.GetSemanticModel(propertyDeclarationSyntax.Initializer.SyntaxTree);
+ value = propertyDeclarationSyntax.Initializer.Value.FullQualifiedSyntax(sm);
+ }
+ else
+ {
+ var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors
+ .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
+ if (propertyGet != null)
+ {
+ if (propertyGet.ExpressionBody != null)
+ {
+ if (propertyGet.ExpressionBody.Expression is IdentifierNameSyntax identifierNameSyntax)
+ {
+ var sm = context.Compilation.GetSemanticModel(identifierNameSyntax.SyntaxTree);
+ var fieldSymbol = sm.GetSymbolInfo(identifierNameSyntax).Symbol as IFieldSymbol;
+ EqualsValueClauseSyntax? initializer = fieldSymbol?.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax())
+ .OfType<VariableDeclaratorSyntax>()
+ .Select(s => s.Initializer)
+ .FirstOrDefault(i => i != null);
+
+ if (initializer != null)
+ {
+ sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
+ value = initializer.Value.FullQualifiedSyntax(sm);
+ }
+ }
+ }
+ else
+ {
+ var returns = propertyGet.DescendantNodes().OfType<ReturnStatementSyntax>();
+ if (returns.Count() == 1)
+ {
+ // Generate only single return
+ var returnStatementSyntax = returns.Single();
+ if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax)
+ {
+ var sm = context.Compilation.GetSemanticModel(identifierNameSyntax.SyntaxTree);
+ var fieldSymbol = sm.GetSymbolInfo(identifierNameSyntax).Symbol as IFieldSymbol;
+ EqualsValueClauseSyntax? initializer = fieldSymbol?.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax())
+ .OfType<VariableDeclaratorSyntax>()
+ .Select(s => s.Initializer)
+ .FirstOrDefault(i => i != null);
+
+ if (initializer != null)
+ {
+ sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
+ value = initializer.Value.FullQualifiedSyntax(sm);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ exportedMembers.Add(new ExportedPropertyMetadata(
+ property.Name, marshalType.Value, propertyType, value));
+ }
+
+ foreach (var field in exportedFields)
+ {
+ if (field.IsStatic)
+ {
+ Common.ReportExportedMemberIsStatic(context, field);
+ continue;
+ }
+
+ // TODO: We should still restore read-only fields after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
+ if (field.IsReadOnly)
+ {
+ Common.ReportExportedMemberIsReadOnly(context, field);
+ continue;
+ }
+
+ var fieldType = field.Type;
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(fieldType, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportExportedMemberTypeNotSupported(context, field);
+ continue;
+ }
+
+ EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences
+ .Select(r => r.GetSyntax())
+ .OfType<VariableDeclaratorSyntax>()
+ .Select(s => s.Initializer)
+ .FirstOrDefault(i => i != null);
+
+ // This needs to be fully qualified to avoid issues with namespaces.
+ string? value = null;
+ if (initializer != null)
+ {
+ var sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
+ value = initializer.Value.FullQualifiedSyntax(sm);
+ }
+
+ exportedMembers.Add(new ExportedPropertyMetadata(
+ field.Name, marshalType.Value, fieldType, value));
+ }
+
+ // Generate GetGodotExportedProperties
+
+ if (exportedMembers.Count > 0)
+ {
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ string dictionaryType =
+ "global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>";
+
+ source.Append("#if TOOLS\n");
+ source.Append(" internal new static ");
+ source.Append(dictionaryType);
+ source.Append(" GetGodotPropertyDefaultValues()\n {\n");
+
+ source.Append(" var values = new ");
+ source.Append(dictionaryType);
+ source.Append("(");
+ source.Append(exportedMembers.Count);
+ source.Append(");\n");
+
+ foreach (var exportedMember in exportedMembers)
+ {
+ string defaultValueLocalName = string.Concat("__", exportedMember.Name, "_default_value");
+
+ source.Append(" ");
+ source.Append(exportedMember.TypeSymbol.FullQualifiedNameIncludeGlobal());
+ source.Append(" ");
+ source.Append(defaultValueLocalName);
+ source.Append(" = ");
+ source.Append(exportedMember.Value ?? "default");
+ source.Append(";\n");
+ source.Append(" values.Add(PropertyName.");
+ source.Append(exportedMember.Name);
+ source.Append(", ");
+ source.AppendManagedToVariantExpr(defaultValueLocalName,
+ exportedMember.TypeSymbol, exportedMember.Type);
+ source.Append(");\n");
+ }
+
+ source.Append(" return values;\n");
+ source.Append(" }\n");
+ source.Append("#endif\n");
+
+ source.Append("#pragma warning restore CS0109\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private struct ExportedPropertyMetadata
+ {
+ public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value)
+ {
+ Name = name;
+ Type = type;
+ TypeSymbol = typeSymbol;
+ Value = value;
+ }
+
+ public string Name { get; }
+ public MarshalType Type { get; }
+ public ITypeSymbol TypeSymbol { get; }
+ public string? Value { get; }
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
new file mode 100644
index 0000000000..ec04a319e2
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptRegistrarGenerator.cs
@@ -0,0 +1,19 @@
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators
+{
+ // Placeholder. Once we switch to native extensions this will act as the registrar for all
+ // user Godot classes in the assembly. Think of it as something similar to `register_types`.
+ public class ScriptRegistrarGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
new file mode 100644
index 0000000000..821f3af75f
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -0,0 +1,286 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptSerializationGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptSerialization.generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var propertySymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property)
+ .Cast<IPropertySymbol>()
+ .Where(s => !s.IsIndexer);
+
+ var fieldSymbols = members
+ .Where(s => !s.IsStatic && s.Kind == SymbolKind.Field && !s.IsImplicitlyDeclared)
+ .Cast<IFieldSymbol>();
+
+ var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+ var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
+
+ var signalDelegateSymbols = members
+ .Where(s => s.Kind == SymbolKind.NamedType)
+ .Cast<INamedTypeSymbol>()
+ .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+ List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+ foreach (var signalDelegateSymbol in signalDelegateSymbols)
+ {
+ if (!signalDelegateSymbol.Name.EndsWith(ScriptSignalsGenerator.SignalDelegateSuffix))
+ continue;
+
+ string signalName = signalDelegateSymbol.Name;
+ signalName = signalName.Substring(0,
+ signalName.Length - ScriptSignalsGenerator.SignalDelegateSuffix.Length);
+
+ var invokeMethodData = signalDelegateSymbol
+ .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+ if (invokeMethodData == null)
+ continue;
+
+ godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+ }
+
+ source.Append(
+ " protected override void SaveGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
+ source.Append(" base.SaveGodotObjectData(info);\n");
+
+ // Save properties
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+
+ source.Append(" info.AddProperty(PropertyName.")
+ .Append(propertyName)
+ .Append(", ")
+ .AppendManagedToVariantExpr(string.Concat("this.", propertyName),
+ property.PropertySymbol.Type, property.Type)
+ .Append(");\n");
+ }
+
+ // Save fields
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+
+ source.Append(" info.AddProperty(PropertyName.")
+ .Append(fieldName)
+ .Append(", ")
+ .AppendManagedToVariantExpr(string.Concat("this.", fieldName),
+ field.FieldSymbol.Type, field.Type)
+ .Append(");\n");
+ }
+
+ // Save signal events
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+
+ source.Append(" info.AddSignalEventDelegate(SignalName.")
+ .Append(signalName)
+ .Append(", this.backing_")
+ .Append(signalName)
+ .Append(");\n");
+ }
+
+ source.Append(" }\n");
+
+ source.Append(
+ " protected override void RestoreGodotObjectData(global::Godot.Bridge.GodotSerializationInfo info)\n {\n");
+ source.Append(" base.RestoreGodotObjectData(info);\n");
+
+ // Restore properties
+
+ foreach (var property in godotClassProperties)
+ {
+ string propertyName = property.PropertySymbol.Name;
+
+ source.Append(" if (info.TryGetProperty(PropertyName.")
+ .Append(propertyName)
+ .Append(", out var _value_")
+ .Append(propertyName)
+ .Append("))\n")
+ .Append(" this.")
+ .Append(propertyName)
+ .Append(" = ")
+ .AppendVariantToManagedExpr(string.Concat("_value_", propertyName),
+ property.PropertySymbol.Type, property.Type)
+ .Append(";\n");
+ }
+
+ // Restore fields
+
+ foreach (var field in godotClassFields)
+ {
+ string fieldName = field.FieldSymbol.Name;
+
+ source.Append(" if (info.TryGetProperty(PropertyName.")
+ .Append(fieldName)
+ .Append(", out var _value_")
+ .Append(fieldName)
+ .Append("))\n")
+ .Append(" this.")
+ .Append(fieldName)
+ .Append(" = ")
+ .AppendVariantToManagedExpr(string.Concat("_value_", fieldName),
+ field.FieldSymbol.Type, field.Type)
+ .Append(";\n");
+ }
+
+ // Restore signal events
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+ string signalDelegateQualifiedName = signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal();
+
+ source.Append(" if (info.TryGetSignalEventDelegate<")
+ .Append(signalDelegateQualifiedName)
+ .Append(">(SignalName.")
+ .Append(signalName)
+ .Append(", out var _value_")
+ .Append(signalName)
+ .Append("))\n")
+ .Append(" this.backing_")
+ .Append(signalName)
+ .Append(" = _value_")
+ .Append(signalName)
+ .Append(";\n");
+ }
+
+ source.Append(" }\n");
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
new file mode 100644
index 0000000000..ba6c10aa31
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -0,0 +1,433 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+// TODO:
+// Determine a proper way to emit the signal.
+// 'Emit(nameof(TheEvent))' creates a StringName every time and has the overhead of string marshaling.
+// I haven't decided on the best option yet. Some possibilities:
+// - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
+// - Generate a 'EmitSignalName' method for each event signal.
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptSignalsGenerator : ISourceGenerator
+ {
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.AreGodotSourceGeneratorsDisabled())
+ return;
+
+ INamedTypeSymbol[] godotClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialGodotScriptOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ if (godotClasses.Length > 0)
+ {
+ var typeCache = new MarshalUtils.TypeCache(context.Compilation);
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, typeCache, godotClass);
+ }
+ }
+ }
+
+ internal static string SignalDelegateSuffix = "EventHandler";
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ MarshalUtils.TypeCache typeCache,
+ INamedTypeSymbol symbol
+ )
+ {
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ bool isInnerClass = symbol.ContainingType != null;
+
+ string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
+ + "_ScriptSignals.generated";
+
+ var source = new StringBuilder();
+
+ source.Append("using Godot;\n");
+ source.Append("using Godot.NativeInterop;\n");
+ source.Append("\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("partial class ");
+ source.Append(symbol.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ var members = symbol.GetMembers();
+
+ var signalDelegateSymbols = members
+ .Where(s => s.Kind == SymbolKind.NamedType)
+ .Cast<INamedTypeSymbol>()
+ .Where(namedTypeSymbol => namedTypeSymbol.TypeKind == TypeKind.Delegate)
+ .Where(s => s.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotSignalAttribute() ?? false));
+
+ List<GodotSignalDelegateData> godotSignalDelegates = new();
+
+ foreach (var signalDelegateSymbol in signalDelegateSymbols)
+ {
+ if (!signalDelegateSymbol.Name.EndsWith(SignalDelegateSuffix))
+ {
+ Common.ReportSignalDelegateMissingSuffix(context, signalDelegateSymbol);
+ continue;
+ }
+
+ string signalName = signalDelegateSymbol.Name;
+ signalName = signalName.Substring(0, signalName.Length - SignalDelegateSuffix.Length);
+
+ var invokeMethodData = signalDelegateSymbol
+ .DelegateInvokeMethod?.HasGodotCompatibleSignature(typeCache);
+
+ if (invokeMethodData == null)
+ {
+ if (signalDelegateSymbol.DelegateInvokeMethod is IMethodSymbol methodSymbol)
+ {
+ foreach (var parameter in methodSymbol.Parameters)
+ {
+ if (parameter.RefKind != RefKind.None)
+ {
+ Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ continue;
+ }
+
+ var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(parameter.Type, typeCache);
+
+ if (marshalType == null)
+ {
+ Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ }
+ }
+
+ if (!methodSymbol.ReturnsVoid)
+ {
+ Common.ReportSignalDelegateSignatureMustReturnVoid(context, signalDelegateSymbol);
+ }
+ }
+
+ continue;
+ }
+
+ godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
+ }
+
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append(
+ $" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n");
+
+ // Generate cached StringNames for methods and properties, for fast lookup
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+ source.Append(" public new static readonly global::Godot.StringName ");
+ source.Append(signalName);
+ source.Append(" = \"");
+ source.Append(signalName);
+ source.Append("\";\n");
+ }
+
+ source.Append(" }\n"); // class GodotInternal
+
+ // Generate GetGodotSignalList
+
+ if (godotSignalDelegates.Count > 0)
+ {
+ const string listType = "global::System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
+
+ source.Append(" internal new static ")
+ .Append(listType)
+ .Append(" GetGodotSignalList()\n {\n");
+
+ source.Append(" var signals = new ")
+ .Append(listType)
+ .Append("(")
+ .Append(godotSignalDelegates.Count)
+ .Append(");\n");
+
+ foreach (var signalDelegateData in godotSignalDelegates)
+ {
+ var methodInfo = DetermineMethodInfo(signalDelegateData);
+ AppendMethodInfo(source, methodInfo);
+ }
+
+ source.Append(" return signals;\n");
+ source.Append(" }\n");
+ }
+
+ source.Append("#pragma warning restore CS0109\n");
+
+ // Generate signal event
+
+ foreach (var signalDelegate in godotSignalDelegates)
+ {
+ string signalName = signalDelegate.Name;
+
+ // TODO: Hide backing event from code-completion and debugger
+ // The reason we have a backing field is to hide the invoke method from the event,
+ // as it doesn't emit the signal, only the event delegates. This can confuse users.
+ // Maybe we should directly connect the delegates, as we do with native signals?
+ source.Append(" private ")
+ .Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
+ .Append(" backing_")
+ .Append(signalName)
+ .Append(";\n");
+
+ source.Append(
+ $" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
+
+ source.Append(" public event ")
+ .Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
+ .Append(" ")
+ .Append(signalName)
+ .Append(" {\n")
+ .Append(" add => backing_")
+ .Append(signalName)
+ .Append(" += value;\n")
+ .Append(" remove => backing_")
+ .Append(signalName)
+ .Append(" -= value;\n")
+ .Append("}\n");
+ }
+
+ // Generate RaiseGodotClassSignalCallbacks
+
+ if (godotSignalDelegates.Count > 0)
+ {
+ source.Append(
+ " protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, ");
+ source.Append("NativeVariantPtrArgs args)\n {\n");
+
+ foreach (var signal in godotSignalDelegates)
+ {
+ GenerateSignalEventInvoker(signal, source);
+ }
+
+ source.Append(" base.RaiseGodotClassSignalCallbacks(signal, args);\n");
+
+ source.Append(" }\n");
+ }
+
+ source.Append("}\n"); // partial class
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
+ {
+ source.Append(" signals.Add(new(name: SignalName.")
+ .Append(methodInfo.Name)
+ .Append(", returnVal: ");
+
+ AppendPropertyInfo(source, methodInfo.ReturnVal);
+
+ source.Append(", flags: (global::Godot.MethodFlags)")
+ .Append((int)methodInfo.Flags)
+ .Append(", arguments: ");
+
+ if (methodInfo.Arguments is { Count: > 0 })
+ {
+ source.Append("new() { ");
+
+ foreach (var param in methodInfo.Arguments)
+ {
+ AppendPropertyInfo(source, param);
+
+ // C# allows colon after the last element
+ source.Append(", ");
+ }
+
+ source.Append(" }");
+ }
+ else
+ {
+ source.Append("null");
+ }
+
+ source.Append(", defaultArguments: null));\n");
+ }
+
+ private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
+ {
+ source.Append("new(type: (global::Godot.Variant.Type)")
+ .Append((int)propertyInfo.Type)
+ .Append(", name: \"")
+ .Append(propertyInfo.Name)
+ .Append("\", hint: (global::Godot.PropertyHint)")
+ .Append((int)propertyInfo.Hint)
+ .Append(", hintString: \"")
+ .Append(propertyInfo.HintString)
+ .Append("\", usage: (global::Godot.PropertyUsageFlags)")
+ .Append((int)propertyInfo.Usage)
+ .Append(", exported: ")
+ .Append(propertyInfo.Exported ? "true" : "false")
+ .Append(")");
+ }
+
+ private static MethodInfo DetermineMethodInfo(GodotSignalDelegateData signalDelegateData)
+ {
+ var invokeMethodData = signalDelegateData.InvokeMethodData;
+
+ PropertyInfo returnVal;
+
+ if (invokeMethodData.RetType != null)
+ {
+ returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty);
+ }
+ else
+ {
+ returnVal = new PropertyInfo(VariantType.Nil, string.Empty, PropertyHint.None,
+ hintString: null, PropertyUsageFlags.Default, exported: false);
+ }
+
+ int paramCount = invokeMethodData.ParamTypes.Length;
+
+ List<PropertyInfo>? arguments;
+
+ if (paramCount > 0)
+ {
+ arguments = new(capacity: paramCount);
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ arguments.Add(DeterminePropertyInfo(invokeMethodData.ParamTypes[i],
+ name: invokeMethodData.Method.Parameters[i].Name));
+ }
+ }
+ else
+ {
+ arguments = null;
+ }
+
+ return new MethodInfo(signalDelegateData.Name, returnVal, MethodFlags.Default, arguments,
+ defaultArguments: null);
+ }
+
+ private static PropertyInfo DeterminePropertyInfo(MarshalType marshalType, string name)
+ {
+ var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
+
+ var propUsage = PropertyUsageFlags.Default;
+
+ if (memberVariantType == VariantType.Nil)
+ propUsage |= PropertyUsageFlags.NilIsVariant;
+
+ return new PropertyInfo(memberVariantType, name,
+ PropertyHint.None, string.Empty, propUsage, exported: false);
+ }
+
+ private static void GenerateSignalEventInvoker(
+ GodotSignalDelegateData signal,
+ StringBuilder source
+ )
+ {
+ string signalName = signal.Name;
+ var invokeMethodData = signal.InvokeMethodData;
+
+ source.Append(" if (signal == SignalName.");
+ source.Append(signalName);
+ source.Append(" && args.Count == ");
+ source.Append(invokeMethodData.ParamTypes.Length);
+ source.Append(") {\n");
+ source.Append(" backing_");
+ source.Append(signalName);
+ source.Append("?.Invoke(");
+
+ for (int i = 0; i < invokeMethodData.ParamTypes.Length; i++)
+ {
+ if (i != 0)
+ source.Append(", ");
+
+ source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
+ invokeMethodData.ParamTypeSymbols[i], invokeMethodData.ParamTypes[i]);
+ }
+
+ source.Append(");\n");
+
+ source.Append(" return;\n");
+
+ source.Append(" }\n");
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 2bf1cb7a18..01aa65bfc3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -7,8 +7,6 @@ namespace GodotTools.BuildLogger
{
public class GodotBuildLogger : ILogger
{
- public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
-
public string Parameters { get; set; }
public LoggerVerbosity Verbosity { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
index 0afec970c6..9e36497b06 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
@@ -5,6 +5,6 @@
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
index d6d8962f90..cfd5c88a58 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
- <TargetFramework>netstandard2.0</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 60a4f297c9..7c5502814f 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -34,7 +34,7 @@ namespace GodotTools.Core
path = path.Replace('\\', '/');
path = path[path.Length - 1] == '/' ? path.Substring(0, path.Length - 1) : path;
- string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
@@ -60,7 +60,7 @@ namespace GodotTools.Core
public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
{
- var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
+ var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
if (allowDirSeparator)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index 303ca3a293..d2132115f3 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
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index bc09e1ebf9..72e2a1fc0d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -123,12 +123,16 @@ namespace GodotTools.IdeMessaging
string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
// FileSystemWatcher requires an existing directory
- if (!Directory.Exists(projectMetadataDir)) {
+ if (!Directory.Exists(projectMetadataDir))
+ {
// Check if the non hidden version exists
string nonHiddenProjectMetadataDir = Path.Combine(godotProjectDir, "godot", "mono", "metadata");
- if (Directory.Exists(nonHiddenProjectMetadataDir)) {
+ if (Directory.Exists(nonHiddenProjectMetadataDir))
+ {
projectMetadataDir = nonHiddenProjectMetadataDir;
- } else {
+ }
+ else
+ {
Directory.CreateDirectory(projectMetadataDir);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
index 686202e81e..2448a2953b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
@@ -25,10 +25,7 @@ namespace GodotTools.IdeMessaging
public override bool Equals(object obj)
{
- if (obj is GodotIdeMetadata metadata)
- return metadata == this;
-
- return false;
+ return obj is GodotIdeMetadata metadata && metadata == this;
}
public bool Equals(GodotIdeMetadata other)
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index 10d7e1898e..dd3913b4f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -78,7 +78,7 @@ namespace GodotTools.IdeMessaging
clientStream.WriteTimeout = ClientWriteTimeout;
clientReader = new StreamReader(clientStream, Encoding.UTF8);
- clientWriter = new StreamWriter(clientStream, Encoding.UTF8) {NewLine = "\n"};
+ clientWriter = new StreamWriter(clientStream, Encoding.UTF8) { NewLine = "\n" };
}
public async Task Process()
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
index 548e7f06ee..a57c82b608 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
@@ -17,7 +17,7 @@ namespace GodotTools.IdeMessaging
if (content.Status == MessageStatus.Ok)
SetResult(JsonConvert.DeserializeObject<T>(content.Body));
else
- SetResult(new T {Status = content.Status});
+ SetResult(new T { Status = content.Status });
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
index d84a63c83c..a285d5fa97 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
@@ -21,14 +21,14 @@ namespace GodotTools.IdeMessaging.Utils
public void OnCompleted(Action continuation)
{
if (this.continuation != null)
- throw new InvalidOperationException("This awaiter has already been listened");
+ throw new InvalidOperationException("This awaiter already has a continuation.");
this.continuation = continuation;
}
public void SetResult(T result)
{
if (IsCompleted)
- throw new InvalidOperationException("This awaiter is already completed");
+ throw new InvalidOperationException("This awaiter is already completed.");
IsCompleted = true;
this.result = result;
@@ -39,7 +39,7 @@ namespace GodotTools.IdeMessaging.Utils
public void SetException(Exception exception)
{
if (IsCompleted)
- throw new InvalidOperationException("This awaiter is already completed");
+ throw new InvalidOperationException("This awaiter is already completed.");
IsCompleted = true;
this.exception = exception;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
index 9d593fbf8a..ab21527344 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
@@ -13,7 +13,7 @@ namespace GodotTools.IdeMessaging.Utils
return waitAsyncTask.ContinueWith<IDisposable>(t => wrapper, cancellationToken).ConfigureAwait(false);
}
- private struct SemaphoreSlimWaitReleaseWrapper : IDisposable
+ private readonly struct SemaphoreSlimWaitReleaseWrapper : IDisposable
{
private readonly SemaphoreSlim semaphoreSlim;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
index 5b3ed0b1b7..c05096bdcc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index 7a4641dbbc..cc0deb6cc0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -249,8 +249,8 @@ namespace GodotTools.OpenVisualStudio
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
+ // flag = SERVERCALL_RETRYLATER
if (dwRejectType == 2)
- // flag = SERVERCALL_RETRYLATER
{
// Retry the thread call immediately if return >= 0 & < 100
return 99;
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 37123ba2b2..bde14b2b40 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -1,32 +1,16 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
+ <PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
- <!--
- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
- here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
- We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
- searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
- -->
- <ItemGroup>
- <None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
- </ItemGroup>
- <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) ">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
- </PropertyGroup>
- <!-- Need to copy it here as well on Windows -->
- <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
- </Target>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 7d49d251dd..f3c8e89dff 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.Build.Construction;
@@ -14,14 +15,15 @@ namespace GodotTools.ProjectEditor
public static ProjectRootElement GenGameProject(string name)
{
if (name.Length == 0)
- throw new ArgumentException("Project name is empty", nameof(name));
+ throw new ArgumentException("Project name is empty.", nameof(name));
var root = ProjectRootElement.Create(NewProjectFileOptions.None);
root.Sdk = GodotSdkAttrValue;
var mainGroup = root.AddPropertyGroup();
- mainGroup.AddProperty("TargetFramework", "netstandard2.1");
+ mainGroup.AddProperty("TargetFramework", "net6.0");
+ mainGroup.AddProperty("EnableDynamicLoading", "true");
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
@@ -35,7 +37,7 @@ namespace GodotTools.ProjectEditor
public static string GenAndSaveGameProject(string dir, string name)
{
if (name.Length == 0)
- throw new ArgumentException("Project name is empty", nameof(name));
+ throw new ArgumentException("Project name is empty.", nameof(name));
string path = Path.Combine(dir, name + ".csproj");
@@ -44,7 +46,7 @@ namespace GodotTools.ProjectEditor
// Save (without BOM)
root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
- return Guid.NewGuid().ToString().ToUpper();
+ return Guid.NewGuid().ToString().ToUpperInvariant();
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index cdac9acb25..7b1d5c228a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -19,6 +19,16 @@ namespace GodotTools.ProjectEditor
public static class ProjectUtils
{
+ public static void MSBuildLocatorRegisterDefaults(out Version version, out string path)
+ {
+ var instance = Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
+ version = instance.Version;
+ path = instance.MSBuildPath;
+ }
+
+ public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
+ => Microsoft.Build.Locator.MSBuildLocator.RegisterMSBuildPath(msbuildPath);
+
public static MSBuildProject Open(string path)
{
var root = ProjectRootElement.Open(path);
@@ -42,7 +52,8 @@ namespace GodotTools.ProjectEditor
var root = project.Root;
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
- if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(root.Sdk) &&
+ root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
root.Sdk = godotSdkAttrValue;
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
index aab2d73bdd..37bd4a0be0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -8,8 +8,8 @@
</Target>
<Target Name="GenerateGodotNupkgsVersionsFile"
- DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
- BeforeTargets="BeforeCompile;CoreCompile">
+ DependsOnTargets="_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="PrepareForBuild;CompileDesignTime;BeforeCompile;CoreCompile">
<ItemGroup>
<Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
<FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
@@ -21,10 +21,13 @@
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
<PropertyGroup>
<GenerateGodotNupkgsVersionsCode><![CDATA[
-namespace $(RootNamespace) {
- public class GeneratedGodotNupkgsVersions {
+namespace $(RootNamespace)
+{
+ public class GeneratedGodotNupkgsVersions
+ {
public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
+ public const string GodotSharp = "$(PackageVersion_GodotSharp)"%3b
}
}
]]></GenerateGodotNupkgsVersionsCode>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
index 3bc1698c15..d60e6343ea 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
+ <!-- Specify compile items manually to avoid including dangling generated items. -->
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index d3107a69db..564775635d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
@@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{D8C421B2-8911-41EB-B983-F675C7141EB7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{55666071-BEC1-4A52-8A98-9A4A7A947DBF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -49,5 +53,13 @@ Global
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index 28bf57dc21..edbf53a389 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,28 +4,36 @@ using Godot.Collections;
using GodotTools.Internals;
using Path = System.IO.Path;
+#nullable enable
+
namespace GodotTools.Build
{
[Serializable]
- public sealed class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
+ public sealed partial class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
{
- public string Solution { get; }
- public string[] Targets { get; }
- public string Configuration { get; }
- public bool Restore { get; }
+ public string Solution { get; private set; }
+ public string Configuration { get; private set; }
+ public string? RuntimeIdentifier { get; private set; }
+ public string? PublishOutputDir { get; private set; }
+ public bool Restore { get; private set; }
+ public bool Rebuild { get; private set; }
+ public bool OnlyClean { get; private set; }
+
// TODO Use List once we have proper serialization
- public Array<string> CustomProperties { get; } = new Array<string>();
+ public Godot.Collections.Array CustomProperties { get; private set; } = new();
- public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
+ public string LogsDirPath =>
+ Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- if (obj is BuildInfo other)
- return other.Solution == Solution && other.Targets == Targets &&
- other.Configuration == Configuration && other.Restore == Restore &&
- other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
-
- return false;
+ return obj is BuildInfo other &&
+ other.Solution == Solution &&
+ other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier &&
+ other.PublishOutputDir == PublishOutputDir && other.Restore == Restore &&
+ other.Rebuild == Rebuild && other.OnlyClean == OnlyClean &&
+ other.CustomProperties == CustomProperties &&
+ other.LogsDirPath == LogsDirPath;
}
public override int GetHashCode()
@@ -34,25 +42,44 @@ namespace GodotTools.Build
{
int hash = 17;
hash = (hash * 29) + Solution.GetHashCode();
- hash = (hash * 29) + Targets.GetHashCode();
hash = (hash * 29) + Configuration.GetHashCode();
+ hash = (hash * 29) + (RuntimeIdentifier?.GetHashCode() ?? 0);
+ hash = (hash * 29) + (PublishOutputDir?.GetHashCode() ?? 0);
hash = (hash * 29) + Restore.GetHashCode();
+ hash = (hash * 29) + Rebuild.GetHashCode();
+ hash = (hash * 29) + OnlyClean.GetHashCode();
hash = (hash * 29) + CustomProperties.GetHashCode();
hash = (hash * 29) + LogsDirPath.GetHashCode();
return hash;
}
}
+ // Needed for instantiation from Godot, after reloading assemblies
private BuildInfo()
{
+ Solution = string.Empty;
+ Configuration = string.Empty;
+ }
+
+ public BuildInfo(string solution, string configuration, bool restore, bool rebuild, bool onlyClean)
+ {
+ Solution = solution;
+ Configuration = configuration;
+ Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
- public BuildInfo(string solution, string[] targets, string configuration, bool restore)
+ public BuildInfo(string solution, string configuration, string runtimeIdentifier,
+ string publishOutputDir, bool restore, bool rebuild, bool onlyClean)
{
Solution = solution;
- Targets = targets;
Configuration = configuration;
+ RuntimeIdentifier = runtimeIdentifier;
+ PublishOutputDir = publishOutputDir;
Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 21bff70b15..993c6d9217 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,12 +1,10 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Ides.Rider;
+using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
-using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
-using OS = GodotTools.Utils.OS;
namespace GodotTools.Build
{
@@ -14,13 +12,8 @@ namespace GodotTools.Build
{
private static BuildInfo _buildInProgress;
- public const string PropNameMSBuildMono = "MSBuild (Mono)";
- public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
- public const string PropNameMSBuildJetBrains = "MSBuild (JetBrains Rider)";
- public const string PropNameDotnetCli = "dotnet CLI";
-
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
- public const string MsBuildLogFileName = "msbuild_log.txt";
+ private const string MsBuildLogFileName = "msbuild_log.txt";
public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
@@ -62,14 +55,14 @@ namespace GodotTools.Build
private static void PrintVerbose(string text)
{
- if (Godot.OS.IsStdoutVerbose())
- Godot.GD.Print(text);
+ if (OS.IsStdoutVerbose())
+ GD.Print(text);
}
- public static bool Build(BuildInfo buildInfo)
+ private static bool Build(BuildInfo buildInfo)
{
if (_buildInProgress != null)
- throw new InvalidOperationException("A build is already in progress");
+ throw new InvalidOperationException("A build is already in progress.");
_buildInProgress = buildInfo;
@@ -103,7 +96,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -117,7 +111,7 @@ namespace GodotTools.Build
public static async Task<bool> BuildAsync(BuildInfo buildInfo)
{
if (_buildInProgress != null)
- throw new InvalidOperationException("A build is already in progress");
+ throw new InvalidOperationException("A build is already in progress.");
_buildInProgress = buildInfo;
@@ -148,7 +142,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -159,18 +154,54 @@ namespace GodotTools.Build
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
+ private static bool Publish(BuildInfo buildInfo)
{
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+ if (_buildInProgress != null)
+ throw new InvalidOperationException("A build is already in progress.");
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+ _buildInProgress = buildInfo;
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+ try
+ {
+ BuildStarted?.Invoke(buildInfo);
+
+ // Required in order to update the build tasks list
+ Internal.GodotMainIteration();
- return BuildProjectBlocking(buildInfo);
+ try
+ {
+ RemoveOldIssuesFile(buildInfo);
+ }
+ catch (IOException e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ Console.Error.WriteLine(e);
+ }
+
+ try
+ {
+ int exitCode = BuildSystem.Publish(buildInfo, StdOutputReceived, StdErrorReceived);
+
+ if (exitCode != 0)
+ PrintVerbose(
+ $"dotnet publish exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
+
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
+
+ return exitCode == 0;
+ }
+ catch (Exception e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The publish method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ finally
+ {
+ _buildInProgress = null;
+ }
}
private static bool BuildProjectBlocking(BuildInfo buildInfo)
@@ -178,31 +209,109 @@ namespace GodotTools.Build
if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
- // Make sure the API assemblies are up to date before building the project.
- // We may not have had the chance to update the release API assemblies, and the debug ones
- // may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
+ using var pr = new EditorProgress("dotnet_build_project", "Building .NET project...", 1);
+
+ pr.Step("Building project solution", 0);
- if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
+ if (!Build(buildInfo))
{
- ShowBuildErrorDialog("Failed to update the Godot API assemblies");
+ ShowBuildErrorDialog("Failed to build project solution");
return false;
}
- using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
+ return true;
+ }
+
+ private static bool CleanProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
+ return true; // No solution to clean
+
+ using var pr = new EditorProgress("dotnet_clean_project", "Cleaning .NET project...", 1);
+
+ pr.Step("Cleaning project solution", 0);
+
+ if (!Build(buildInfo))
{
- pr.Step("Building project solution", 0);
+ ShowBuildErrorDialog("Failed to clean project solution");
+ return false;
+ }
- if (!Build(buildInfo))
- {
- ShowBuildErrorDialog("Failed to build project solution");
- return false;
- }
+ return true;
+ }
+
+ private static bool PublishProjectBlocking(BuildInfo buildInfo)
+ {
+ using var pr = new EditorProgress("dotnet_publish_project", "Publishing .NET project...", 1);
+
+ pr.Step("Running dotnet publish", 0);
+
+ if (!Publish(buildInfo))
+ {
+ ShowBuildErrorDialog("Failed to publish .NET project");
+ return false;
}
return true;
}
+ private static BuildInfo CreateBuildInfo(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false,
+ bool onlyClean = false
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ restore: true, rebuild, onlyClean);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || Utils.OS.PlatformNameMap.TryGetValue(OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ private static BuildInfo CreatePublishBuildInfo(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ [DisallowNull] string publishOutputDir
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ runtimeIdentifier, publishOutputDir, restore: true, rebuild: false, onlyClean: false);
+
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ public static bool BuildProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false
+ ) => BuildProjectBlocking(CreateBuildInfo(configuration, platform, rebuild));
+
+ public static bool CleanProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null
+ ) => CleanProjectBlocking(CreateBuildInfo(configuration, platform, rebuild: false));
+
+ public static bool PublishProjectBlocking(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ string publishOutputDir
+ ) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
+ platform, runtimeIdentifier, publishOutputDir));
+
public static bool EditorBuildCallback()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -215,7 +324,7 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
@@ -226,47 +335,6 @@ namespace GodotTools.Build
public static void Initialize()
{
- // Build tool settings
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
-
- BuildTool msbuildDefault;
-
- if (OS.IsWindows)
- {
- if (RiderPathManager.IsExternalEditorSetToRider(editorSettings))
- msbuildDefault = BuildTool.JetBrainsMsBuild;
- else
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildVs;
- }
- else
- {
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildMono;
- }
-
- EditorDef("mono/builds/build_tool", msbuildDefault);
-
- string hintString;
-
- if (OS.IsWindows)
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameMSBuildVs}:{(int)BuildTool.MsBuildVs}," +
- $"{PropNameMSBuildJetBrains}:{(int)BuildTool.JetBrainsMsBuild}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
- else
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
-
- editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Godot.Variant.Type.Int,
- ["name"] = "mono/builds/build_tool",
- ["hint"] = Godot.PropertyHint.Enum,
- ["hint_string"] = hintString
- });
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index ebdaca0ce8..e439822666 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -1,17 +1,16 @@
using Godot;
using System;
-using Godot.Collections;
+using System.Diagnostics.CodeAnalysis;
using GodotTools.Internals;
-using JetBrains.Annotations;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
namespace GodotTools.Build
{
- public class BuildOutputView : VBoxContainer, ISerializationListener
+ public partial class BuildOutputView : VBoxContainer, ISerializationListener
{
[Serializable]
- private class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
+ private partial class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
{
public bool Warning { get; set; }
public string File { get; set; }
@@ -22,7 +21,8 @@ namespace GodotTools.Build
public string ProjectFile { get; set; }
}
- [Signal] public event Action BuildStateChanged;
+ [Signal]
+ public delegate void BuildStateChangedEventHandler();
public bool HasBuildExited { get; private set; } = false;
@@ -58,7 +58,7 @@ namespace GodotTools.Build
}
// TODO Use List once we have proper serialization.
- private readonly Array<BuildIssue> _issues = new Array<BuildIssue>();
+ private Godot.Collections.Array<BuildIssue> _issues = new();
private ItemList _issuesList;
private PopupMenu _issuesListContextMenu;
private TextEdit _buildLog;
@@ -69,71 +69,63 @@ namespace GodotTools.Build
private void LoadIssuesFromFile(string csvFile)
{
- using (var file = new Godot.File())
+ using var file = FileAccess.Open(csvFile, FileAccess.ModeFlags.Read);
+
+ if (file == null)
+ return;
+
+ while (!file.EofReached())
{
- try
- {
- Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
+ string[] csvColumns = file.GetCsvLine();
- if (openError != Error.Ok)
- return;
+ if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
+ return;
- while (!file.EofReached())
- {
- string[] csvColumns = file.GetCsvLine();
-
- if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
- return;
-
- if (csvColumns.Length != 7)
- {
- GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
- continue;
- }
-
- var issue = new BuildIssue
- {
- Warning = csvColumns[0] == "warning",
- File = csvColumns[1],
- Line = int.Parse(csvColumns[2]),
- Column = int.Parse(csvColumns[3]),
- Code = csvColumns[4],
- Message = csvColumns[5],
- ProjectFile = csvColumns[6]
- };
-
- if (issue.Warning)
- WarningCount += 1;
- else
- ErrorCount += 1;
-
- _issues.Add(issue);
- }
- }
- finally
+ if (csvColumns.Length != 7)
{
- file.Close(); // Disposing it is not enough. We need to call Close()
+ GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+ continue;
}
+
+ var issue = new BuildIssue
+ {
+ Warning = csvColumns[0] == "warning",
+ File = csvColumns[1],
+ Line = int.Parse(csvColumns[2]),
+ Column = int.Parse(csvColumns[3]),
+ Code = csvColumns[4],
+ Message = csvColumns[5],
+ ProjectFile = csvColumns[6]
+ };
+
+ if (issue.Warning)
+ WarningCount += 1;
+ else
+ ErrorCount += 1;
+
+ _issues.Add(issue);
}
}
- private void IssueActivated(int idx)
+ private void IssueActivated(long idx)
{
if (idx < 0 || idx >= _issuesList.ItemCount)
- throw new IndexOutOfRangeException("Item list index out of range");
+ throw new ArgumentOutOfRangeException(nameof(idx), "Item list index out of range.");
// Get correct issue idx from issue list
- int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)_issuesList.GetItemMetadata((int)idx);
if (issueIndex < 0 || issueIndex >= _issues.Count)
- throw new IndexOutOfRangeException("Issue index out of range");
+ throw new InvalidOperationException("Issue index out of range.");
BuildIssue issue = _issues[issueIndex];
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
return;
- string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : _buildInfo.Solution.GetBaseDir();
+ string projectDir = !string.IsNullOrEmpty(issue.ProjectFile) ?
+ issue.ProjectFile.GetBaseDir() :
+ _buildInfo.Solution.GetBaseDir();
string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
@@ -291,7 +283,7 @@ namespace GodotTools.Build
public void RestartBuild()
{
if (!HasBuildExited)
- throw new InvalidOperationException("Build already started");
+ throw new InvalidOperationException("Build already started.");
BuildManager.RestartBuild(this);
}
@@ -299,7 +291,7 @@ namespace GodotTools.Build
public void StopBuild()
{
if (!HasBuildExited)
- throw new InvalidOperationException("Build is not in progress");
+ throw new InvalidOperationException("Build is not in progress.");
BuildManager.StopBuild(this);
}
@@ -309,7 +301,7 @@ namespace GodotTools.Build
Copy
}
- private void IssuesListContextOptionPressed(int id)
+ private void IssuesListContextOptionPressed(long id)
{
switch ((IssuesContextMenuOption)id)
{
@@ -334,9 +326,9 @@ namespace GodotTools.Build
}
}
- private void IssuesListClicked(int index, Vector2 atPosition, int mouseButtonIndex)
+ private void IssuesListClicked(long index, Vector2 atPosition, long mouseButtonIndex)
{
- if (mouseButtonIndex != (int)MouseButton.Right)
+ if (mouseButtonIndex != (long)MouseButton.Right)
{
return;
}
@@ -364,19 +356,19 @@ namespace GodotTools.Build
{
base._Ready();
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+ SizeFlagsVertical = SizeFlags.ExpandFill;
var hsc = new HSplitContainer
{
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
+ SizeFlagsHorizontal = SizeFlags.ExpandFill,
+ SizeFlagsVertical = SizeFlags.ExpandFill
};
AddChild(hsc);
_issuesList = new ItemList
{
- SizeFlagsVertical = (int)SizeFlags.ExpandFill,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the build log
};
_issuesList.ItemActivated += IssueActivated;
_issuesList.AllowRmbSelect = true;
@@ -390,8 +382,8 @@ namespace GodotTools.Build
_buildLog = new TextEdit
{
Editable = false,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
+ SizeFlagsVertical = SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = SizeFlags.ExpandFill // Avoid being squashed by the issues list
};
hsc.AddChild(_buildLog);
@@ -412,6 +404,16 @@ namespace GodotTools.Build
{
// In case it didn't update yet. We don't want to have to serialize any pending output.
UpdateBuildLogText();
+
+ // NOTE:
+ // Currently, GodotTools is loaded in its own load context. This load context is not reloaded, but the script still are.
+ // Until that changes, we need workarounds like this one because events keep strong references to disposed objects.
+ BuildManager.BuildLaunchFailed -= BuildLaunchFailed;
+ BuildManager.BuildStarted -= BuildStarted;
+ BuildManager.BuildFinished -= BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived -= StdOutputReceived;
+ BuildManager.StdErrorReceived -= StdErrorReceived;
}
public void OnAfterDeserialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 02e9d98647..d0cd529d1f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -1,61 +1,93 @@
-using GodotTools.Core;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using GodotTools.BuildLogger;
-using GodotTools.Internals;
using GodotTools.Utils;
-using Directory = System.IO.Directory;
namespace GodotTools.Build
{
public static class BuildSystem
{
- private static string MonoWindowsBinDir
+ private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
- {
- string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin");
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (!Directory.Exists(monoWinBinDir))
- throw new FileNotFoundException("Cannot find the Windows Mono install bin directory.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- return monoWinBinDir;
- }
+ var startInfo = new ProcessStartInfo(dotnetPath);
+
+ BuildArguments(buildInfo, startInfo.ArgumentList);
+
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
+
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+ startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
+
+ // Needed when running from Developer Command Prompt for VS
+ RemovePlatformVariable(startInfo.EnvironmentVariables);
+
+ var process = new Process { StartInfo = startInfo };
+
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
+
+ process.Start();
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ return process;
}
- private static Godot.EditorSettings EditorSettings =>
- GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ {
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ {
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
+ }
- private static bool UsingMonoMsBuildOnWindows
+ public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
- if (OS.IsWindows)
- {
- return (BuildTool)EditorSettings.GetSetting("mono/builds/build_tool")
- == BuildTool.MsBuildMono;
- }
+ await process.WaitForExitAsync();
- return false;
+ return process.ExitCode;
}
}
- private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static Process LaunchPublish(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- (string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (msbuildPath == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- string compilerArgs = BuildArguments(buildTool, buildInfo);
+ var startInfo = new ProcessStartInfo(dotnetPath);
- var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
+ BuildPublishArguments(buildInfo, startInfo.ArgumentList);
- string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
stdOutHandler?.Invoke(launchMessage);
if (Godot.OS.IsStdoutVerbose())
Console.WriteLine(launchMessage);
@@ -63,27 +95,16 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
- startInfo.CreateNoWindow = true;
-
- if (UsingMonoMsBuildOnWindows)
- {
- // These environment variables are required for Mono's MSBuild to find the compilers.
- // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
- string monoWinBinDir = MonoWindowsBinDir;
- startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
- startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
- startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
- }
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
- var process = new Process {StartInfo = startInfo};
+ var process = new Process { StartInfo = startInfo };
if (stdOutHandler != null)
- process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
if (stdErrHandler != null)
- process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
process.Start();
@@ -93,9 +114,9 @@ namespace GodotTools.Build
return process;
}
- public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ public static int Publish(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ using (var process = LaunchPublish(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,38 +124,102 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ // `dotnet clean` / `dotnet build` commands
+ arguments.Add(buildInfo.OnlyClean ? "clean" : "build");
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // `dotnet clean` doesn't recognize these options
+ if (!buildInfo.OnlyClean)
{
- await process.WaitForExitAsync();
+ // Restore
+ // `dotnet build` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
+
+ // Incremental or rebuild
+ if (buildInfo.Rebuild)
+ arguments.Add("--no-incremental");
+ }
- return process.ExitCode;
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
+
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
}
}
- private static string BuildArguments(BuildTool buildTool, BuildInfo buildInfo)
+ private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- string arguments = string.Empty;
+ arguments.Add("publish"); // `dotnet publish` command
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // Restore
+ // `dotnet publish` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
- if (buildTool == BuildTool.DotnetCli)
- arguments += "msbuild"; // `dotnet msbuild` command
+ // Incremental or rebuild
+ // TODO: Not supported in `dotnet publish` (https://github.com/dotnet/sdk/issues/11099)
+ // if (buildInfo.Rebuild)
+ // arguments.Add("--no-incremental");
- arguments += $@" ""{buildInfo.Solution}""";
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
- if (buildInfo.Restore)
- arguments += " /restore";
+ // Runtime Identifier
+ arguments.Add("-r");
+ arguments.Add(buildInfo.RuntimeIdentifier!);
- arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " +
- $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
- $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
+ // Self-published
+ arguments.Add("--self-contained");
+ arguments.Add("true");
- foreach (string customProperty in buildInfo.CustomProperties)
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
+ }
+
+ // Publish output directory
+ if (buildInfo.PublishOutputDir != null)
{
- arguments += " /p:" + customProperty;
+ arguments.Add("-o");
+ arguments.Add(buildInfo.PublishOutputDir);
}
+ }
+
+ private static void AddLoggerArgument(BuildInfo buildInfo, Collection<string> arguments)
+ {
+ string buildLoggerPath = Path.Combine(Internals.GodotSharpDirs.DataEditorToolsDir,
+ "GodotTools.BuildLogger.dll");
- return arguments;
+ arguments.Add(
+ $"-l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}");
}
private static void RemovePlatformVariable(StringDictionary environmentVariables)
@@ -145,7 +230,7 @@ namespace GodotTools.Build
foreach (string env in environmentVariables.Keys)
{
- if (env.ToUpper() == "PLATFORM")
+ if (env.ToUpperInvariant() == "PLATFORM")
platformEnvironmentVariables.Add(env);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
deleted file mode 100644
index 837c8adddb..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace GodotTools.Build
-{
- public enum BuildTool : long
- {
- MsBuildMono,
- MsBuildVs,
- JetBrainsMsBuild,
- DotnetCli
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
new file mode 100644
index 0000000000..b437c7e742
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Runtime.InteropServices;
+using JetBrains.Annotations;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools.Build
+{
+ public static class DotNetFinder
+ {
+ [CanBeNull]
+ public static string FindDotNetExe()
+ {
+ // In the future, this method may do more than just search in PATH. We could look in
+ // known locations or use Godot's linked nethost to search from the hostfxr location.
+
+ if (OS.IsMacOS)
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.X64)
+ {
+ string dotnet_x64 = "/usr/local/share/dotnet/x64/dotnet"; // Look for x64 version, when running under Rosetta 2.
+ if (File.Exists(dotnet_x64))
+ {
+ return dotnet_x64;
+ }
+ }
+ string dotnet = "/usr/local/share/dotnet/dotnet"; // Look for native version.
+ if (File.Exists(dotnet))
+ {
+ return dotnet;
+ }
+ }
+
+ return OS.PathWhich("dotnet");
+ }
+
+ public static bool TryFindDotNetSdk(
+ Version expectedVersion,
+ [NotNullWhen(true)] out Version version,
+ [NotNullWhen(true)] out string path
+ )
+ {
+ version = null;
+ path = null;
+
+ string dotNetExe = FindDotNetExe();
+
+ if (string.IsNullOrEmpty(dotNetExe))
+ return false;
+
+ using Process process = new Process();
+ process.StartInfo = new ProcessStartInfo(dotNetExe, "--list-sdks")
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true
+ };
+
+ process.StartInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US";
+
+ var lines = new List<string>();
+
+ process.OutputDataReceived += (_, e) =>
+ {
+ if (!string.IsNullOrWhiteSpace(e.Data))
+ lines.Add(e.Data);
+ };
+
+ try
+ {
+ process.Start();
+ }
+ catch
+ {
+ return false;
+ }
+
+ process.BeginOutputReadLine();
+ process.WaitForExit();
+
+ Version latestVersionMatch = null;
+ string matchPath = null;
+
+ foreach (var line in lines)
+ {
+ string[] sdkLineParts = line.Trim()
+ .Split(' ', 2, StringSplitOptions.TrimEntries);
+
+ if (sdkLineParts.Length < 2)
+ continue;
+
+ if (!Version.TryParse(sdkLineParts[0], out var lineVersion))
+ continue;
+
+ // We're looking for the exact same major version
+ if (lineVersion.Major != expectedVersion.Major)
+ continue;
+
+ if (latestVersionMatch != null && lineVersion < latestVersionMatch)
+ continue;
+
+ latestVersionMatch = lineVersion;
+ matchPath = sdkLineParts[1].TrimStart('[').TrimEnd(']');
+ }
+
+ if (latestVersionMatch == null)
+ return false;
+
+ version = latestVersionMatch;
+ path = Path.Combine(matchPath!, version.ToString());
+
+ return true;
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 3c020a2589..2e438f3f8f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -1,13 +1,12 @@
using System;
using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
namespace GodotTools.Build
{
- public class MSBuildPanel : VBoxContainer
+ public partial class MSBuildPanel : VBoxContainer
{
public BuildOutputView BuildOutputView { get; private set; }
@@ -28,7 +27,6 @@ namespace GodotTools.Build
BuildOutputView.UpdateIssuesList();
}
- [UsedImplicitly]
public void BuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -57,7 +55,6 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void RebuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -73,7 +70,7 @@ namespace GodotTools.Build
GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
- if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" }))
+ if (!BuildManager.BuildProjectBlocking("Debug", rebuild: true))
return; // Build failed
// Notify running game for hot-reload
@@ -86,18 +83,17 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void CleanSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return; // No solution to build
- BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" });
+ _ = BuildManager.CleanProjectBlocking("Debug");
}
private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
- private void BuildMenuOptionPressed(int id)
+ private void BuildMenuOptionPressed(long id)
{
switch ((BuildMenuOptions)id)
{
@@ -126,10 +122,10 @@ namespace GodotTools.Build
{
base._Ready();
- CustomMinimumSize = new Vector2(0, 228) * EditorScale;
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+ CustomMinimumSize = new Vector2i(0, (int)(228 * EditorScale));
+ SizeFlagsVertical = SizeFlags.ExpandFill;
- var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
AddChild(toolBarHBox);
_buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
@@ -143,7 +139,7 @@ namespace GodotTools.Build
_errorsBtn = new Button
{
- HintTooltip = "Show Errors".TTR(),
+ TooltipText = "Show Errors".TTR(),
Icon = GetThemeIcon("StatusError", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
@@ -155,7 +151,7 @@ namespace GodotTools.Build
_warningsBtn = new Button
{
- HintTooltip = "Show Warnings".TTR(),
+ TooltipText = "Show Warnings".TTR(),
Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
@@ -179,7 +175,7 @@ namespace GodotTools.Build
AddChild(BuildOutputView);
}
- public override void _Notification(int what)
+ public override void _Notification(long what)
{
base._Notification(what);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
deleted file mode 100644
index a859c6f717..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Godot;
-using GodotTools.Ides.Rider;
-using GodotTools.Internals;
-using Directory = System.IO.Directory;
-using Environment = System.Environment;
-using File = System.IO.File;
-using Path = System.IO.Path;
-using OS = GodotTools.Utils.OS;
-
-namespace GodotTools.Build
-{
- public static class MsBuildFinder
- {
- private static string _msbuildToolsPath = string.Empty;
- private static string _msbuildUnixPath = string.Empty;
-
- public static (string, BuildTool) FindMsBuild()
- {
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
-
- if (OS.IsWindows)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = OS.PathWhich("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
- goto case BuildTool.MsBuildVs;
- }
- case BuildTool.MsBuildVs:
- {
- if (string.IsNullOrEmpty(_msbuildToolsPath) || !File.Exists(_msbuildToolsPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildToolsPath = FindMsBuildToolsPathOnWindows();
-
- if (string.IsNullOrEmpty(_msbuildToolsPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
- }
-
- if (!_msbuildToolsPath.EndsWith("\\"))
- _msbuildToolsPath += "\\";
-
- return (Path.Combine(_msbuildToolsPath, "MSBuild.exe"), BuildTool.MsBuildVs);
- }
- case BuildTool.MsBuildMono:
- {
- string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildMono}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.MsBuildMono);
- }
- case BuildTool.JetBrainsMsBuild:
- {
- string editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
-
- if (!File.Exists(editorPath))
- throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}");
-
- var riderDir = new FileInfo(editorPath).Directory?.Parent;
-
- string msbuildPath = Path.Combine(riderDir.FullName, @"tools\MSBuild\Current\Bin\MSBuild.exe");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildJetBrains}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.JetBrainsMsBuild);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- if (OS.IsUnixLike)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = FindBuildEngineOnUnix("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
- goto case BuildTool.MsBuildMono;
- }
- case BuildTool.MsBuildMono:
- {
- if (string.IsNullOrEmpty(_msbuildUnixPath) || !File.Exists(_msbuildUnixPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
- }
-
- if (string.IsNullOrEmpty(_msbuildUnixPath))
- throw new FileNotFoundException($"Cannot find binary for '{BuildManager.PropNameMSBuildMono}'");
-
- return (_msbuildUnixPath, BuildTool.MsBuildMono);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- throw new PlatformNotSupportedException();
- }
-
- private static IEnumerable<string> MsBuildHintDirs
- {
- get
- {
- var result = new List<string>();
-
- if (OS.IsMacOS)
- {
- result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
- result.Add("/opt/local/bin/");
- result.Add("/usr/local/var/homebrew/linked/mono/bin/");
- result.Add("/usr/local/bin/");
- result.Add("/usr/local/bin/dotnet/");
- result.Add("/usr/local/share/dotnet/");
- }
-
- result.Add("/opt/novell/mono/bin/");
-
- return result;
- }
- }
-
- private static string FindBuildEngineOnUnix(string name)
- {
- string ret = OS.PathWhich(name);
-
- if (!string.IsNullOrEmpty(ret))
- return ret;
-
- string retFallback = OS.PathWhich($"{name}.exe");
-
- if (!string.IsNullOrEmpty(retFallback))
- return retFallback;
-
- foreach (string hintDir in MsBuildHintDirs)
- {
- string hintPath = Path.Combine(hintDir, name);
-
- if (File.Exists(hintPath))
- return hintPath;
- }
-
- return string.Empty;
- }
-
- private static string FindMsBuildToolsPathOnWindows()
- {
- if (!OS.IsWindows)
- throw new PlatformNotSupportedException();
-
- // Try to find 15.0 with vswhere
-
- string[] envNames = Internal.GodotIs32Bits() ?
- envNames = new[] { "ProgramFiles", "ProgramW6432" } :
- envNames = new[] { "ProgramFiles(x86)", "ProgramFiles" };
-
- string vsWherePath = null;
- foreach (var envName in envNames)
- {
- vsWherePath = Environment.GetEnvironmentVariable(envName);
- if (!string.IsNullOrEmpty(vsWherePath))
- {
- vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
- if (File.Exists(vsWherePath))
- break;
- }
-
- vsWherePath = null;
- }
-
- var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
-
- var outputArray = new Godot.Collections.Array<string>();
- int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
- output: (Godot.Collections.Array)outputArray);
-
- if (exitCode != 0)
- return string.Empty;
-
- if (outputArray.Count == 0)
- return string.Empty;
-
- var lines = outputArray[0].Split('\n');
-
- foreach (string line in lines)
- {
- int sepIdx = line.IndexOf(':');
-
- if (sepIdx <= 0)
- continue;
-
- string key = line.Substring(0, sepIdx); // No need to trim
-
- if (key != "installationPath")
- continue;
-
- string value = line.Substring(sepIdx + 1).StripEdges();
-
- if (string.IsNullOrEmpty(value))
- throw new FormatException("installationPath value is empty");
-
- if (!value.EndsWith("\\"))
- value += "\\";
-
- // Since VS2019, the directory is simply named "Current"
- string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
-
- if (Directory.Exists(msbuildDir))
- return msbuildDir;
-
- // Directory name "15.0" is used in VS 2017
- return Path.Combine(value, "MSBuild\\15.0\\Bin");
- }
-
- return string.Empty;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index 63b97e981e..fe309b8102 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -21,70 +22,13 @@ namespace GodotTools.Build
public static string GodotFallbackFolderPath
=> Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
- private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.Load(nuGetConfigPath);
-
- const string nuGetConfigRootName = "configuration";
-
- var rootNode = xmlDoc.DocumentElement;
-
- if (rootNode == null)
- {
- // No root node, create it
- rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
- xmlDoc.AppendChild(rootNode);
-
- // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
- XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
- rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
- }
- else
- {
- // Check that the root node is the expected one
- if (rootNode.Name != nuGetConfigRootName)
- throw new Exception("Invalid root Xml node for NuGet.Config. " +
- $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
- }
-
- var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
- rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
-
- // Check if it already has our fallback package folder
- for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
- {
- if (xmlNode.NodeType != XmlNodeType.Element)
- continue;
-
- var xmlElement = (XmlElement)xmlNode;
- if (xmlElement.Name == "add" &&
- xmlElement.Attributes["key"]?.Value == name &&
- xmlElement.Attributes["value"]?.Value == path)
- {
- return;
- }
- }
-
- XmlElement newEntry = xmlDoc.CreateElement("add");
- newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
- newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
-
- fallbackFoldersNode.AppendChild(newEntry);
-
- xmlDoc.Save(nuGetConfigPath);
- }
-
/// <summary>
- /// Returns all the paths where the user NuGet.Config files can be found.
+ /// Returns all the paths where the Godot.Offline.Config files can be found.
/// Does not determine whether the returned files exist or not.
/// </summary>
- private static string[] GetAllUserNuGetConfigFilePaths()
+ private static string[] GetAllGodotNuGetConfigFilePaths()
{
- // Where to find 'NuGet/NuGet.Config':
+ // Where to find 'NuGet/config/Godot.Offline.Config':
//
// - Mono/.NETFramework (standalone NuGet):
// Uses Environment.SpecialFolder.ApplicationData
@@ -96,10 +40,12 @@ namespace GodotTools.Build
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ const string configFileName = "Godot.Offline.Config";
+
if (Utils.OS.IsWindows)
{
// %APPDATA% for both
- return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
+ return new[] { Path.Combine(applicationData, "NuGet", "config", configFileName) };
}
var paths = new string[2];
@@ -109,20 +55,20 @@ namespace GodotTools.Build
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
if (!string.IsNullOrEmpty(dotnetCliHome))
{
- paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "config", configFileName);
}
else
{
string home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
- paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "config", configFileName);
}
// Mono/.NETFramework (standalone NuGet)
// ApplicationData is $HOME/.config on Linux/macOS
- paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+ paths[1] = Path.Combine(applicationData, "NuGet", "config", configFileName);
return paths;
}
@@ -139,28 +85,26 @@ namespace GodotTools.Build
// The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
/// <summary>
- /// Adds the specified fallback folder to the user NuGet.Config files,
+ /// Adds the specified fallback folder to the Godot.Offline.Config files,
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
/// </summary>
- public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ public static void AddFallbackFolderToGodotNuGetConfigs(string name, string path)
{
- foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ // Make sure the fallback folder exists to avoid error:
+ // MSB4018: The "ResolvePackageAssets" task failed unexpectedly.
+ System.IO.Directory.CreateDirectory(path);
+
+ foreach (string nuGetConfigPath in GetAllGodotNuGetConfigFilePaths())
{
- if (!System.IO.File.Exists(nuGetConfigPath))
- {
- // It doesn't exist, so we create a default one
- const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
+ string defaultConfig = @$"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
- <packageSources>
- <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
- </packageSources>
+ <fallbackPackageFolders>
+ <add key=""{name}"" value=""{path}"" />
+ </fallbackPackageFolders>
</configuration>
";
- System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
- System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
- }
-
- AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
}
}
@@ -181,12 +125,13 @@ namespace GodotTools.Build
// - The sha512 of the nupkg is base64 encoded.
// - We can get the nuspec from the nupkg which is a Zip file.
- string packageIdLower = packageId.ToLower();
- string packageVersionLower = packageVersion.ToLower();
+ string packageIdLower = packageId.ToLowerInvariant();
+ string packageVersionLower = packageVersion.ToLowerInvariant();
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+ string nupkgMetadataDestPath = Path.Combine(destDir, ".nupkg.metadata");
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
return; // Already added (for speed we don't check if every file is properly extracted)
@@ -195,12 +140,18 @@ namespace GodotTools.Build
// Generate .nupkg.sha512 file
- using (var alg = SHA512.Create())
- {
- alg.ComputeHash(File.ReadAllBytes(nupkgPath));
- string base64Hash = Convert.ToBase64String(alg.Hash);
- File.WriteAllText(nupkgSha512DestPath, base64Hash);
- }
+ byte[] hash = SHA512.HashData(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+
+ // Generate .nupkg.metadata file
+ // Spec: https://github.com/NuGet/Home/wiki/Nupkg-Metadata-File
+
+ File.WriteAllText(nupkgMetadataDestPath, @$"{{
+ ""version"": 2,
+ ""contentHash"": ""{base64Hash}"",
+ ""source"": null
+}}");
// Extract nupkg
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
@@ -227,9 +178,11 @@ namespace GodotTools.Build
var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
if (nuspecEntry == null)
- throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+ throw new InvalidOperationException(
+ $"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
- nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name
+ .ToLowerInvariant().SimplifyGodotPath()));
// Extract the other package files
@@ -247,7 +200,7 @@ namespace GodotTools.Build
entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
// Nuspec at root level. We already extracted it previously but in lower case.
- entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
+ !entryFullName.Contains('/') && entryFullName.EndsWith(".nuspec"))
{
continue;
}
@@ -293,6 +246,8 @@ namespace GodotTools.Build
{
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
+ ("GodotSharp", GeneratedGodotNupkgsVersions.GodotSharp),
+ ("GodotSharpEditor", GeneratedGodotNupkgsVersions.GodotSharp),
};
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index e9718cc82c..94efcba3f1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -22,7 +22,7 @@ namespace GodotTools.Export
public bool FullAot;
private bool _useInterpreter;
- public bool UseInterpreter { get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; }
+ public bool UseInterpreter { readonly get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; }
public string[] ExtraAotOptions;
public string[] ExtraOptimizerOptions;
@@ -75,8 +75,24 @@ namespace GodotTools.Export
}
else
{
- string bits = features.Contains("64") ? "64" : features.Contains("32") ? "32" : null;
- CompileAssembliesForDesktop(exporter, platform, isDebug, bits, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
+ string arch = "";
+ if (features.Contains("x86_64"))
+ {
+ arch = "x86_64";
+ }
+ else if (features.Contains("x86_32"))
+ {
+ arch = "x86_32";
+ }
+ else if (features.Contains("arm64"))
+ {
+ arch = "arm64";
+ }
+ else if (features.Contains("arm32"))
+ {
+ arch = "arm32";
+ }
+ CompileAssembliesForDesktop(exporter, platform, isDebug, arch, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
}
}
@@ -112,7 +128,7 @@ namespace GodotTools.Export
}
}
- public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string bits, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
+ public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string arch, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
{
foreach (var assembly in assemblies)
{
@@ -126,9 +142,9 @@ namespace GodotTools.Export
string outputFileName = assemblyName + ".dll" + outputFileExtension;
string tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
- var compilerArgs = GetAotCompilerArgs(platform, isDebug, bits, aotOpts, assemblyPath, tempOutputFilePath);
+ var compilerArgs = GetAotCompilerArgs(platform, isDebug, arch, aotOpts, assemblyPath, tempOutputFilePath);
- string compilerDirPath = GetMonoCrossDesktopDirName(platform, bits);
+ string compilerDirPath = GetMonoCrossDesktopDirName(platform, arch);
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
@@ -203,7 +219,7 @@ namespace GodotTools.Export
int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs);
if (clangExitCode != 0)
- throw new Exception($"Command 'clang' exited with code: {clangExitCode}");
+ throw new InvalidOperationException($"Command 'clang' exited with code: {clangExitCode}.");
objFilePathsForiOSArch[arch].Add(objFilePath);
}
@@ -289,7 +305,7 @@ MONO_AOT_MODE_LAST = 1000,
// Archive the AOT object files into a static library
var arFilePathsForAllArchs = new List<string>();
- string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName;
+ string projectAssemblyName = GodotSharpDirs.ProjectAssemblyName;
foreach (var archPathsPair in objFilePathsForiOSArch)
{
@@ -309,7 +325,7 @@ MONO_AOT_MODE_LAST = 1000,
int arExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("ar"), arArgs);
if (arExitCode != 0)
- throw new Exception($"Command 'ar' exited with code: {arExitCode}");
+ throw new InvalidOperationException($"Command 'ar' exited with code: {arExitCode}.");
arFilePathsForAllArchs.Add(arOutputFilePath);
}
@@ -327,7 +343,7 @@ MONO_AOT_MODE_LAST = 1000,
int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs);
if (lipoExitCode != 0)
- throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}");
+ throw new InvalidOperationException($"Command 'lipo' exited with code: {lipoExitCode}.");
// TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator
@@ -427,14 +443,14 @@ MONO_AOT_MODE_LAST = 1000,
}
else if (!Directory.Exists(androidToolchain))
{
- throw new FileNotFoundException("Android toolchain not found: " + androidToolchain);
+ throw new FileNotFoundException($"Android toolchain not found: '{androidToolchain}'.");
}
var androidToolPrefixes = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "arm-linux-androideabi-",
- ["arm64-v8a"] = "aarch64-linux-android-",
- ["x86"] = "i686-linux-android-",
+ ["arm32"] = "arm-linux-androideabi-",
+ ["arm64"] = "aarch64-linux-android-",
+ ["x86_32"] = "i686-linux-android-",
["x86_64"] = "x86_64-linux-android-"
};
@@ -524,12 +540,12 @@ MONO_AOT_MODE_LAST = 1000,
Console.WriteLine($"Running: \"{process.StartInfo.FileName}\" {process.StartInfo.Arguments}");
if (!process.Start())
- throw new Exception("Failed to start process for Mono AOT compiler");
+ throw new InvalidOperationException("Failed to start process for Mono AOT compiler.");
process.WaitForExit();
if (process.ExitCode != 0)
- throw new Exception($"Mono AOT compiler exited with code: {process.ExitCode}");
+ throw new InvalidOperationException($"Mono AOT compiler exited with code: {process.ExitCode}.");
}
}
@@ -547,9 +563,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var androidAbis = new[]
{
- "armeabi-v7a",
- "arm64-v8a",
- "x86",
+ "arm32",
+ "arm64",
+ "x86_32",
"x86_64"
};
@@ -560,9 +576,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var abiArchs = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "armv7",
- ["arm64-v8a"] = "aarch64-v8a",
- ["x86"] = "i686",
+ ["arm32"] = "armv7",
+ ["arm64"] = "aarch64-v8a",
+ ["x86_32"] = "i686",
["x86_64"] = "x86_64"
};
@@ -571,31 +587,25 @@ MONO_AOT_MODE_LAST = 1000,
return $"{arch}-linux-android";
}
- private static string GetMonoCrossDesktopDirName(string platform, string bits)
+ private static string GetMonoCrossDesktopDirName(string platform, string arch)
{
switch (platform)
{
case OS.Platforms.Windows:
case OS.Platforms.UWP:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"windows-{arch}";
}
case OS.Platforms.MacOS:
{
- Debug.Assert(bits == null || bits == "64");
- string arch = "x86_64";
return $"{platform}-{arch}";
}
case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"linux-{arch}";
}
case OS.Platforms.Haiku:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"{platform}-{arch}";
}
default:
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index cca18a2a1f..db96003baf 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -4,11 +4,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@@ -17,61 +15,17 @@ using Path = System.IO.Path;
namespace GodotTools.Export
{
- public class ExportPlugin : EditorExportPlugin
+ public partial class ExportPlugin : EditorExportPlugin
{
- [Flags]
- private enum I18NCodesets : long
- {
- None = 0,
- CJK = 1,
- MidEast = 2,
- Other = 4,
- Rare = 8,
- West = 16,
- All = CJK | MidEast | Other | Rare | West
- }
-
- private string _maybeLastExportError;
-
- private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir)
- {
- var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets");
-
- if (codesets == I18NCodesets.None)
- return;
+ public override string _GetName() => "C#";
- void AddI18NAssembly(string name) => assemblies.Add(name, Path.Combine(bclDir, $"{name}.dll"));
-
- AddI18NAssembly("I18N");
-
- if ((codesets & I18NCodesets.CJK) != 0)
- AddI18NAssembly("I18N.CJK");
- if ((codesets & I18NCodesets.MidEast) != 0)
- AddI18NAssembly("I18N.MidEast");
- if ((codesets & I18NCodesets.Other) != 0)
- AddI18NAssembly("I18N.Other");
- if ((codesets & I18NCodesets.Rare) != 0)
- AddI18NAssembly("I18N.Rare");
- if ((codesets & I18NCodesets.West) != 0)
- AddI18NAssembly("I18N.West");
- }
+ private List<string> _tempFolders = new List<string>();
public void RegisterExportSettings()
{
// TODO: These would be better as export preset options, but that doesn't seem to be supported yet
GlobalDef("mono/export/include_scripts_content", false);
- GlobalDef("mono/export/export_assemblies_inside_pck", true);
-
- GlobalDef("mono/export/i18n_codesets", I18NCodesets.All);
-
- ProjectSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Variant.Type.Int,
- ["name"] = "mono/export/i18n_codesets",
- ["hint"] = PropertyHint.Flags,
- ["hint_string"] = "CJK,MidEast,Other,Rare,West"
- });
GlobalDef("mono/export/aot/enabled", false);
GlobalDef("mono/export/aot/full_aot", false);
@@ -85,11 +39,7 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/android_toolchain_path", "");
}
- private void AddFile(string srcPath, string dstPath, bool remap = false)
- {
- // Add file to the PCK
- AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap);
- }
+ private string _maybeLastExportError;
// With this method we can override how a file is exported in the PCK
public override void _ExportFile(string path, string type, string[] features)
@@ -100,7 +50,9 @@ namespace GodotTools.Export
return;
if (Path.GetExtension(path) != Internal.CSharpLanguageExtension)
- throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
+ throw new ArgumentException(
+ $"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
+ nameof(path));
// TODO What if the source file is not part of the game's C# project
@@ -119,7 +71,7 @@ namespace GodotTools.Export
}
}
- public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
+ public override void _ExportBegin(string[] features, bool isDebug, string path, long flags)
{
base._ExportBegin(features, isDebug, path, flags);
@@ -142,7 +94,7 @@ namespace GodotTools.Export
}
}
- private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
+ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long flags)
{
_ = flags; // Unused
@@ -150,161 +102,111 @@ namespace GodotTools.Export
return;
if (!DeterminePlatformFromFeatures(features, out string platform))
- throw new NotSupportedException("Target platform not supported");
+ throw new NotSupportedException("Target platform not supported.");
+
+ if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
+ .Contains(platform))
+ {
+ throw new NotImplementedException("Target platform not yet implemented.");
+ }
string outputDir = new FileInfo(path).Directory?.FullName ??
- throw new FileNotFoundException("Base directory not found");
+ throw new FileNotFoundException("Output base directory not found.");
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
- throw new Exception("Failed to build project");
-
- // Add dependency assemblies
-
- var assemblies = new Godot.Collections.Dictionary<string, string>();
-
- string projectDllName = GodotSharpEditor.ProjectAssemblyName;
- string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
- string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
-
- assemblies[projectDllName] = projectDllSrcPath;
-
- string bclDir = DeterminePlatformBclDir(platform);
-
- if (platform == OS.Platforms.Android)
+ var archs = new List<string>();
+ if (features.Contains("x86_64"))
{
- string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
- string monoAndroidAssemblyPath = Path.Combine(godotAndroidExtProfileDir, "Mono.Android.dll");
-
- if (!File.Exists(monoAndroidAssemblyPath))
- throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
-
- assemblies["Mono.Android"] = monoAndroidAssemblyPath;
+ archs.Add("x86_64");
}
- else if (platform == OS.Platforms.HTML5)
+ else if (features.Contains("x86_32"))
{
- // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies.
- // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies
- // reference a different version even though the assembly is the same, for some weird reason.
-
- var wasmFrameworkAssemblies = new[] { "WebAssembly.Bindings", "WebAssembly.Net.WebSockets" };
-
- foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies)
+ archs.Add("x86_32");
+ }
+ else if (features.Contains("arm64"))
+ {
+ archs.Add("arm64");
+ }
+ else if (features.Contains("universal"))
+ {
+ if (platform == OS.Platforms.MacOS)
{
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
- assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
+ archs.Add("x86_64");
+ archs.Add("arm64");
}
+ }
- // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority.
- (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[]
- {
- ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http")
- };
-
- foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
+ foreach (var arch in archs)
+ {
+ string ridOS = DetermineRuntimeIdentifierOS(platform);
+ string ridArch = DetermineRuntimeIdentifierArch(arch);
+ string runtimeIdentifier = $"{ridOS}-{ridArch}";
+ string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}";
+ if (platform == OS.Platforms.MacOS)
{
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
- if (File.Exists(thisWasmFrameworkAssemblyPath))
- {
- assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
- }
- else
- {
- thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- {
- throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
- $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
- thisWasmFrameworkAssemblyPath);
- }
-
- assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath;
- }
+ projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
}
- }
-
- var initialAssemblies = assemblies.Duplicate();
- internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
- AddI18NAssemblies(assemblies, bclDir);
+ // Create temporary publish output directory
- string outputDataDir = null;
+ string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
+ $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
- if (PlatformHasTemplateDir(platform))
- outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+ _tempFolders.Add(publishOutputTempDir);
- string apiConfig = isDebug ? "Debug" : "Release";
- string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
+ if (!Directory.Exists(publishOutputTempDir))
+ Directory.CreateDirectory(publishOutputTempDir);
- bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
-
- if (!assembliesInsidePck)
- {
- string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies");
- if (!Directory.Exists(outputDataGameAssembliesDir))
- Directory.CreateDirectory(outputDataGameAssembliesDir);
- }
+ // Execute dotnet publish
- foreach (var assembly in assemblies)
- {
- void AddToAssembliesDir(string fileSrcPath)
+ if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
+ runtimeIdentifier, publishOutputTempDir))
{
- if (assembliesInsidePck)
- {
- string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile());
- AddFile(fileSrcPath, fileDstPath);
- }
- else
- {
- Debug.Assert(outputDataDir != null);
- string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile());
- File.Copy(fileSrcPath, fileDstPath);
- }
+ throw new InvalidOperationException("Failed to build project.");
}
- string assemblySrcPath = assembly.Value;
+ string soExt = ridOS switch
+ {
+ OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
+ OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
+ _ => "so"
+ };
- string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null);
- string pdbSrcPath = assemblyPathWithoutExtension + ".pdb";
+ if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
+ // NativeAOT shared library output
+ && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
+ {
+ throw new NotSupportedException(
+ "Publish succeeded but project assembly not found in the output directory");
+ }
- AddToAssembliesDir(assemblySrcPath);
+ // Add to the exported project shared object list.
- if (File.Exists(pdbSrcPath))
- AddToAssembliesDir(pdbSrcPath);
+ foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
+ {
+ AddSharedObject(file, tags: null, projectDataDirName);
+ }
}
+ }
- // AOT compilation
- bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
+ private string DetermineRuntimeIdentifierOS(string platform)
+ => OS.DotNetOSPlatformMap[platform];
- if (aotEnabled)
+ private string DetermineRuntimeIdentifierArch(string arch)
+ {
+ return arch switch
{
- string aotToolchainPath = null;
-
- if (platform == OS.Platforms.Android)
- aotToolchainPath = (string)ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
-
- if (aotToolchainPath == string.Empty)
- aotToolchainPath = null; // Don't risk it being used as current working dir
-
- // TODO: LLVM settings are hard-coded and disabled for now
- var aotOpts = new AotOptions
- {
- EnableLLVM = false,
- LLVMOnly = false,
- LLVMPath = "",
- LLVMOutputPath = "",
- FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
- UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
- ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
- ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
- ToolchainPath = aotToolchainPath
- };
-
- AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
- }
+ "x86" => "x86",
+ "x86_32" => "x86",
+ "x64" => "x64",
+ "x86_64" => "x64",
+ "armeabi-v7a" => "arm",
+ "arm64-v8a" => "arm64",
+ "armv7" => "arm",
+ "arm64" => "arm64",
+ _ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
+ };
}
public override void _ExportEnd()
@@ -316,79 +218,29 @@ namespace GodotTools.Export
if (Directory.Exists(aotTempDir))
Directory.Delete(aotTempDir, recursive: true);
- // TODO: Just a workaround until the export plugins can be made to abort with errors
- if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
+ foreach (string folder in _tempFolders)
{
- string lastExportError = _maybeLastExportError;
- _maybeLastExportError = null;
-
- GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project");
+ Directory.Delete(folder, recursive: true);
}
- }
+ _tempFolders.Clear();
- [NotNull]
- private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
- {
- string target = isDebug ? "release_debug" : "release";
+ // TODO: The following is just a workaround until the export plugins can be made to abort with errors
- // NOTE: Bits is ok for now as all platforms with a data directory only have one or two architectures.
- // However, this may change in the future if we add arm linux or windows desktop templates.
- string bits = features.Contains("64") ? "64" : "32";
-
- string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
-
- string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- bool validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
+ // We check for empty as well, because it's set to empty after hot-reloading
+ if (!string.IsNullOrEmpty(_maybeLastExportError))
{
- validTemplatePathFound = false;
-
- if (isDebug)
- {
- target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
- templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
- validTemplatePathFound = false;
- }
- }
-
- if (!validTemplatePathFound)
- throw new FileNotFoundException("Data template directory not found", templateDirPath);
-
- string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
-
- if (Directory.Exists(outputDataDir))
- Directory.Delete(outputDataDir, recursive: true); // Clean first
-
- Directory.CreateDirectory(outputDataDir);
-
- foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
- {
- Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
- }
+ string lastExportError = _maybeLastExportError;
+ _maybeLastExportError = null;
- foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
- {
- File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
+ GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project");
}
-
- return outputDataDir;
- }
-
- private static bool PlatformHasTemplateDir(string platform)
- {
- // 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);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
{
foreach (var feature in features)
{
- if (OS.PlatformNameMap.TryGetValue(feature, out platform))
+ if (OS.PlatformFeatureMap.TryGetValue(feature, out platform))
return true;
}
@@ -396,87 +248,11 @@ namespace GodotTools.Export
return false;
}
- private static string GetBclProfileDir(string profile)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- return Path.Combine(templatesDir, "bcl", profile);
- }
-
- private static string DeterminePlatformBclDir(string platform)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- string profile = DeterminePlatformBclProfile(platform);
- platformBclDir = Path.Combine(templatesDir, "bcl", profile);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- if (PlatformRequiresCustomBcl(platform))
- throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
-
- platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on
- }
- }
-
- return platformBclDir;
- }
-
- /// <summary>
- /// Determines whether the BCL bundled with the Godot editor can be used for the target platform,
- /// or if it requires a custom BCL that must be distributed with the export templates.
- /// </summary>
- private static bool PlatformRequiresCustomBcl(string platform)
- {
- if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
- return true;
-
- // The 'net_4_x' BCL is not compatible between Windows and the other platforms.
- // We use the names 'net_4_x_win' and 'net_4_x' to differentiate between the two.
-
- bool isWinOrUwp = new[]
- {
- OS.Platforms.Windows,
- OS.Platforms.UWP
- }.Contains(platform);
-
- return OS.IsWindows ? !isWinOrUwp : isWinOrUwp;
- }
-
- private static string DeterminePlatformBclProfile(string platform)
- {
- switch (platform)
- {
- case OS.Platforms.Windows:
- case OS.Platforms.UWP:
- return "net_4_x_win";
- case OS.Platforms.MacOS:
- case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
- case OS.Platforms.Haiku:
- return "net_4_x";
- case OS.Platforms.Android:
- return "monodroid";
- case OS.Platforms.iOS:
- return "monotouch";
- case OS.Platforms.HTML5:
- return "wasm";
- default:
- throw new NotSupportedException($"Platform not supported: {platform}");
- }
- }
-
private static string DetermineDataDirNameForProject()
{
string appName = (string)ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
- string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
index 93ef837a83..4f5bebfb42 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
@@ -16,7 +16,7 @@ namespace GodotTools.Export
_XcodePath = FindXcode();
if (_XcodePath == null)
- throw new Exception("Could not find Xcode");
+ throw new FileNotFoundException("Could not find Xcode.");
}
return _XcodePath;
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index b39c3d1c0d..08147d9f6a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -13,13 +13,14 @@ using GodotTools.Internals;
using GodotTools.ProjectEditor;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
+using Environment = System.Environment;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
using Path = System.IO.Path;
namespace GodotTools
{
- public class GodotSharpEditor : EditorPlugin, ISerializationListener
+ public partial class GodotSharpEditor : EditorPlugin, ISerializationListener
{
private EditorSettings _editorSettings;
@@ -39,43 +40,40 @@ namespace GodotTools
public bool SkipBuildBeforePlaying { get; set; } = false;
- public static string ProjectAssemblyName
+ [UsedImplicitly]
+ private bool CreateProjectSolutionIfNeeded()
{
- get
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath) || !File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
- string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
- projectAssemblyName = projectAssemblyName.ToSafeDirName();
- if (string.IsNullOrEmpty(projectAssemblyName))
- projectAssemblyName = "UnnamedProject";
- return projectAssemblyName;
+ return CreateProjectSolution();
}
+
+ return true;
}
private bool CreateProjectSolution()
{
- using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
+ using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project...".TTR());
- string resourceDir = ProjectSettings.GlobalizePath("res://");
-
- string path = resourceDir;
- string name = ProjectAssemblyName;
-
- string guid = CsProjOperations.GenerateGameProject(path, name);
+ string csprojDir = Path.GetDirectoryName(GodotSharpDirs.ProjectCsProjPath);
+ string slnDir = Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath);
+ string name = GodotSharpDirs.ProjectAssemblyName;
+ string guid = CsProjOperations.GenerateGameProject(csprojDir, name);
if (guid.Length > 0)
{
var solution = new DotNetSolution(name)
{
- DirectoryPath = path
+ DirectoryPath = slnDir
};
var projectInfo = new DotNetSolution.ProjectInfo
{
Guid = guid,
- PathRelativeToSolution = name + ".csproj",
- Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"}
+ PathRelativeToSolution = Path.GetRelativePath(slnDir, GodotSharpDirs.ProjectCsProjPath),
+ Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
solution.AddNewProject(name, projectInfo);
@@ -90,24 +88,6 @@ namespace GodotTools
return false;
}
- pr.Step("Updating Godot API assemblies...".TTR());
-
- string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
-
- if (!string.IsNullOrEmpty(debugApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
- return false;
- }
-
- string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
-
- if (!string.IsNullOrEmpty(releaseApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
- return false;
- }
-
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
@@ -129,7 +109,7 @@ namespace GodotTools
_toolBarBuildButton.Show();
}
- private void _MenuOptionPressed(int id)
+ private void _MenuOptionPressed(long id)
{
switch ((MenuOptions)id)
{
@@ -141,7 +121,8 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
catch (Exception e)
@@ -167,13 +148,6 @@ namespace GodotTools
Instance.MSBuildPanel.BuildSolution();
}
- public override void _Ready()
- {
- base._Ready();
-
- MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
- }
-
private enum MenuOptions
{
CreateSln,
@@ -197,7 +171,7 @@ namespace GodotTools
[UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
- var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor");
+ var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor");
switch (editorId)
{
@@ -219,13 +193,15 @@ namespace GodotTools
try
{
if (Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+ Console.WriteLine(
+ $"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
OS.RunProcess(command, args);
}
catch (Exception e)
{
- GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ GD.PushError(
+ $"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
}
break;
@@ -347,7 +323,8 @@ namespace GodotTools
[UsedImplicitly]
public bool OverridesExternalEditor()
{
- return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
+ return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") !=
+ ExternalEditorId.None;
}
public override bool _Build()
@@ -363,12 +340,12 @@ namespace GodotTools
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
- ?? throw new Exception("Cannot open C# project");
+ ?? throw new InvalidOperationException("Cannot open C# project.");
// NOTE: The order in which changes are made to the project is important
// Migrate to MSBuild project Sdks style if using the old style
- ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, ProjectAssemblyName);
+ ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
@@ -396,10 +373,39 @@ namespace GodotTools
{
base._EnablePlugin();
+ ProjectSettingsChanged += GodotSharpDirs.DetermineProjectLocation;
+
if (Instance != null)
throw new InvalidOperationException();
Instance = this;
+ var dotNetSdkSearchVersion = Environment.Version;
+
+ // First we try to find the .NET Sdk ourselves to make sure we get the
+ // correct version first (`RegisterDefaults` always picks the latest).
+ if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string sdkPath))
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+
+ ProjectUtils.MSBuildLocatorRegisterMSBuildPath(sdkPath);
+ }
+ else
+ {
+ try
+ {
+ ProjectUtils.MSBuildLocatorRegisterDefaults(out sdkVersion, out sdkPath);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+ }
+ catch (InvalidOperationException e)
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ GD.PrintErr(e.ToString());
+ GD.PushError($".NET Sdk not found. The required version is '{dotNetSdkSearchVersion}'.");
+ }
+ }
+
var editorInterface = GetEditorInterface();
var editorBaseControl = editorInterface.GetBaseControl();
@@ -409,9 +415,11 @@ namespace GodotTools
editorBaseControl.AddChild(_errorDialog);
MSBuildPanel = new MSBuildPanel();
+ MSBuildPanel.Ready += () =>
+ MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
_bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
- AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
+ AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
_menuPopup = new PopupMenu();
_menuPopup.Hide();
@@ -423,7 +431,7 @@ namespace GodotTools
_toolBarBuildButton = new Button
{
Text = "Build",
- HintTooltip = "Build Solution".TTR(),
+ TooltipText = "Build Solution".TTR(),
FocusMode = Control.FocusModeEnum.None,
Shortcut = buildSolutionShortcut,
ShortcutInTooltip = true
@@ -445,7 +453,7 @@ namespace GodotTools
_menuPopup.IdPressed += _MenuOptionPressed;
// External editor settings
- EditorDef("mono/editor/external_editor", ExternalEditorId.None);
+ EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None));
string settingsHintStr = "Disabled";
@@ -472,9 +480,9 @@ namespace GodotTools
_editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.Int,
+ ["type"] = (int)Variant.Type.Int,
["name"] = "mono/editor/external_editor",
- ["hint"] = PropertyHint.Enum,
+ ["hint"] = (int)PropertyHint.Enum,
["hint_string"] = settingsHintStr
});
@@ -487,7 +495,8 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
{
@@ -503,20 +512,23 @@ namespace GodotTools
protected override void Dispose(bool disposing)
{
- base.Dispose(disposing);
-
- if (_exportPluginWeak != null)
+ if (disposing)
{
- // We need to dispose our export plugin before the editor destroys EditorSettings.
- // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
- // will be freed after EditorSettings already was, and its device polling thread
- // will try to access the EditorSettings singleton, resulting in null dereferencing.
- (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
+ if (IsInstanceValid(_exportPluginWeak))
+ {
+ // We need to dispose our export plugin before the editor destroys EditorSettings.
+ // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
+ // will be freed after EditorSettings already was, and its device polling thread
+ // will try to access the EditorSettings singleton, resulting in null dereferencing.
+ (_exportPluginWeak.GetRef().AsGodotObject() as ExportPlugin)?.Dispose();
+
+ _exportPluginWeak.Dispose();
+ }
- _exportPluginWeak.Dispose();
+ GodotIdeManager?.Dispose();
}
- GodotIdeManager?.Dispose();
+ base.Dispose(disposing);
}
public void OnBeforeSerialize()
@@ -533,8 +545,10 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
[UsedImplicitly]
- private GodotSharpEditor()
+ private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
+ Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+ return new GodotSharpEditor().NativeInstance;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index f1d45463c5..30525ba04a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -1,14 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ <LangVersion>10</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
+ <ProduceReferenceAssembly>false</ProduceReferenceAssembly>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <!-- Needed for our source generators to work despite this not being a Godot game project -->
+ <PropertyGroup>
+ <IsGodotToolsProject>true</IsGodotToolsProject>
+ </PropertyGroup>
+ <ItemGroup>
+ <CompilerVisibleProperty Include="IsGodotToolsProject" />
+ </ItemGroup>
<PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') ">
<!-- The project is part of the Godot source tree -->
<!-- Use the Godot source tree output folder instead of '$(ProjectDir)/bin' -->
@@ -20,6 +30,8 @@
<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="13.0.1" />
+ <!-- For RiderPathLocator -->
+ <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
@@ -30,6 +42,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="..\..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj" />
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index dd05f28af0..89ac8058b9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -1,14 +1,15 @@
using Godot;
using GodotTools.Internals;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
namespace GodotTools
{
- public class HotReloadAssemblyWatcher : Node
+ public partial class HotReloadAssemblyWatcher : Node
{
private Timer _watchTimer;
- public override void _Notification(int what)
+ public override void _Notification(long what)
{
if (what == Node.NotificationWmWindowFocusIn)
{
@@ -25,6 +26,7 @@ namespace GodotTools
Internal.ReloadAssemblies(softReload: false);
}
+ [UsedImplicitly]
public void RestartTimer()
{
_watchTimer.Stop();
@@ -38,7 +40,7 @@ namespace GodotTools
_watchTimer = new Timer
{
OneShot = false,
- WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
+ WaitTime = 0.5f
};
_watchTimer.Timeout += TimerTimeout;
AddChild(_watchTimer);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 23339fe50b..9df90ac608 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -8,7 +8,7 @@ using GodotTools.Internals;
namespace GodotTools.Ides
{
- public sealed class GodotIdeManager : Node, ISerializationListener
+ public sealed partial class GodotIdeManager : Node, ISerializationListener
{
private MessagingServer _messagingServer;
@@ -76,7 +76,7 @@ namespace GodotTools.Ides
public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
{
- var editorId = (ExternalEditorId)GodotSharpEditor.Instance.GetEditorInterface()
+ var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface()
.GetEditorSettings().GetSetting("mono/editor/external_editor");
string editorIdentity = GetExternalEditorIdentity(editorId);
@@ -153,7 +153,7 @@ namespace GodotTools.Ides
}
default:
- throw new ArgumentOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(editorId));
}
}
@@ -180,17 +180,17 @@ namespace GodotTools.Ides
public void SendOpenFile(string file)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file });
}
public void SendOpenFile(string file, int line)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file, Line = line});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file, Line = line });
}
public void SendOpenFile(string file, int line, int column)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file, Line = line, Column = column});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file, Line = line, Column = column });
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 6f11831b80..62db6e3af5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -385,7 +385,7 @@ namespace GodotTools.Ides
// However, it doesn't fix resource loading if the rest of the path is also case insensitive.
string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
- var response = new CodeCompletionResponse {Kind = request.Kind, ScriptFile = request.ScriptFile};
+ var response = new CodeCompletionResponse { Kind = request.Kind, ScriptFile = request.ScriptFile };
response.Suggestions = await Task.Run(() =>
Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
return response;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 71055f0125..dad6e35344 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using System.Runtime.Versioning;
using Godot;
-using JetBrains.Annotations;
using Microsoft.Win32;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
@@ -41,7 +42,7 @@ namespace GodotTools.Ides.Rider
{
return CollectAllRiderPathsLinux();
}
- throw new Exception("Unexpected OS.");
+ throw new InvalidOperationException("Unexpected OS.");
}
catch (Exception e)
{
@@ -113,6 +114,7 @@ namespace GodotTools.Ides.Rider
return installInfos.ToArray();
}
+ [SupportedOSPlatform("windows")]
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
@@ -214,9 +216,10 @@ namespace GodotTools.Ides.Rider
return "../../build.txt";
if (OS.IsMacOS)
return "Contents/Resources/build.txt";
- throw new Exception("Unknown OS.");
+ throw new InvalidOperationException("Unknown OS.");
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
@@ -229,6 +232,7 @@ namespace GodotTools.Ides.Rider
}
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
{
if (key == null) return;
@@ -324,7 +328,7 @@ namespace GodotTools.Ides.Rider
{
public string install_location;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetInstallLocationFromJson(string json)
{
try
@@ -378,7 +382,7 @@ namespace GodotTools.Ides.Rider
public string version;
public string versionSuffix;
- [CanBeNull]
+ [return: MaybeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
@@ -402,7 +406,7 @@ namespace GodotTools.Ides.Rider
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetLatestBuildFromJson(string json)
{
try
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index ac29efb716..60602a5847 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider
public static void Initialize()
{
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var editor = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor");
+ var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor");
if (editor == ExternalEditorId.Rider)
{
if (!editorSettings.HasSetting(EditorPathSettingName))
@@ -30,9 +30,9 @@ namespace GodotTools.Ides.Rider
Globals.EditorDef(EditorPathSettingName, "Optional");
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.String,
+ ["type"] = (int)Variant.Type.String,
["name"] = EditorPathSettingName,
- ["hint"] = PropertyHint.File,
+ ["hint"] = (int)PropertyHint.File,
["hint_string"] = ""
});
}
@@ -66,6 +66,9 @@ namespace GodotTools.Ides.Rider
if (string.IsNullOrEmpty(path))
return false;
+ if (path.IndexOfAny(Path.GetInvalidPathChars()) != -1)
+ return false;
+
var fileInfo = new FileInfo(path);
string filename = fileInfo.Name.ToLowerInvariant();
return filename.StartsWith("rider", StringComparison.Ordinal);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
index 70ba7c733a..8f39ad063e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
namespace GodotTools.Internals
{
@@ -8,19 +9,12 @@ namespace GodotTools.Internals
{
public string Task { get; }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Create(string task, string label, int amount, bool canCancel);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Dispose(string task);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
-
public EditorProgress(string task, string label, int amount, bool canCancel = false)
{
Task = task;
- internal_Create(task, label, amount, canCancel);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(task);
+ using godot_string labelIn = Marshaling.ConvertStringToNative(label);
+ Internal.godot_icall_EditorProgress_Create(taskIn, labelIn, amount, canCancel);
}
~EditorProgress()
@@ -33,18 +27,23 @@ namespace GodotTools.Internals
public void Dispose()
{
- internal_Dispose(Task);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ Internal.godot_icall_EditorProgress_Dispose(taskIn);
GC.SuppressFinalize(this);
}
public void Step(string state, int step = -1, bool forceRefresh = true)
{
- internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
{
- return internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ return Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 5c5ced8c29..45ae7eb86b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -1,3 +1,5 @@
+using Godot;
+using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -5,35 +7,42 @@ namespace GodotTools.Internals
{
public static class Globals
{
- public static float EditorScale => internal_EditorScale();
-
- public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_GlobalDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_EditorDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorShortcut(string setting) =>
- internal_EditorShortcut(setting);
+ public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
+
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
+ }
+
+ // ReSharper disable once UnusedMethodReturnValue.Global
+ public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = defaultValue.CopyNativeVariant();
+ Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged,
+ out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
+ }
+
+ public static Variant EditorShortcut(string setting)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
+ return Variant.CreateTakingOwnershipOfDisposableValue(result);
+ }
[SuppressMessage("ReSharper", "InconsistentNaming")]
- public static string TTR(this string text) => internal_TTR(text);
-
- // Internal Calls
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern float internal_EditorScale();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorShortcut(string setting);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_TTR(string text);
+ public static string TTR(this string text)
+ {
+ using godot_string textIn = Marshaling.ConvertStringToNative(text);
+ Internal.godot_icall_Globals_TTR(textIn, out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 5e70c399b2..7624989092 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -1,103 +1,119 @@
-using System.Runtime.CompilerServices;
+using System.IO;
+using Godot;
+using Godot.NativeInterop;
+using GodotTools.Core;
+using static GodotTools.Internals.Globals;
namespace GodotTools.Internals
{
public static class GodotSharpDirs
{
- public static string ResDataDir => internal_ResDataDir();
- public static string ResMetadataDir => internal_ResMetadataDir();
- public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
- public static string ResAssembliesDir => internal_ResAssembliesDir();
- public static string ResConfigDir => internal_ResConfigDir();
- public static string ResTempDir => internal_ResTempDir();
- public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
- public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
-
- public static string MonoUserDir => internal_MonoUserDir();
- public static string MonoLogsDir => internal_MonoLogsDir();
-
- #region Tools-only
- public static string MonoSolutionsDir => internal_MonoSolutionsDir();
- public static string BuildLogsDirs => internal_BuildLogsDirs();
-
- public static string ProjectSlnPath => internal_ProjectSlnPath();
- public static string ProjectCsProjPath => internal_ProjectCsProjPath();
-
- public static string DataEditorToolsDir => internal_DataEditorToolsDir();
- public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
- #endregion
-
- public static string DataMonoEtcDir => internal_DataMonoEtcDir();
- public static string DataMonoLibDir => internal_DataMonoLibDir();
-
- #region Windows-only
- public static string DataMonoBinDir => internal_DataMonoBinDir();
- #endregion
-
-
- #region Internal
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResDataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResMetadataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResConfigDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoUserDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoLogsDir();
-
- #region Tools-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoSolutionsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_BuildLogsDirs();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectSlnPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectCsProjPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorToolsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorPrebuiltApiDir();
- #endregion
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoEtcDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoLibDir();
-
- #region Windows-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoBinDir();
- #endregion
-
- #endregion
+ public static string ResMetadataDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string MonoUserDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_MonoUserDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string BuildLogsDirs
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string DataEditorToolsDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static void DetermineProjectLocation()
+ {
+ static string DetermineProjectName()
+ {
+ string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
+ if (string.IsNullOrEmpty(projectAssemblyName))
+ projectAssemblyName = "UnnamedProject";
+ return projectAssemblyName;
+ }
+
+ _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
+ if (string.IsNullOrEmpty(_projectAssemblyName))
+ {
+ _projectAssemblyName = DetermineProjectName();
+ ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
+ }
+
+ string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
+ if (string.IsNullOrEmpty(slnParentDir))
+ slnParentDir = "res://";
+ else if (!slnParentDir.StartsWith("res://"))
+ slnParentDir = "res://" + slnParentDir;
+
+ // The csproj should be in the same folder as project.godot.
+ string csprojParentDir = "res://";
+
+ _projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir),
+ string.Concat(_projectAssemblyName, ".sln"));
+
+ _projectCsProjPath = Path.Combine(ProjectSettings.GlobalizePath(csprojParentDir),
+ string.Concat(_projectAssemblyName, ".csproj"));
+ }
+
+ private static string _projectAssemblyName;
+ private static string _projectSlnPath;
+ private static string _projectCsProjPath;
+
+ public static string ProjectAssemblyName
+ {
+ get
+ {
+ if (_projectAssemblyName == null)
+ DetermineProjectLocation();
+ return _projectAssemblyName;
+ }
+ }
+
+ public static string ProjectSlnPath
+ {
+ get
+ {
+ if (_projectSlnPath == null)
+ DetermineProjectLocation();
+ return _projectSlnPath;
+ }
+ }
+
+ public static string ProjectCsProjPath
+ {
+ get
+ {
+ if (_projectCsProjPath == null)
+ DetermineProjectLocation();
+ return _projectCsProjPath;
+ }
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 12c90178c9..fd810996f7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -1,114 +1,161 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
+using Godot.SourceGenerators.Internal;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
{
- public static class Internal
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [GenerateUnmanagedCallbacks(typeof(InternalUnmanagedCallbacks))]
+ internal static partial class Internal
{
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
- public static string UpdateApiAssembliesFromPrebuilt(string config) =>
- internal_UpdateApiAssembliesFromPrebuilt(config);
+ public static string FullExportTemplatesDir
+ {
+ get
+ {
+ godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
- public static string FullExportTemplatesDir =>
- internal_FullExportTemplatesDir();
+ public static string SimplifyGodotPath(this string path) => Godot.StringExtensions.SimplifyPath(path);
- public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
+ public static bool IsMacOSAppBundleInstalled(string bundleId)
+ {
+ using godot_string bundleIdIn = Marshaling.ConvertStringToNative(bundleId);
+ return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn);
+ }
- public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId);
+ public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits();
- public static bool GodotIs32Bits() => internal_GodotIs32Bits();
+ public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble();
- public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
+ public static void GodotMainIteration() => godot_icall_Internal_GodotMainIteration();
- public static void GodotMainIteration() => internal_GodotMainIteration();
+ public static bool IsAssembliesReloadingNeeded() => godot_icall_Internal_IsAssembliesReloadingNeeded();
- public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
+ public static void ReloadAssemblies(bool softReload) => godot_icall_Internal_ReloadAssemblies(softReload);
- public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
+ public static void EditorDebuggerNodeReloadScripts() => godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
+ public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
+ godot_icall_Internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus);
- public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
+ public static void EditorNodeShowScriptScreen() => godot_icall_Internal_EditorNodeShowScriptScreen();
- public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
+ public static void EditorRunPlay() => godot_icall_Internal_EditorRunPlay();
- public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
- internal_ScriptEditorEdit(resource, line, col, grabFocus);
+ public static void EditorRunStop() => godot_icall_Internal_EditorRunStop();
- public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
+ public static void ScriptEditorDebugger_ReloadScripts() =>
+ godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
+ public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind,
+ string scriptFile)
+ {
+ using godot_string scriptFileIn = Marshaling.ConvertStringToNative(scriptFile);
+ godot_icall_Internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res);
+ using (res)
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(res);
+ }
- public static void EditorRunPlay() => internal_EditorRunPlay();
+ #region Internal
- public static void EditorRunStop() => internal_EditorRunStop();
+ private static bool initialized = false;
- public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ internal static unsafe void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized.");
+ initialized = true;
- public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) =>
- internal_CodeCompletionRequest((int)kind, scriptFile);
+ if (unmanagedCallbacksSize != sizeof(InternalUnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch.", nameof(unmanagedCallbacksSize));
- #region Internal
+ _unmanagedCallbacks = Unsafe.AsRef<InternalUnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct InternalUnmanagedCallbacks
+ {
+ }
+
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'editor/editor_internal_calls.cpp'.
+ */
+
+ public static partial void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
+
+ public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
+ int amount, bool canCancel);
+
+ public static partial void godot_icall_EditorProgress_Dispose(in godot_string task);
+
+ public static partial bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state,
+ int step,
+ bool forceRefresh);
+
+ private static partial void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+
+ private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
+ private static partial bool godot_icall_Internal_GodotIs32Bits();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_FullExportTemplatesDir();
+ private static partial bool godot_icall_Internal_GodotIsRealTDouble();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_SimplifyGodotPath(this string path);
+ private static partial void godot_icall_Internal_GodotMainIteration();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId);
+ private static partial bool godot_icall_Internal_IsAssembliesReloadingNeeded();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIs32Bits();
+ private static partial void godot_icall_Internal_ReloadAssemblies(bool softReload);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIsRealTDouble();
+ private static partial void godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GodotMainIteration();
+ private static partial bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
+ bool grabFocus);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetCoreApiHash();
+ private static partial void godot_icall_Internal_EditorNodeShowScriptScreen();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetEditorApiHash();
+ private static partial void godot_icall_Internal_EditorRunPlay();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsAssembliesReloadingNeeded();
+ private static partial void godot_icall_Internal_EditorRunStop();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ReloadAssemblies(bool softReload);
+ private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorDebuggerNodeReloadScripts();
+ private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
+ out godot_packed_string_array res);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
+ public static partial float godot_icall_Globals_EditorScale();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorNodeShowScriptScreen();
+ public static partial void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoWindowsInstallRoot();
+ public static partial void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunPlay();
+ public static partial void
+ godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunStop();
+ public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ScriptEditorDebugger_ReloadScripts();
+ public static partial void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile);
+ public static partial bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);
#endregion
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs b/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
index 820d0c0b83..9a8fdcc7c5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
@@ -1,6 +1,6 @@
namespace GodotTools
{
- public struct PlaySettings
+ public readonly struct PlaySettings
{
public bool HasDebugger { get; }
public string DebuggerHost { get; }
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
index 05499339b1..89bda704bb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -1,14 +1,14 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using Godot;
using GodotTools.Core;
-using JetBrains.Annotations;
namespace GodotTools.Utils
{
public static class FsPathUtils
{
- private static readonly string _resourcePath = ProjectSettings.GlobalizePath("res://");
+ private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
{
@@ -30,11 +30,11 @@ namespace GodotTools.Utils
return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
}
- [CanBeNull]
+ [return: MaybeNull]
public static string LocalizePathWithCaseChecked(string path)
{
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
- string resourcePathNorm = _resourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
return null;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 5cef6e5c3c..c16f803226 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -1,24 +1,24 @@
+using Godot.NativeInterop;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
-using JetBrains.Annotations;
+using System.Runtime.Versioning;
+using System.Text;
+using GodotTools.Internals;
namespace GodotTools.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class OS
{
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string GetPlatformName();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool UnixFileHasExecutableAccess(string filePath);
-
- public static class Names
+ /// <summary>
+ /// Display names for the OS platforms.
+ /// </summary>
+ private static class Names
{
public const string Windows = "Windows";
public const string MacOS = "macOS";
@@ -26,27 +26,58 @@ namespace GodotTools.Utils
public const string FreeBSD = "FreeBSD";
public const string NetBSD = "NetBSD";
public const string BSD = "BSD";
- public const string Server = "Server";
public const string UWP = "UWP";
public const string Haiku = "Haiku";
public const string Android = "Android";
public const string iOS = "iOS";
- public const string HTML5 = "HTML5";
+ public const string Web = "Web";
}
+ /// <summary>
+ /// Godot platform identifiers.
+ /// </summary>
public static class Platforms
{
public const string Windows = "windows";
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 = "ios";
- public const string HTML5 = "javascript";
+ public const string Web = "web";
+ }
+
+ /// <summary>
+ /// OS name part of the .NET runtime identifier (RID).
+ /// See https://docs.microsoft.com/en-us/dotnet/core/rid-catalog.
+ /// </summary>
+ public static class DotNetOS
+ {
+ public const string Win = "win";
+ public const string OSX = "osx";
+ public const string Linux = "linux";
+ public const string Win10 = "win10";
+ public const string Android = "android";
+ public const string iOS = "ios";
+ public const string Browser = "browser";
}
+ public static readonly Dictionary<string, string> PlatformFeatureMap = new Dictionary<string, string>(
+ // Export `features` may be in lower case
+ StringComparer.InvariantCultureIgnoreCase
+ )
+ {
+ ["Windows"] = Platforms.Windows,
+ ["macOS"] = Platforms.MacOS,
+ ["Linux"] = Platforms.LinuxBSD,
+ ["UWP"] = Platforms.UWP,
+ ["Haiku"] = Platforms.Haiku,
+ ["Android"] = Platforms.Android,
+ ["iOS"] = Platforms.iOS,
+ ["Web"] = Platforms.Web
+ };
+
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
[Names.Windows] = Platforms.Windows,
@@ -55,55 +86,85 @@ namespace GodotTools.Utils
[Names.FreeBSD] = Platforms.LinuxBSD,
[Names.NetBSD] = Platforms.LinuxBSD,
[Names.BSD] = Platforms.LinuxBSD,
- [Names.Server] = Platforms.Server,
[Names.UWP] = Platforms.UWP,
[Names.Haiku] = Platforms.Haiku,
[Names.Android] = Platforms.Android,
[Names.iOS] = Platforms.iOS,
- [Names.HTML5] = Platforms.HTML5
+ [Names.Web] = Platforms.Web
+ };
+
+ public static readonly Dictionary<string, string> DotNetOSPlatformMap = new Dictionary<string, string>
+ {
+ [Platforms.Windows] = DotNetOS.Win,
+ [Platforms.MacOS] = DotNetOS.OSX,
+ // TODO:
+ // Does .NET 6 support BSD variants? If it does, it may need the name `unix`
+ // instead of `linux` in the runtime identifier. This would be a problem as
+ // Godot has a single export profile for both, named LinuxBSD.
+ [Platforms.LinuxBSD] = DotNetOS.Linux,
+ [Platforms.UWP] = DotNetOS.Win10,
+ [Platforms.Android] = DotNetOS.Android,
+ [Platforms.iOS] = DotNetOS.iOS,
+ [Platforms.Web] = DotNetOS.Browser
};
private static bool IsOS(string name)
{
- return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return name.Equals(platformName, StringComparison.OrdinalIgnoreCase);
+ }
}
private static bool IsAnyOS(IEnumerable<string> names)
{
- return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase));
+ }
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
new[] { Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD };
private static readonly IEnumerable<string> UnixLikePlatforms =
- new[] { Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS }
+ new[] { Names.MacOS, Names.Haiku, Names.Android, Names.iOS }
.Concat(LinuxBSDPlatforms).ToArray();
- private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
- private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS));
- private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms));
- private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
- private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
- private static readonly Lazy<bool> _isHaiku = new Lazy<bool>(() => IsOS(Names.Haiku));
- private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
- private static readonly Lazy<bool> _isiOS = new Lazy<bool>(() => IsOS(Names.iOS));
- private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
- private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
-
- public static bool IsWindows => _isWindows.Value || IsUWP;
- public static bool IsMacOS => _isMacOS.Value;
- public static bool IsLinuxBSD => _isLinuxBSD.Value;
- public static bool IsServer => _isServer.Value;
- public static bool IsUWP => _isUWP.Value;
+ private static readonly Lazy<bool> _isWindows = new(() => IsOS(Names.Windows));
+ private static readonly Lazy<bool> _isMacOS = new(() => IsOS(Names.MacOS));
+ private static readonly Lazy<bool> _isLinuxBSD = new(() => IsAnyOS(LinuxBSDPlatforms));
+ private static readonly Lazy<bool> _isUWP = new(() => IsOS(Names.UWP));
+ private static readonly Lazy<bool> _isHaiku = new(() => IsOS(Names.Haiku));
+ private static readonly Lazy<bool> _isAndroid = new(() => IsOS(Names.Android));
+ private static readonly Lazy<bool> _isiOS = new(() => IsOS(Names.iOS));
+ private static readonly Lazy<bool> _isWeb = new(() => IsOS(Names.Web));
+ private static readonly Lazy<bool> _isUnixLike = new(() => IsAnyOS(UnixLikePlatforms));
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsWindows => _isWindows.Value || IsUWP;
+
+ [SupportedOSPlatformGuard("osx")] public static bool IsMacOS => _isMacOS.Value;
+
+ [SupportedOSPlatformGuard("linux")] public static bool IsLinuxBSD => _isLinuxBSD.Value;
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsUWP => _isUWP.Value;
+
public static bool IsHaiku => _isHaiku.Value;
- public static bool IsAndroid => _isAndroid.Value;
- public static bool IsiOS => _isiOS.Value;
- public static bool IsHTML5 => _isHTML5.Value;
+
+ [SupportedOSPlatformGuard("android")] public static bool IsAndroid => _isAndroid.Value;
+
+ [SupportedOSPlatformGuard("ios")] public static bool IsiOS => _isiOS.Value;
+
+ [SupportedOSPlatformGuard("browser")] public static bool IsWeb => _isWeb.Value;
public static bool IsUnixLike => _isUnixLike.Value;
public static char PathSep => IsWindows ? ';' : ':';
+ [return: MaybeNull]
public static string PathWhich([NotNull] string name)
{
if (IsWindows)
@@ -112,9 +173,11 @@ namespace GodotTools.Utils
return PathWhichUnix(name);
}
+ [return: MaybeNull]
private static string PathWhichWindows([NotNull] string name)
{
- string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
+ string[] windowsExts =
+ Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
char[] invalidPathChars = Path.GetInvalidPathChars();
@@ -133,7 +196,7 @@ namespace GodotTools.Utils
string nameExt = Path.GetExtension(name);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
- windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
+ windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@@ -147,6 +210,7 @@ namespace GodotTools.Utils
select path + ext).FirstOrDefault(File.Exists);
}
+ [return: MaybeNull]
private static string PathWhichUnix([NotNull] string name)
{
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
@@ -168,19 +232,16 @@ namespace GodotTools.Utils
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
return searchDirs.Select(dir => Path.Combine(dir, name))
- .FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path));
+ .FirstOrDefault(path =>
+ {
+ using godot_string pathIn = Marshaling.ConvertStringToNative(path);
+ return File.Exists(path) && Internal.godot_icall_Utils_OS_UnixFileHasExecutableAccess(pathIn);
+ });
}
public static void RunProcess(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
- {
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
-
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
+ var startInfo = new ProcessStartInfo(command)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -188,44 +249,104 @@ namespace GodotTools.Utils
CreateNoWindow = true
};
- using (Process process = Process.Start(startInfo))
- {
- if (process == null)
- throw new Exception("No process was started");
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- if (IsWindows && process.Id > 0)
- User32Dll.AllowSetForegroundWindow(process.Id); // allows application to focus itself
- }
+ using Process process = Process.Start(startInfo);
+
+ if (process == null)
+ throw new InvalidOperationException("No process was started.");
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ if (IsWindows && process.Id > 0)
+ User32Dll.AllowSetForegroundWindow(process.Id); // Allows application to focus itself
}
public static int ExecuteCommand(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
+ var startInfo = new ProcessStartInfo(command)
{
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
+ // Print the output
+ RedirectStandardOutput = false,
+ RedirectStandardError = false,
+ UseShellExecute = false
+ };
+
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments));
+ Console.WriteLine(startInfo.GetCommandLineDisplay(new StringBuilder("Executing: ")).ToString());
- Console.WriteLine($"Executing: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ using var process = new Process { StartInfo = startInfo };
+ process.Start();
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
- // Print the output
- startInfo.RedirectStandardOutput = false;
- startInfo.RedirectStandardError = false;
+ private static void AppendProcessFileNameForDisplay(this StringBuilder builder, string fileName)
+ {
+ if (builder.Length > 0)
+ builder.Append(' ');
+
+ if (fileName.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(fileName);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(fileName);
+ }
+ }
- startInfo.UseShellExecute = false;
+ private static void AppendProcessArgumentsForDisplay(this StringBuilder builder,
+ Collection<string> argumentList)
+ {
+ // This is intended just for reading. It doesn't need to be a valid command line.
+ // E.g.: We don't handle escaping of quotes.
- using (var process = new Process { StartInfo = startInfo })
+ foreach (string argument in argumentList)
{
- process.Start();
- process.WaitForExit();
+ if (builder.Length > 0)
+ builder.Append(' ');
- return process.ExitCode;
+ if (argument.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(argument);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(argument);
+ }
}
}
+
+ public static StringBuilder GetCommandLineDisplay(
+ this ProcessStartInfo startInfo,
+ StringBuilder optionalBuilder = null
+ )
+ {
+ var builder = optionalBuilder ?? new StringBuilder();
+
+ builder.AppendProcessFileNameForDisplay(startInfo.FileName);
+
+ if (startInfo.ArgumentList.Count == 0)
+ {
+ builder.Append(' ');
+ builder.Append(startInfo.Arguments);
+ }
+ else
+ {
+ builder.AppendProcessArgumentsForDisplay(startInfo.ArgumentList);
+ }
+
+ return builder;
+ }
}
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 2e628cb576..03cbfda1bd 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* bindings_generator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* bindings_generator.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "bindings_generator.h"
@@ -41,69 +41,99 @@
#include "core/string/ucaps.h"
#include "main/main.h"
-#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_defs.h"
-#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
+StringBuilder &operator<<(StringBuilder &r_sb, const String &p_string) {
+ r_sb.append(p_string);
+ return r_sb;
+}
+
+StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
+ r_sb.append(p_cstring);
+ return r_sb;
+}
+
#define CS_INDENT " " // 4 whitespaces
#define INDENT1 CS_INDENT
#define INDENT2 INDENT1 INDENT1
#define INDENT3 INDENT2 INDENT1
#define INDENT4 INDENT3 INDENT1
-#define INDENT5 INDENT4 INDENT1
-#define MEMBER_BEGIN "\n" INDENT2
+#define MEMBER_BEGIN "\n" INDENT1
#define OPEN_BLOCK "{\n"
#define CLOSE_BLOCK "}\n"
-#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
-#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
+#define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
+#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
+#define CLOSE_BLOCK_L1 INDENT1 CLOSE_BLOCK
#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
-#define CS_FIELD_MEMORYOWN "memoryOwn"
+#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
+#define BINDINGS_NATIVE_NAME_FIELD "NativeName"
+
+#define CS_PARAM_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
-#define CS_SMETHOD_GETINSTANCE "GetPtr"
+#define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
#define CS_METHOD_CALL "Call"
+#define CS_PROPERTY_SINGLETON "Singleton"
+#define CS_METHOD_INVOKE_GODOT_CLASS_METHOD "InvokeGodotClassMethod"
+#define CS_METHOD_HAS_GODOT_CLASS_METHOD "HasGodotClassMethod"
+
+#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
+#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
+#define CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX "MethodProxyName_"
-#define GLUE_HEADER_FILE "glue_header.h"
#define ICALL_PREFIX "godot_icall_"
-#define SINGLETON_ICALL_SUFFIX "_get_singleton"
-#define ICALL_GET_METHODBIND "__ClassDB_get_method"
+#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
+#define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor"
#define C_LOCAL_RET "ret"
#define C_LOCAL_VARARG_RET "vararg_ret"
#define C_LOCAL_PTRCALL_ARGS "call_args"
-#define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT"
-
-#define C_NS_MONOUTILS "GDMonoUtils"
-#define C_NS_MONOINTERNALS "GDMonoInternals"
-#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged"
-#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed"
-
-#define C_NS_MONOMARSHAL "GDMonoMarshal"
-#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant"
-#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object"
-#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot"
-#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
-#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
-#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
-#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
-#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
-#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-
-#define BINDINGS_GENERATOR_VERSION UINT32_C(13)
+
+#define C_CLASS_NATIVE_FUNCS "NativeFuncs"
+#define C_NS_MONOUTILS "InteropUtils"
+#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
+#define C_METHOD_ENGINE_GET_SINGLETON C_NS_MONOUTILS ".EngineGetSingleton"
+
+#define C_NS_MONOMARSHAL "Marshaling"
+#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".ConvertStringToNative"
+#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".ConvertStringToManaged"
+#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".ConvertSystemArrayToNative" #m_type
+#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL ".ConvertNative" #m_type "ToSystemArray"
+#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative"
+#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged"
+#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative"
+#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
// Types that will be ignored by the generator and won't be available in C#.
-const Vector<String> ignored_types = { "PhysicsServer3DExtension" };
+const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" };
-const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
+void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
+ // C interface for enums is the same as that of 'uint32_t'. Remember to apply
+ // any of the changes done here to the 'uint32_t' type interface as well.
+
+ r_enum_itype.cs_type = r_enum_itype.proxy_name;
+ r_enum_itype.cs_in_expr = "(int)%0";
+ r_enum_itype.cs_out = "%5return (%2)%0(%1);";
+
+ {
+ // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
+ r_enum_itype.c_in = "%5%0 %1_in = %1;\n";
+ r_enum_itype.c_out = "%5return (%0)%1;\n";
+ r_enum_itype.c_type = "long";
+ r_enum_itype.c_arg_in = "&%s_in";
+ }
+ r_enum_itype.c_type_in = "int";
+ r_enum_itype.c_type_out = r_enum_itype.c_type_in;
+ r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
+}
static String fix_doc_description(const String &p_bbcode) {
// This seems to be the correct way to do this. It's the same EditorHelp does.
@@ -359,23 +389,23 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append(tag);
xml_output.append("</c>");
} else if (tag == "PackedByteArray") {
- xml_output.append("<see cref=\"T:byte[]\"/>");
+ xml_output.append("<see cref=\"byte\"/>[]");
} else if (tag == "PackedInt32Array") {
- xml_output.append("<see cref=\"T:int[]\"/>");
+ xml_output.append("<see cref=\"int\"/>[]");
} else if (tag == "PackedInt64Array") {
- xml_output.append("<see cref=\"T:long[]\"/>");
+ xml_output.append("<see cref=\"long\"/>[]");
} else if (tag == "PackedFloat32Array") {
- xml_output.append("<see cref=\"T:float[]\"/>");
+ xml_output.append("<see cref=\"float\"/>[]");
} else if (tag == "PackedFloat64Array") {
- xml_output.append("<see cref=\"T:double[]\"/>");
+ xml_output.append("<see cref=\"double\"/>[]");
} else if (tag == "PackedStringArray") {
- xml_output.append("<see cref=\"T:string[]\"/>");
+ xml_output.append("<see cref=\"string\"/>[]");
} else if (tag == "PackedVector2Array") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector2[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>[]");
} else if (tag == "PackedVector3Array") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Vector3[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>[]");
} else if (tag == "PackedColorArray") {
- xml_output.append("<see cref=\"T:" BINDINGS_NAMESPACE ".Color[]\"/>");
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>[]");
} else {
const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag));
@@ -794,49 +824,28 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
}
}
-void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
+Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_itype) {
for (const MethodInterface &imethod : p_itype.methods) {
if (imethod.is_virtual) {
continue;
}
- const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
- String im_sig = "IntPtr " CS_PARAM_METHODBIND;
- String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr";
+ String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind";
if (!imethod.is_static) {
- im_sig += ", IntPtr " CS_PARAM_INSTANCE;
- im_unique_sig += ",IntPtr";
+ im_unique_sig += ",CallInstance";
}
// Get arguments information
- int i = 0;
for (const ArgumentInterface &iarg : imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
-
- im_sig += ", ";
- im_sig += arg_type->im_type_in;
- im_sig += " arg";
- im_sig += itos(i + 1);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
im_unique_sig += ",";
- im_unique_sig += get_unique_sig(*arg_type);
-
- i++;
- }
-
- String im_type_out = return_type->im_type_out;
-
- if (return_type->ret_as_byref_arg) {
- // Doesn't affect the unique signature
- im_type_out = "void";
-
- im_sig += ", ";
- im_sig += return_type->im_type_out;
- im_sig += " argRet";
-
- i++;
+ im_unique_sig += get_arg_unique_sig(*arg_type);
}
// godot_icall_{argc}_{icallcount}
@@ -845,7 +854,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
icall_method += "_";
icall_method += itos(method_icalls.size());
- InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig);
+ InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig);
+
+ im_icall.is_vararg = imethod.is_vararg;
+ im_icall.is_static = imethod.is_static;
+ im_icall.return_type = imethod.return_type;
+
+ for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
+ im_icall.argument_types.push_back(F->get().type);
+ }
List<InternalCall>::Element *match = method_icalls.find(im_icall);
@@ -859,47 +876,49 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
method_icalls_map.insert(&imethod, &added->get());
}
}
+
+ return OK;
}
void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
+ p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
p_output.append("using System;\n\n");
- p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
// The class where we put the extensions doesn't matter, so just use "GD".
- p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+ p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
#define ARRAY_IS_EMPTY(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>Whether or not the array is empty.</returns>\n"); \
- p_output.append(INDENT2 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return instance == null || instance.Length == 0;\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>Whether or not the array is empty.</returns>\n"); \
+ p_output.append(INDENT1 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return instance == null || instance.Length == 0;\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_JOIN(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
- p_output.append(INDENT2 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
- p_output.append(INDENT2 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return String.Join(delimiter, instance);\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT1 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return String.Join(delimiter, instance);\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_STRINGIFY(m_type) \
- p_output.append("\n" INDENT2 "/// <summary>\n"); \
- p_output.append(INDENT2 "/// Converts this " #m_type " array to a string with brackets.\n"); \
- p_output.append(INDENT2 "/// </summary>\n"); \
- p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
- p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
- p_output.append(INDENT2 "public static string Stringify(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
- p_output.append(INDENT3 "return \"[\" + instance.Join() + \"]\";\n"); \
- p_output.append(INDENT2 CLOSE_BLOCK);
+ p_output.append("\n" INDENT1 "/// <summary>\n"); \
+ p_output.append(INDENT1 "/// Converts this " #m_type " array to a string with brackets.\n"); \
+ p_output.append(INDENT1 "/// </summary>\n"); \
+ p_output.append(INDENT1 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT1 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT1 "public static string Stringify(this " #m_type "[] instance)\n"); \
+ p_output.append(OPEN_BLOCK_L1); \
+ p_output.append(INDENT2 "return \"[\" + instance.Join() + \"]\";\n"); \
+ p_output.append(INDENT1 CLOSE_BLOCK);
#define ARRAY_ALL(m_type) \
ARRAY_IS_EMPTY(m_type) \
@@ -925,18 +944,18 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
#undef ARRAY_JOIN
#undef ARRAY_STRINGIFY
- p_output.append(INDENT1 CLOSE_BLOCK); // End of GD class.
- p_output.append(CLOSE_BLOCK); // End of namespace.
+ p_output.append(CLOSE_BLOCK); // End of GD class.
}
void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
// Constants (in partial GD class)
+ p_output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+
p_output.append("\n#pragma warning disable CS1591 // Disable warning: "
"'Missing XML comment for publicly visible type or member'\n");
- p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+ p_output.append("public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n{");
for (const ConstantInterface &iconstant : global_constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
@@ -947,12 +966,12 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -967,7 +986,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n");
}
- p_output.append(INDENT1 CLOSE_BLOCK); // end of GD class
+ p_output.append(CLOSE_BLOCK); // end of GD class
// Enums
@@ -985,21 +1004,21 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
CRASH_COND(enum_class_name != "Variant"); // Hard-coded...
- _log("Declaring global enum '%s' inside static class '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
+ _log("Declaring global enum '%s' inside struct '%s'\n", enum_proxy_name.utf8().get_data(), enum_class_name.utf8().get_data());
- p_output.append("\n" INDENT1 "public static partial class ");
+ p_output.append("\npublic partial struct ");
p_output.append(enum_class_name);
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK);
}
if (ienum.is_flags) {
- p_output.append("\n" INDENT1 "[System.Flags]");
+ p_output.append("\n[System.Flags]");
}
- p_output.append("\n" INDENT1 "public enum ");
+ p_output.append("\npublic enum ");
p_output.append(enum_proxy_name);
p_output.append(" : long");
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
for (const ConstantInterface &iconstant : ienum.constants) {
@@ -1008,34 +1027,32 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- p_output.append(INDENT2 "/// <summary>\n");
+ p_output.append(INDENT1 "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>\n");
+ p_output.append(INDENT1 "/// </summary>\n");
}
}
- p_output.append(INDENT2);
+ p_output.append(INDENT1);
p_output.append(iconstant.proxy_name);
p_output.append(" = ");
p_output.append(itos(iconstant.value));
p_output.append(&iconstant != &last ? ",\n" : "\n");
}
- p_output.append(INDENT1 CLOSE_BLOCK);
+ p_output.append(CLOSE_BLOCK);
if (enum_in_static_class) {
- p_output.append(INDENT1 CLOSE_BLOCK);
+ p_output.append(CLOSE_BLOCK);
}
}
- p_output.append(CLOSE_BLOCK); // end of namespace
-
p_output.append("\n#pragma warning restore CS1591\n");
}
@@ -1106,42 +1123,38 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
- // Generate sources from compressed files
+ // Generate native calls
StringBuilder cs_icalls_content;
+ cs_icalls_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
cs_icalls_content.append("using System;\n"
- "using System.Runtime.CompilerServices;\n"
+ "using System.Diagnostics.CodeAnalysis;\n"
+ "using System.Runtime.InteropServices;\n"
+ "using Godot.NativeInterop;\n"
"\n");
- cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS "\n{");
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\n");
-#define ADD_INTERNAL_CALL(m_icall) \
- if (!m_icall.editor_only) { \
- cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
- for (const InternalCall &internal_call : core_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
+ for (const InternalCall &icall : method_icalls) {
+ if (icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
}
-#undef ADD_INTERNAL_CALL
-
- cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ cs_icalls_content.append(CLOSE_BLOCK);
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
@@ -1152,6 +1165,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1215,41 +1230,40 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate native calls
+
StringBuilder cs_icalls_content;
+ cs_icalls_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
cs_icalls_content.append("using System;\n"
- "using System.Runtime.CompilerServices;\n"
+ "using System.Diagnostics.CodeAnalysis;\n"
+ "using System.Runtime.InteropServices;\n"
+ "using Godot.NativeInterop;\n"
"\n");
- cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
-
- cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
- cs_icalls_content.append("\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append("[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK);
-#define ADD_INTERNAL_CALL(m_icall) \
- if (m_icall.editor_only) { \
- cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(INDENT1 "internal static ulong godot_api_hash = ");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
-#undef ADD_INTERNAL_CALL
+ cs_icalls_content.append("\n");
- cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
+ for (const InternalCall &icall : method_icalls) {
+ if (!icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ cs_icalls_content.append(CLOSE_BLOCK);
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS_EDITOR ".cs");
@@ -1260,6 +1274,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1299,7 +1315,7 @@ Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
// Generate GodotSharp source files
- String core_proj_dir = output_dir.plus_file(CORE_API_ASSEMBLY_NAME);
+ String core_proj_dir = output_dir.path_join(CORE_API_ASSEMBLY_NAME);
proj_err = generate_cs_core_project(core_proj_dir);
if (proj_err != OK) {
@@ -1309,7 +1325,7 @@ Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
// Generate GodotSharpEditor source files
- String editor_proj_dir = output_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
+ String editor_proj_dir = output_dir.path_join(EDITOR_API_ASSEMBLY_NAME);
proj_err = generate_cs_editor_project(editor_proj_dir);
if (proj_err != OK) {
@@ -1343,16 +1359,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
CRASH_COND(itype.is_singleton);
}
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
_log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
StringBuilder output;
+ output.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+
output.append("using System;\n"); // IntPtr
output.append("using System.Diagnostics;\n"); // DebuggerBrowsable
+ output.append("using Godot.NativeInterop;\n");
output.append("\n"
"#pragma warning disable CS1591 // Disable warning: "
@@ -1360,7 +1375,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"#pragma warning disable CS1573 // Disable warning: "
"'Parameter has no matching param tag in the XML comment'\n");
- output.append("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ output.append("\n#nullable disable\n");
const DocData::ClassDoc *class_doc = itype.class_doc;
@@ -1369,40 +1384,48 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- output.append(INDENT1 "/// <summary>\n");
+ output.append("/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT1 "/// ");
+ output.append("/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT1 "/// </summary>\n");
+ output.append("/// </summary>\n");
}
}
- output.append(INDENT1 "public ");
+ // We generate a `GodotClassName` attribute if the engine class name is not the same as the
+ // generated C# class name. This allows introspection code to find the name associated with
+ // the class. If the attribute is not present, the C# class name can be used instead.
+ if (itype.name != itype.proxy_name) {
+ output << "[GodotClassName(\"" << itype.name << "\")]\n";
+ }
+
+ output.append("public ");
if (itype.is_singleton) {
output.append("static partial class ");
} else {
- output.append(itype.is_instantiable ? "partial class " : "abstract partial class ");
+ // Even if the class is not instantiable, we can't declare it abstract because
+ // the engine can still instantiate them and return them via the scripting API.
+ // Example: `SceneTreeTimer` returned from `SceneTree.create_timer`.
+ // See the reverted commit: ef5672d3f94a7321ed779c922088bb72adbb1521
+ output.append("partial class ");
}
output.append(itype.proxy_name);
- if (itype.is_singleton) {
- output.append("\n");
- } else if (is_derived_type) {
+ if (is_derived_type && !itype.is_singleton) {
if (obj_types.has(itype.base_name)) {
output.append(" : ");
output.append(obj_types[itype.base_name].proxy_name);
- output.append("\n");
} else {
ERR_PRINT("Base type '" + itype.base_name.operator String() + "' does not exist, for class '" + itype.name + "'.");
return ERR_INVALID_DATA;
}
}
- output.append(INDENT1 "{");
+ output.append("\n{");
// Add constants
@@ -1415,12 +1438,12 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT2 "/// ");
+ output.append(INDENT1 "/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT2 "/// </summary>");
+ output.append(INDENT1 "/// </summary>");
}
}
@@ -1456,26 +1479,26 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
if (summary_lines.size()) {
- output.append(INDENT3 "/// <summary>\n");
+ output.append(INDENT2 "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- output.append(INDENT3 "/// ");
+ output.append(INDENT2 "/// ");
output.append(summary_lines[i]);
output.append("\n");
}
- output.append(INDENT3 "/// </summary>\n");
+ output.append(INDENT2 "/// </summary>\n");
}
}
- output.append(INDENT3);
+ output.append(INDENT2);
output.append(iconstant.proxy_name);
output.append(" = ");
output.append(itos(iconstant.value));
output.append(&iconstant != &last ? ",\n" : "\n");
}
- output.append(INDENT2 CLOSE_BLOCK);
+ output.append(INDENT1 CLOSE_BLOCK);
}
// Add properties
@@ -1491,55 +1514,68 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add the type name and the singleton pointer as static fields
output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
- output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
- "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
- "singleton = Engine.GetSingleton(typeof(");
- output.append(itype.proxy_name);
- output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
- output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output << MEMBER_BEGIN "public static Godot.Object " CS_PROPERTY_SINGLETON "\n" INDENT1 "{\n"
+ << INDENT2 "get\n" INDENT2 "{\n" INDENT3 "if (singleton == null)\n"
+ << INDENT4 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof("
+ << itype.proxy_name
+ << ").Name);\n" INDENT3 "return singleton;\n" INDENT2 "}\n" INDENT1 "}\n";
+
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
+ } else {
+ // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring
+ // them manually in the `Object.base.cs` partial class declaration, because they're
+ // required by other static fields in this generated partial class declaration.
+ // Static fields are initialized in order of declaration, but when they're in different
+ // partial class declarations then it becomes harder to tell (Rider warns about this).
- output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." ICALL_PREFIX);
- output.append(itype.name);
- output.append(SINGLETON_ICALL_SUFFIX "();\n");
- } else if (is_derived_type) {
- // Add member fields
+ // Add native name static field
- output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ if (is_derived_type) {
+ output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n";
+ }
+
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
- // Add default constructor
if (itype.is_instantiable) {
- output.append(MEMBER_BEGIN "public ");
- output.append(itype.proxy_name);
- output.append("() : this(");
- output.append(itype.memory_own ? "true" : "false");
-
- // The default constructor may also be called by the engine when instancing existing native objects
- // The engine will initialize the pointer field of the managed side before calling the constructor
- // This is why we only allocate a new native object from the constructor if the pointer field is not set
- output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." + ctor_method);
- output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
- } else {
- // Hide the constructor
+ // Add native constructor static field
+
+ output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly unsafe delegate* unmanaged<IntPtr> "
+ << CS_STATIC_FIELD_NATIVE_CTOR " = " ICALL_CLASSDB_GET_CONSTRUCTOR
+ << "(" BINDINGS_NATIVE_NAME_FIELD ");\n";
+ }
+
+ if (is_derived_type) {
+ // Add default constructor
+ if (itype.is_instantiable) {
+ output << MEMBER_BEGIN "public " << itype.proxy_name << "() : this("
+ << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L1
+ << INDENT2 "unsafe\n" INDENT2 OPEN_BLOCK
+ << INDENT3 "_ConstructAndInitialize(" CS_STATIC_FIELD_NATIVE_CTOR ", "
+ << BINDINGS_NATIVE_NAME_FIELD ", CachedType, refCounted: "
+ << (itype.is_ref_counted ? "true" : "false") << ");\n"
+ << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1;
+ } else {
+ // Hide the constructor
+ output.append(MEMBER_BEGIN "internal ");
+ output.append(itype.proxy_name);
+ output.append("() {}\n");
+ }
+
+ // Add.. em.. trick constructor. Sort of.
output.append(MEMBER_BEGIN "internal ");
output.append(itype.proxy_name);
- output.append("() {}\n");
+ output.append("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n");
}
-
- // Add.. em.. trick constructor. Sort of.
- output.append(MEMBER_BEGIN "internal ");
- output.append(itype.proxy_name);
- output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
}
+ // Methods
+
int method_bind_count = 0;
for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
@@ -1547,30 +1583,186 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
+ // Signals
+
for (const SignalInterface &isignal : itype.signals_) {
Error method_err = _generate_cs_signal(itype, isignal, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
}
- if (itype.is_singleton) {
- InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
+ // Script members look-up
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
+ if (!itype.is_singleton && (is_derived_type || itype.has_virtual_methods)) {
+ // Generate method names cache fields
+
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
+
+ output << MEMBER_BEGIN "// ReSharper disable once InconsistentNaming\n"
+ << INDENT1 "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly StringName "
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " = \"" << imethod.proxy_name << "\";\n";
}
- }
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
+ // TODO: Only generate HasGodotClassMethod and InvokeGodotClassMethod if there's any method
+
+ // Generate InvokeGodotClassMethod
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(in godot_string_name method, "
+ << "NativeVariantPtrArgs args, out godot_variant ret)\n"
+ << INDENT1 "{\n";
+
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
+
+ // We also call HasGodotClassMethod to ensure the method is overridden and avoid calling
+ // the stub implementation. This solution adds some extra overhead to calls, but it's
+ // much simpler than other solutions. This won't be a problem once we move to function
+ // pointers of generated wrappers for each method, as lookup will only happen once.
+
+ // We check both native names (snake_case) and proxy names (PascalCase)
+ output << INDENT2 "if ((method == " << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << " || method == MethodName." << imethod.proxy_name
+ << ") && args.Count == " << itos(imethod.arguments.size())
+ << " && " << CS_METHOD_HAS_GODOT_CLASS_METHOD << "((godot_string_name)"
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name << ".NativeValue))\n"
+ << INDENT2 "{\n";
+
+ if (imethod.return_type.cname != name_cache.type_void) {
+ output << INDENT3 "var callRet = ";
+ } else {
+ output << INDENT3;
+ }
+
+ output << imethod.proxy_name << "(";
+
+ for (int i = 0; i < imethod.arguments.size(); i++) {
+ const ArgumentInterface &iarg = imethod.arguments[i];
+
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
+
+ if (i != 0) {
+ output << ", ";
+ }
+
+ if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) {
+ String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+
+ output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed, "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
+ } else {
+ output << sformat(arg_type->cs_variant_to_managed,
+ "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name);
+ }
+ }
+
+ output << ");\n";
+
+ if (imethod.return_type.cname != name_cache.type_void) {
+ const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
+
+ output << INDENT3 "ret = "
+ << sformat(return_type->cs_managed_to_variant, "callRet", return_type->cs_type, return_type->name)
+ << ";\n"
+ << INDENT3 "return true;\n";
+ } else {
+ output << INDENT3 "ret = default;\n"
+ << INDENT3 "return true;\n";
+ }
+
+ output << INDENT2 "}\n";
+ }
+
+ if (is_derived_type) {
+ output << INDENT2 "return base." CS_METHOD_INVOKE_GODOT_CLASS_METHOD "(method, args, out ret);\n";
+ } else {
+ output << INDENT2 "ret = default;\n"
+ << INDENT2 "return false;\n";
+ }
+
+ output << INDENT1 "}\n";
+
+ // Generate HasGodotClassMethod
+
+ output << MEMBER_BEGIN "protected internal " << (is_derived_type ? "override" : "virtual")
+ << " bool " CS_METHOD_HAS_GODOT_CLASS_METHOD "(in godot_string_name method)\n"
+ << INDENT1 "{\n";
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
+ for (const MethodInterface &imethod : itype.methods) {
+ if (!imethod.is_virtual) {
+ continue;
+ }
+
+ // We check for native names (snake_case). If we detect one, we call HasGodotClassMethod
+ // again, but this time with the respective proxy name (PascalCase). It's the job of
+ // user derived classes to override the method and check for those. Our C# source
+ // generators take care of generating those override methods.
+ output << INDENT2 "if (method == MethodName." << imethod.proxy_name
+ << ")\n" INDENT2 "{\n"
+ << INDENT3 "if (" CS_METHOD_HAS_GODOT_CLASS_METHOD "("
+ << CS_STATIC_FIELD_METHOD_PROXY_NAME_PREFIX << imethod.name
+ << ".NativeValue.DangerousSelfRef))\n" INDENT3 "{\n"
+ << INDENT4 "return true;\n"
+ << INDENT3 "}\n" INDENT2 "}\n";
+ }
+
+ if (is_derived_type) {
+ output << INDENT2 "return base." CS_METHOD_HAS_GODOT_CLASS_METHOD "(method);\n";
+ } else {
+ output << INDENT2 "return false;\n";
}
+
+ output << INDENT1 "}\n";
+ }
+
+ //Generate StringName for all class members
+ bool is_inherit = !itype.is_singleton && obj_types.has(itype.base_name);
+ //PropertyName
+ if (is_inherit) {
+ output << MEMBER_BEGIN "public new class PropertyName : " << obj_types[itype.base_name].proxy_name << ".PropertyName";
+ } else {
+ output << MEMBER_BEGIN "public class PropertyName";
+ }
+ output << "\n"
+ << INDENT1 "{\n";
+ for (const PropertyInterface &iprop : itype.properties) {
+ output << INDENT2 "public static readonly StringName " << iprop.proxy_name << " = \"" << iprop.cname << "\";\n";
+ }
+ output << INDENT1 "}\n";
+ //MethodName
+ if (is_inherit) {
+ output << MEMBER_BEGIN "public new class MethodName : " << obj_types[itype.base_name].proxy_name << ".MethodName";
+ } else {
+ output << MEMBER_BEGIN "public class MethodName";
+ }
+ output << "\n"
+ << INDENT1 "{\n";
+ for (const MethodInterface &imethod : itype.methods) {
+ output << INDENT2 "public static readonly StringName " << imethod.proxy_name << " = \"" << imethod.cname << "\";\n";
+ }
+ output << INDENT1 "}\n";
+ //SignalName
+ if (is_inherit) {
+ output << MEMBER_BEGIN "public new class SignalName : " << obj_types[itype.base_name].proxy_name << ".SignalName";
+ } else {
+ output << MEMBER_BEGIN "public class SignalName";
+ }
+ output << "\n"
+ << INDENT1 "{\n";
+ for (const SignalInterface &isignal : itype.signals_) {
+ output << INDENT2 "public static readonly StringName " << isignal.proxy_name << " = \"" << isignal.cname << "\";\n";
}
+ output << INDENT1 "}\n";
- output.append(INDENT1 CLOSE_BLOCK /* class */
- CLOSE_BLOCK /* namespace */);
+ output.append(CLOSE_BLOCK /* class */);
output.append("\n"
"#pragma warning restore CS1591\n"
@@ -1649,12 +1841,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -1669,15 +1861,10 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(prop_cs_type);
p_output.append(" ");
p_output.append(p_iprop.proxy_name);
- p_output.append("\n" INDENT2 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK_L1);
if (getter) {
- p_output.append(INDENT3 "get\n"
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
-
- OPEN_BLOCK_L3);
+ p_output.append(INDENT2 "get\n" OPEN_BLOCK_L2 INDENT3);
p_output.append("return ");
p_output.append(getter->proxy_name + "(");
@@ -1692,21 +1879,11 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(itos(p_iprop.index));
}
}
- p_output.append(");\n"
-
- CLOSE_BLOCK_L3
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning restore CS0618\n");
+ p_output.append(");\n" CLOSE_BLOCK_L2);
}
if (setter) {
- p_output.append(INDENT3 "set\n"
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning disable CS0618 // Disable warning about obsolete method\n"
-
- OPEN_BLOCK_L3);
+ p_output.append(INDENT2 "set\n" OPEN_BLOCK_L2 INDENT3);
p_output.append(setter->proxy_name + "(");
if (p_iprop.index != -1) {
@@ -1720,21 +1897,17 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(itos(p_iprop.index) + ", ");
}
}
- p_output.append("value);\n"
-
- CLOSE_BLOCK_L3
-
- // TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
- "#pragma warning restore CS0618\n");
+ p_output.append("value);\n" CLOSE_BLOCK_L2);
}
- p_output.append(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
return OK;
}
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
- const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
"Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");
@@ -1745,14 +1918,21 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"' from the editor API. Core API cannot have dependencies on the editor API.");
}
- String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
+ String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count);
String arguments_sig;
- String cs_in_statements;
+ StringBuilder cs_in_statements;
+ bool cs_in_expr_is_unsafe = false;
String icall_params = method_bind_field;
+
if (!p_imethod.is_static) {
- icall_params += ", " + sformat(p_itype.cs_in, "this");
+ if (p_itype.cs_in.size()) {
+ cs_in_statements << sformat(p_itype.cs_in, p_itype.c_type, "this",
+ String(), String(), String(), INDENT2);
+ }
+
+ icall_params += ", " + sformat(p_itype.cs_in_expr, "this");
}
StringBuilder default_args_doc;
@@ -1760,7 +1940,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
const ArgumentInterface &first = p_imethod.arguments.front()->get();
for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
"Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
@@ -1813,27 +1994,23 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (iarg.default_argument.size() && iarg.def_param_mode != ArgumentInterface::CONSTANT) {
// The default value of an argument must be constant. Otherwise we make it Nullable and do the following:
// Type arg_in = arg.HasValue ? arg.Value : <non-const default value>;
- String arg_in = iarg.name;
- arg_in += "_in";
+ String arg_or_defval_local = iarg.name;
+ arg_or_defval_local += "OrDefVal";
- cs_in_statements += arg_cs_type;
- cs_in_statements += " ";
- cs_in_statements += arg_in;
- cs_in_statements += " = ";
- cs_in_statements += iarg.name;
+ cs_in_statements << INDENT2 << arg_cs_type << " " << arg_or_defval_local << " = " << iarg.name;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
- cs_in_statements += ".HasValue ? ";
+ cs_in_statements << ".HasValue ? ";
} else {
- cs_in_statements += " != null ? ";
+ cs_in_statements << " != null ? ";
}
- cs_in_statements += iarg.name;
+ cs_in_statements << iarg.name;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
- cs_in_statements += ".Value : ";
+ cs_in_statements << ".Value : ";
} else {
- cs_in_statements += " : ";
+ cs_in_statements << " : ";
}
String cs_type = arg_cs_type;
@@ -1843,10 +2020,18 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String def_arg = sformat(iarg.default_argument, cs_type);
- cs_in_statements += def_arg;
- cs_in_statements += ";\n" INDENT3;
+ cs_in_statements << def_arg << ";\n";
- icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
+ if (arg_type->cs_in.size()) {
+ cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, arg_or_defval_local,
+ String(), String(), String(), INDENT2);
+ }
+
+ if (arg_type->cs_in_expr.is_empty()) {
+ icall_params += arg_or_defval_local;
+ } else {
+ icall_params += sformat(arg_type->cs_in_expr, arg_or_defval_local, arg_type->c_type);
+ }
// Apparently the name attribute must not include the @
String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
@@ -1855,18 +2040,32 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
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);
+ if (arg_type->cs_in.size()) {
+ cs_in_statements << sformat(arg_type->cs_in, arg_type->c_type, iarg.name,
+ String(), String(), String(), INDENT2);
+ }
+
+ icall_params += arg_type->cs_in_expr.is_empty() ? iarg.name : sformat(arg_type->cs_in_expr, iarg.name, arg_type->c_type);
}
+
+ cs_in_expr_is_unsafe |= arg_type->cs_in_expr_is_unsafe;
}
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr ");
- p_output.append(method_bind_field);
- p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
- p_output.append(p_imethod.name);
- p_output.append("\");\n");
+ p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+ << INDENT1 "private static readonly IntPtr " << method_bind_field << " = ";
+
+ if (p_itype.is_singleton) {
+ // Singletons are static classes. They don't derive Godot.Object,
+ // so we need to specify the type to call the static method.
+ p_output << "Object.";
+ }
+
+ p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", MethodName."
+ << p_imethod.proxy_name
+ << ");\n";
}
if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
@@ -1877,12 +2076,12 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
@@ -1890,16 +2089,6 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(default_args_doc.as_string());
}
- if (!p_imethod.is_internal) {
- // TODO: This alone adds ~0.2 MB of bloat to the core API assembly. It would be
- // better to generate a table in the C++ glue instead. That way the strings wouldn't
- // add that much extra bloat as they're already used in engine code. Also, it would
- // probably be much faster than looking up the attributes when fetching methods.
- p_output.append(MEMBER_BEGIN "[GodotMethod(\"");
- p_output.append(p_imethod.name);
- p_output.append("\")]");
- }
-
if (p_imethod.is_deprecated) {
if (p_imethod.deprecation_message.is_empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
@@ -1919,21 +2108,23 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
+ if (cs_in_expr_is_unsafe) {
+ p_output.append("unsafe ");
+ }
+
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);
+ p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L1);
if (p_imethod.is_virtual) {
// Godot virtual method must be overridden, therefore we return a default value by default.
if (return_type->cname == name_cache.type_void) {
- p_output.append("return;\n" CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
} else {
- p_output.append("return default(");
- p_output.append(return_cs_type);
- p_output.append(");\n" CLOSE_BLOCK_L2);
+ p_output.append(INDENT2 "return default;\n" CLOSE_BLOCK_L1);
}
return OK; // Won't increment method bind count
@@ -1942,7 +2133,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (p_imethod.requires_object_call) {
// Fallback to Godot's object.Call(string, params)
- p_output.append(CS_METHOD_CALL "(\"");
+ p_output.append(INDENT2 CS_METHOD_CALL "(\"");
p_output.append(p_imethod.name);
p_output.append("\"");
@@ -1951,7 +2142,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(iarg.name);
}
- p_output.append(");\n" CLOSE_BLOCK_L2);
+ p_output.append(");\n" CLOSE_BLOCK_L1);
return OK; // Won't increment method bind count
}
@@ -1965,20 +2156,21 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call += ".";
im_call += im_icall->name;
- if (p_imethod.arguments.size()) {
- p_output.append(cs_in_statements);
+ if (p_imethod.arguments.size() && cs_in_statements.get_string_length() > 0) {
+ p_output.append(cs_in_statements.as_string());
}
if (return_type->cname == name_cache.type_void) {
- p_output.append(im_call + "(" + icall_params + ");\n");
+ p_output << INDENT2 << im_call << "(" << icall_params << ");\n";
} else if (return_type->cs_out.is_empty()) {
- p_output.append("return " + im_call + "(" + icall_params + ");\n");
+ p_output << INDENT2 "return " << im_call << "(" << icall_params << ");\n";
} else {
- p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out));
+ p_output.append(sformat(return_type->cs_out, im_call, icall_params,
+ return_cs_type, return_type->c_type_out, String(), INDENT2));
p_output.append("\n");
}
- p_output.append(CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L1);
}
p_method_bind_count++;
@@ -1988,11 +2180,17 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output) {
String arguments_sig;
+ String delegate_type_params;
+
+ if (!p_isignal.arguments.is_empty()) {
+ delegate_type_params += "<";
+ }
// Retrieve information from the arguments
const ArgumentInterface &first = p_isignal.arguments.front()->get();
for (const ArgumentInterface &iarg : p_isignal.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
"Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'.");
@@ -2007,15 +2205,81 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
if (&iarg != &first) {
arguments_sig += ", ";
+ delegate_type_params += ", ";
}
arguments_sig += arg_type->cs_type;
arguments_sig += " ";
arguments_sig += iarg.name;
+
+ delegate_type_params += arg_type->cs_type;
+ }
+
+ if (!p_isignal.arguments.is_empty()) {
+ delegate_type_params += ">";
}
// Generate signal
{
+ p_output.append(MEMBER_BEGIN "/// <summary>\n");
+ p_output.append(INDENT1 "/// ");
+ p_output.append("Represents the method that handles the ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "." + p_isignal.proxy_name + "\"/>");
+ p_output.append(" event of a ");
+ p_output.append("<see cref=\"" BINDINGS_NAMESPACE "." + p_itype.proxy_name + "\"/>");
+ p_output.append(" class.\n");
+ p_output.append(INDENT1 "/// </summary>");
+
+ if (p_isignal.is_deprecated) {
+ if (p_isignal.deprecation_message.is_empty()) {
+ WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+ }
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_isignal.deprecation_message);
+ p_output.append("\")]");
+ }
+
+ bool is_parameterless = p_isignal.arguments.size() == 0;
+
+ // Delegate name is [SignalName]EventHandler
+ String delegate_name = is_parameterless ? "Action" : p_isignal.proxy_name + "EventHandler";
+
+ if (!is_parameterless) {
+ // Generate delegate
+ p_output.append(MEMBER_BEGIN "public delegate void ");
+ p_output.append(delegate_name);
+ p_output.append("(");
+ p_output.append(arguments_sig);
+ p_output.append(");\n");
+
+ // Generate Callable trampoline for the delegate
+ p_output << MEMBER_BEGIN "private static void " << p_isignal.proxy_name << "Trampoline"
+ << "(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)\n"
+ << INDENT1 "{\n"
+ << INDENT2 "Callable.ThrowIfArgCountMismatch(args, " << itos(p_isignal.arguments.size()) << ");\n"
+ << INDENT2 "((" << delegate_name << ")delegateObj)(";
+
+ int idx = 0;
+ for (const ArgumentInterface &iarg : p_isignal.arguments) {
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
+
+ if (idx != 0) {
+ p_output << ",";
+ }
+
+ p_output << sformat(arg_type->cs_variant_to_managed,
+ "args[" + itos(idx) + "]", arg_type->cs_type, arg_type->name);
+
+ idx++;
+ }
+
+ p_output << ");\n"
+ << INDENT2 "ret = default;\n"
+ << INDENT1 "}\n";
+ }
+
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -2024,464 +2288,288 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(MEMBER_BEGIN "/// <summary>\n");
for (int i = 0; i < summary_lines.size(); i++) {
- p_output.append(INDENT2 "/// ");
+ p_output.append(INDENT1 "/// ");
p_output.append(summary_lines[i]);
p_output.append("\n");
}
- p_output.append(INDENT2 "/// </summary>");
+ p_output.append(INDENT1 "/// </summary>");
}
}
if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.is_empty()) {
- WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
- }
-
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
p_output.append(p_isignal.deprecation_message);
p_output.append("\")]");
}
- String delegate_name = p_isignal.proxy_name;
- delegate_name += "Handler"; // Delegate name is [SignalName]Handler
-
- // Generate delegate
- p_output.append(MEMBER_BEGIN "public delegate void ");
- p_output.append(delegate_name);
- p_output.append("(");
- p_output.append(arguments_sig);
- p_output.append(");\n");
-
// TODO:
// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
- // Cached signal name (StringName)
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_");
- p_output.append(p_isignal.name);
- p_output.append(" = \"");
- p_output.append(p_isignal.name);
- p_output.append("\";\n");
-
// Generate event
- p_output.append(MEMBER_BEGIN "[Signal]" MEMBER_BEGIN "public ");
+ p_output.append(MEMBER_BEGIN "public ");
if (p_itype.is_singleton) {
p_output.append("static ");
}
+ if (!is_parameterless) {
+ // `unsafe` is needed for taking the trampoline's function pointer
+ p_output << "unsafe ";
+ }
+
p_output.append("event ");
p_output.append(delegate_name);
p_output.append(" ");
p_output.append(p_isignal.proxy_name);
- p_output.append("\n" OPEN_BLOCK_L2);
+ p_output.append("\n" OPEN_BLOCK_L1 INDENT2);
if (p_itype.is_singleton) {
- p_output.append("add => Singleton.Connect(__signal_name_");
+ p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(SignalName.");
} else {
- p_output.append("add => Connect(__signal_name_");
+ p_output.append("add => Connect(SignalName.");
}
- p_output.append(p_isignal.name);
- p_output.append(", new Callable(value));\n");
-
- if (p_itype.is_singleton) {
- p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
+ if (is_parameterless) {
+ // Delegate type is Action. No need for custom trampoline.
+ p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
} else {
- p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+ p_output << p_isignal.proxy_name
+ << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
}
- p_output.append(p_isignal.name);
- p_output.append(", new Callable(value));\n");
- p_output.append(CLOSE_BLOCK_L2);
- }
-
- return OK;
-}
-
-Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
-
- bool dir_exists = DirAccess::exists(p_output_dir);
- ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist.");
-
- StringBuilder output;
-
- output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
- output.append("#include \"" GLUE_HEADER_FILE "\"\n");
- output.append("\n#ifdef MONO_GLUE_ENABLED\n");
-
- generated_icall_funcs.clear();
-
- for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) {
- const TypeInterface &itype = type_elem.value;
-
- bool is_derived_type = itype.base_name != StringName();
-
- if (!is_derived_type) {
- // Some Object assertions
- CRASH_COND(itype.cname != name_cache.type_Object);
- CRASH_COND(!itype.is_instantiable);
- CRASH_COND(itype.api_type != ClassDB::API_CORE);
- CRASH_COND(itype.is_singleton);
- }
-
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
- OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data());
-
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
- for (const MethodInterface &imethod : itype.methods) {
- Error method_err = _generate_glue_method(itype, imethod, output);
- ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
- "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
- }
-
- if (itype.is_singleton) {
- String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX;
- InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr");
-
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
- }
-
- output.append("Object* ");
- output.append(singleton_icall_name);
- output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
- output.append(itype.proxy_name);
- output.append("\");\n" CLOSE_BLOCK "\n");
- }
-
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
-
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
- }
-
- output.append("Object* ");
- output.append(ctor_method);
- output.append("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- output.append(itype.name);
- output.append("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
- }
- }
-
- output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
-
- output.append("uint64_t get_core_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
-
- output.append("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
- output.append("#endif // TOOLS_ENABLED\n");
-
- output.append("uint32_t get_bindings_version() { return ");
- output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
-
- output.append("uint32_t get_cs_glue_version() { return ");
- output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
-
- output.append("\nvoid register_generated_icalls() " OPEN_BLOCK);
- output.append("\tgodot_register_glue_header_icalls();\n");
-
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.append("\tGDMonoUtils::add_internal_call("); \
- output.append("\"" BINDINGS_NAMESPACE "."); \
- output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
- output.append("::"); \
- output.append(m_icall.name); \
- output.append("\", "); \
- output.append(m_icall.name); \
- output.append(");\n"); \
- }
-
- bool tools_sequence = false;
- for (const InternalCall &internal_call : core_custom_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
+ if (p_itype.is_singleton) {
+ p_output.append(INDENT2 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(SignalName.");
} else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
+ p_output.append(INDENT2 "remove => Disconnect(SignalName.");
}
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
-
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
- }
-
- output.append("#ifdef TOOLS_ENABLED\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
- output.append("#endif // TOOLS_ENABLED\n");
-
- for (const InternalCall &internal_call : method_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
+ if (is_parameterless) {
+ // Delegate type is Action. No need for custom trampoline.
+ p_output << p_isignal.proxy_name << ", Callable.From(value));\n";
} else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
+ p_output << p_isignal.proxy_name
+ << ", Callable.CreateWithUnsafeTrampoline(value, &" << p_isignal.proxy_name << "Trampoline));\n";
}
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
-
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
+ p_output.append(CLOSE_BLOCK_L1);
}
-#undef ADD_INTERNAL_CALL_REGISTRATION
-
- output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
-
- output.append("\n#endif // MONO_GLUE_ENABLED\n");
-
- Error save_err = _save_file(path::join(p_output_dir, "mono_glue.gen.cpp"), output);
- if (save_err != OK) {
- return save_err;
- }
-
- OS::get_singleton()->print("Mono glue generated successfully\n");
-
return OK;
}
-uint32_t BindingsGenerator::get_version() {
- return BINDINGS_GENERATOR_VERSION;
-}
+Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output) {
+ bool ret_void = p_icall.return_type.cname == name_cache.type_void;
-Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
- Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
+ const TypeInterface *return_type = _get_type_or_null(p_icall.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
- file->store_string(p_content.as_string());
+ StringBuilder c_func_sig;
+ StringBuilder c_in_statements;
+ StringBuilder c_args_var_content;
- return OK;
-}
+ c_func_sig << "IntPtr " CS_PARAM_METHODBIND;
-Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
- if (p_imethod.is_virtual) {
- return OK; // Ignore
- }
-
- bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
-
- const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
-
- String argc_str = itos(p_imethod.arguments.size());
-
- String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND;
- if (!p_imethod.is_static) {
- c_func_sig += ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
+ if (!p_icall.is_static) {
+ c_func_sig += ", IntPtr " CS_PARAM_INSTANCE;
}
- String c_in_statements;
- String c_args_var_content;
// Get arguments information
int i = 0;
- for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ for (const TypeReference &arg_type_ref : p_icall.argument_types) {
+ const TypeInterface *arg_type = _get_type_or_null(arg_type_ref);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Return type not found
String c_param_name = "arg" + itos(i + 1);
- if (p_imethod.is_vararg) {
- if (i < p_imethod.arguments.size() - 1) {
- c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
- c_in_statements += itos(i);
- c_in_statements += sformat(", &%s_in);\n", c_param_name);
+ if (p_icall.is_vararg) {
+ if (i < p_icall.get_arguments_count() - 1) {
+ String c_in_vararg = arg_type->c_in_vararg;
+
+ if (arg_type->is_object_type) {
+ c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObjectPtr(%1);\n";
+ }
+
+ ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG,
+ "VarArg support not implemented for parameter type: " + arg_type->name);
+
+ c_in_statements
+ << sformat(c_in_vararg, return_type->c_type, c_param_name,
+ String(), String(), String(), INDENT3)
+ << INDENT3 C_LOCAL_PTRCALL_ARGS "[" << itos(i)
+ << "] = new IntPtr(&" << c_param_name << "_in);\n";
}
} else {
if (i > 0) {
- c_args_var_content += ", ";
+ c_args_var_content << ", ";
}
if (arg_type->c_in.size()) {
- c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ c_in_statements << sformat(arg_type->c_in, arg_type->c_type, c_param_name,
+ String(), String(), String(), INDENT2);
}
- c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
+ c_args_var_content << sformat(arg_type->c_arg_in, c_param_name);
}
- c_func_sig += ", ";
- c_func_sig += arg_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += c_param_name;
+ c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name;
i++;
}
- if (return_type->ret_as_byref_arg) {
- c_func_sig += ", ";
- c_func_sig += return_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += "arg_ret";
-
- i++;
- }
+ String icall_method = p_icall.name;
- HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ // Generate icall function
- const InternalCall *im_icall = match->value;
- String icall_method = im_icall->name;
+ r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " "
+ << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK;
- if (!generated_icall_funcs.find(im_icall)) {
- generated_icall_funcs.push_back(im_icall);
+ if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) {
+ String ptrcall_return_type;
+ String initialization;
- if (im_icall->editor_only) {
- p_output.append("#ifdef TOOLS_ENABLED\n");
+ if (return_type->is_object_type) {
+ ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type;
+ initialization = " = default";
+ } else {
+ ptrcall_return_type = return_type->c_type;
}
- // Generate icall function
+ r_output << INDENT2;
- p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " ");
- p_output.append(icall_method);
- p_output.append("(");
- p_output.append(c_func_sig);
- p_output.append(") " OPEN_BLOCK);
-
- if (!ret_void) {
- String ptrcall_return_type;
- String initialization;
+ if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) {
+ r_output << "using ";
- if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) {
- // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
- // a specific return type. We trust this information is valid. We need a temporary local to keep
- // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
- // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
- // Alternatively, we could just return Variant, but that would result in a worse API.
- p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n");
+ if (initialization.is_empty()) {
+ initialization = " = default";
}
+ } else if (return_type->c_ret_needs_default_initialization) {
+ initialization = " = default";
+ }
- if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_ref_counted ? "Ref<RefCounted>" : return_type->c_type;
- initialization = return_type->is_ref_counted ? "" : " = nullptr";
- } else {
- ptrcall_return_type = return_type->c_type;
- }
+ r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n";
+ }
- p_output.append("\t" + ptrcall_return_type);
- p_output.append(" " C_LOCAL_RET);
- p_output.append(initialization + ";\n");
+ if (!p_icall.is_static) {
+ r_output << INDENT2 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n"
+ << INDENT3 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n";
+ }
- String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : return_type->c_type_out + "()";
+ String argc_str = itos(p_icall.get_arguments_count());
- if (!p_imethod.is_static) {
- if (return_type->ret_as_byref_arg) {
- p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
- p_output.append(fail_ret);
- p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n");
- } else {
- p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
- p_output.append(fail_ret);
- p_output.append(");\n");
- }
- }
- } else {
- if (!p_imethod.is_static) {
- p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
- }
- }
-
- if (p_imethod.arguments.size()) {
- if (p_imethod.is_vararg) {
- String vararg_arg = "arg" + argc_str;
- String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
-
- p_output.append("\tint vararg_length = mono_array_length(");
- p_output.append(vararg_arg);
- p_output.append(");\n\tint total_length = ");
- p_output.append(real_argc_str);
- p_output.append(" + vararg_length;\n"
- "\tArgumentsVector<Variant> varargs(vararg_length);\n"
- "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n");
- p_output.append(c_in_statements);
- p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- p_output.append(vararg_arg);
- p_output.append(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- p_output.append(real_argc_str);
- p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK);
- } else {
- p_output.append(c_in_statements);
- p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- p_output.append(argc_str + "] = { ");
- p_output.append(c_args_var_content + " };\n");
- }
- }
+ auto generate_call_and_return_stmts = [&](const char *base_indent) {
+ if (p_icall.is_vararg) {
+ // MethodBind Call
+ r_output << base_indent;
- if (p_imethod.is_vararg) {
- p_output.append("\tCallable::CallError vcall_error;\n\t");
+ // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
+ // a specific return type. We trust this information is valid. We need a temporary local to keep
+ // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
+ // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
+ // Alternatively, we could just return Variant, but that would result in a worse API.
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append(C_LOCAL_VARARG_RET " = ");
+ // Usually the return value takes ownership, but in this case the variant is only used
+ // for conversion to another return type. As such, the local variable takes ownership.
+ r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = ";
} else {
- p_output.append(C_LOCAL_RET " = ");
+ // Variant's [c_out] takes ownership of the variant value
+ r_output << "godot_variant " << C_LOCAL_RET " = ";
}
}
- p_output.append(CS_PARAM_METHODBIND "->call(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr");
- p_output.append(", total_length, vcall_error);\n");
+ r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
+ << ", total_length, out _);\n";
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
+ if (return_type->cname == name_cache.enum_Error) {
+ r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(" C_LOCAL_VARARG_RET ");\n";
+ } else {
+ // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented)
+ CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name);
+ r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n";
+ }
}
}
} else {
- p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, ");
- p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n");
+ // MethodBind PtrCall
+ r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null")
+ << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n");
}
+ // Return statement
+
if (!ret_void) {
if (return_type->c_out.is_empty()) {
- p_output.append("\treturn " C_LOCAL_RET ";\n");
- } else if (return_type->ret_as_byref_arg) {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
+ r_output << base_indent << "return " C_LOCAL_RET ";\n";
} else {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
+ r_output << sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET,
+ return_type->name, String(), String(), base_indent);
}
}
+ };
+
+ if (p_icall.get_arguments_count()) {
+ if (p_icall.is_vararg) {
+ String vararg_arg = "arg" + argc_str;
+ String real_argc_str = itos(p_icall.get_arguments_count() - 1); // Arguments count without vararg
- p_output.append(CLOSE_BLOCK "\n");
+ p_icall.get_arguments_count();
- if (im_icall->editor_only) {
- p_output.append("#endif // TOOLS_ENABLED\n");
+ r_output << INDENT2 "int vararg_length = " << vararg_arg << ".Length;\n"
+ << INDENT2 "int total_length = " << real_argc_str << " + vararg_length;\n";
+
+ r_output << INDENT2 "Span<godot_variant.movable> varargs_span = vararg_length <= VarArgsSpanThreshold ?\n"
+ << INDENT3 "stackalloc godot_variant.movable[VarArgsSpanThreshold] :\n"
+ << INDENT3 "new godot_variant.movable[vararg_length];\n";
+
+ r_output << INDENT2 "Span<IntPtr> " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n"
+ << INDENT3 "stackalloc IntPtr[VarArgsSpanThreshold] :\n"
+ << INDENT3 "new IntPtr[total_length];\n";
+
+ r_output << INDENT2 "fixed (godot_variant.movable* varargs = &MemoryMarshal.GetReference(varargs_span))\n"
+ << INDENT2 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
+ "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n"
+ << OPEN_BLOCK_L2;
+
+ r_output << c_in_statements.as_string();
+
+ r_output << INDENT3 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ << INDENT4 "varargs[i] = " << vararg_arg << "[i].NativeVar;\n"
+ << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
+ << CLOSE_BLOCK_L3;
+
+ generate_call_and_return_stmts(INDENT3);
+
+ r_output << CLOSE_BLOCK_L2;
+ } else {
+ r_output << c_in_statements.as_string();
+
+ r_output << INDENT2 "void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*["
+ << argc_str << "] { " << c_args_var_content.as_string() << " };\n";
+
+ generate_call_and_return_stmts(INDENT2);
}
+ } else {
+ generate_call_and_return_stmts(INDENT2);
}
+ r_output << CLOSE_BLOCK_L1;
+
+ return OK;
+}
+
+Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
+
+ file->store_string(p_content.as_string());
+
return OK;
}
@@ -2514,27 +2602,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
return nullptr;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
- const TypeInterface *found = _get_type_or_null(p_typeref);
-
- if (found) {
- return found;
- }
-
- ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
-
- HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname);
-
- if (match) {
- return &match->value;
- }
-
- TypeInterface placeholder;
- TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
-
- 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 "";
@@ -2548,7 +2615,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
int i = 0;
String params = "<";
for (const TypeReference &param_type : p_generic_type_parameters) {
- const TypeInterface *param_itype = _get_type_or_placeholder(param_type);
+ const TypeInterface *param_itype = _get_type_or_null(param_type);
+ ERR_FAIL_NULL_V(param_itype, "");
ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
"Generic type parameter is a singleton: '" + param_itype->name + "'.");
@@ -2571,6 +2639,16 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
return params;
}
+StringName BindingsGenerator::_get_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta) {
+ if (p_type == Variant::INT) {
+ return _get_int_type_name_from_meta(p_meta);
+ } else if (p_type == Variant::FLOAT) {
+ return _get_float_type_name_from_meta(p_meta);
+ } else {
+ return Variant::get_type_name(p_type);
+ }
+}
+
StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
switch (p_meta) {
case GodotTypeInfo::METADATA_INT_IS_INT8:
@@ -2598,8 +2676,8 @@ StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metada
return "ulong";
break;
default:
- // Assume INT32
- return "int";
+ // Assume INT64
+ return "long";
}
}
@@ -2612,12 +2690,8 @@ StringName BindingsGenerator::_get_float_type_name_from_meta(GodotTypeInfo::Meta
return "double";
break;
default:
- // Assume real_t (float or double depending of REAL_T_IS_DOUBLE)
-#ifdef REAL_T_IS_DOUBLE
+ // Assume FLOAT64
return "double";
-#else
- return "float";
-#endif
}
}
@@ -2666,8 +2740,6 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::RECT2:
case Variant::VECTOR3:
case Variant::RID:
- case Variant::ARRAY:
- case Variant::DICTIONARY:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
@@ -2680,6 +2752,10 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::CALLABLE:
case Variant::SIGNAL:
return p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ case Variant::ARRAY:
+ return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Array_generic;
+ case Variant::DICTIONARY:
+ return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Dictionary_generic;
case Variant::OBJECT:
return p_arg_type.is_object_type;
case Variant::VECTOR2I:
@@ -2744,18 +2820,24 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
itype.memory_own = itype.is_ref_counted;
- itype.c_out = "\treturn ";
+ itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
- itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n";
+ itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n";
- itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)";
+ itype.cs_type = itype.proxy_name;
- itype.c_type = "Object*";
+ if (itype.is_singleton) {
+ itype.cs_in_expr = "Object." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")";
+ } else {
+ itype.cs_in_expr = "Object." CS_STATIC_METHOD_GETINSTANCE "(%0)";
+ }
+
+ itype.cs_out = "%5return (%2)%0(%1);";
+
+ itype.c_arg_in = "(void*)%s";
+ itype.c_type = "IntPtr";
itype.c_type_in = itype.c_type;
- itype.c_type_out = "MonoObject*";
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = "IntPtr";
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_out = "Object";
// Populate properties
@@ -2846,6 +2928,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
imethod.is_virtual = true;
+ itype.has_virtual_methods = true;
}
PropertyInfo return_info = method_info.return_val;
@@ -2887,7 +2970,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
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.cname = Variant::get_type_name(return_info.type) + "_@generic";
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;
@@ -2896,13 +2979,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} else if (return_info.type == Variant::NIL) {
imethod.return_type.cname = name_cache.type_void;
} else {
- if (return_info.type == Variant::INT) {
- imethod.return_type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
- } else if (return_info.type == Variant::FLOAT) {
- imethod.return_type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
- } else {
- imethod.return_type.cname = Variant::get_type_name(return_info.type);
- }
+ imethod.return_type.cname = _get_type_name_from_meta(return_info.type, m ? m->get_argument_meta(-1) : GodotTypeInfo::METADATA_NONE);
}
for (int i = 0; i < argc; i++) {
@@ -2919,20 +2996,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} 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.cname = Variant::get_type_name(arginfo.type) + "_@generic";
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) {
iarg.type.cname = name_cache.type_Variant;
} else {
- if (arginfo.type == Variant::INT) {
- iarg.type.cname = _get_int_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
- } else if (arginfo.type == Variant::FLOAT) {
- iarg.type.cname = _get_float_type_name_from_meta(m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
- } else {
- iarg.type.cname = Variant::get_type_name(arginfo.type);
- }
+ iarg.type.cname = _get_type_name_from_meta(arginfo.type, m ? m->get_argument_meta(i) : GodotTypeInfo::METADATA_NONE);
}
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
@@ -2965,12 +3036,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
if (accessor) {
- 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.
- imethod.is_deprecated = true;
- imethod.deprecation_message = imethod.proxy_name + " is deprecated. Use the " + accessor_property->proxy_name + " property instead.";
+ // We only make internal 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.
+ imethod.is_internal = true;
}
if (itype.class_doc) {
@@ -2985,7 +3054,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(itype.find_property_by_name(imethod.cname), false,
"Method name conflicts with property: '" + itype.name + "." + imethod.name + "'.");
- // Classes starting with an underscore are ignored unless they're used as a property setter or getter
+ // Methods starting with an underscore are ignored unless they're used as a property setter or getter
if (!imethod.is_virtual && imethod.name[0] == '_') {
for (const PropertyInterface &iprop : itype.properties) {
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
@@ -3027,20 +3096,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} 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.cname = Variant::get_type_name(arginfo.type) + "_@generic";
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) {
iarg.type.cname = name_cache.type_Variant;
} else {
- if (arginfo.type == Variant::INT) {
- iarg.type.cname = _get_int_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
- } else if (arginfo.type == Variant::FLOAT) {
- iarg.type.cname = _get_float_type_name_from_meta(GodotTypeInfo::METADATA_NONE);
- } else {
- iarg.type.cname = Variant::get_type_name(arginfo.type);
- }
+ iarg.type.cname = _get_type_name_from_meta(arginfo.type, GodotTypeInfo::METADATA_NONE);
}
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
@@ -3090,9 +3153,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
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,
- // so we append 'Enum' to the enum name in those cases.
+ if (itype.find_property_by_proxy_name(enum_proxy_name) || itype.find_method_by_proxy_name(enum_proxy_name) || itype.find_signal_by_proxy_name(enum_proxy_name)) {
+ // In case the enum name conflicts with other PascalCase members,
+ // we append 'Enum' to the enum name in those cases.
+ // We have several conflicts between enums and PascalCase properties.
enum_proxy_name += "Enum";
enum_proxy_cname = StringName(enum_proxy_name);
}
@@ -3139,7 +3203,15 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
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);
+ String constant_proxy_name = snake_to_pascal_case(constant_name, true);
+
+ if (itype.find_property_by_proxy_name(constant_proxy_name) || itype.find_method_by_proxy_name(constant_proxy_name) || itype.find_signal_by_proxy_name(constant_proxy_name)) {
+ // In case the constant name conflicts with other PascalCase members,
+ // we append 'Constant' to the constant name in those cases.
+ constant_proxy_name += "Constant";
+ }
+
+ ConstantInterface iconstant(constant_name, constant_proxy_name, *value);
iconstant.const_doc = nullptr;
for (int i = 0; i < itype.class_doc->constants.size(); i++) {
@@ -3169,7 +3241,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
switch (p_val.get_type()) {
case Variant::NIL:
// Either Object type or Variant
- r_iarg.default_argument = "null";
+ r_iarg.default_argument = "default";
break;
// Atomic types
case Variant::BOOL:
@@ -3189,8 +3261,13 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
case Variant::STRING_NAME:
case Variant::NODE_PATH:
if (r_iarg.type.cname == name_cache.type_StringName || r_iarg.type.cname == name_cache.type_NodePath) {
- r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ if (r_iarg.default_argument.length() > 0) {
+ r_iarg.default_argument = "(%s)\"" + r_iarg.default_argument + "\"";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ } else {
+ // No need for a special `in` statement to change `null` to `""`. Marshaling takes care of this already.
+ r_iarg.default_argument = "null";
+ }
} else {
CRASH_COND(r_iarg.type.cname != name_cache.type_String);
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
@@ -3236,8 +3313,11 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "null";
break;
case Variant::DICTIONARY:
- r_iarg.default_argument = "new %s()";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ ERR_FAIL_COND_V_MSG(!p_val.operator Dictionary().is_empty(), false,
+ "Default value of type 'Dictionary' must be an empty dictionary.");
+ // The [cs_in] expression already interprets null values as empty dictionaries.
+ r_iarg.default_argument = "null";
+ r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
break;
case Variant::RID:
ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
@@ -3246,11 +3326,14 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
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.");
- r_iarg.default_argument = "null";
+ r_iarg.default_argument = "default";
break;
case Variant::ARRAY:
- r_iarg.default_argument = "new %s { }";
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ ERR_FAIL_COND_V_MSG(!p_val.operator Array().is_empty(), false,
+ "Default value of type 'Array' must be an empty array.");
+ // The [cs_in] expression already interprets null values as empty arrays.
+ r_iarg.default_argument = "null";
+ r_iarg.def_param_mode = ArgumentInterface::CONSTANT;
break;
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
@@ -3284,11 +3367,11 @@ 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()) {
+ Projection projection = p_val.operator Projection();
+ if (projection == 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.default_argument = "new Projection(new Vector4" + projection.columns[0].operator String() + ", new Vector4" + projection.columns[1].operator String() + ", new Vector4" + projection.columns[2].operator String() + ", new Vector4" + projection.columns[3].operator String() + ")";
}
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
} break;
@@ -3325,12 +3408,12 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "default";
break;
default:
- CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
+ ERR_FAIL_V_MSG(false, "Unexpected Variant type: " + itos(p_val.get_type()));
break;
}
- if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") {
- r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "default") {
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
}
return true;
@@ -3341,20 +3424,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
TypeInterface itype;
-#define INSERT_STRUCT_TYPE(m_type) \
- { \
- itype = TypeInterface::create_value_type(String(#m_type)); \
- itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \
- itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \
- itype.c_arg_in = "&%s_in"; \
- itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \
- itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
- itype.cs_in = "ref %s"; \
- /* in cs_out, im_type_out (%3) includes the 'out ' part */ \
- itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \
- itype.im_type_out = "out " + itype.cs_type; \
- itype.ret_as_byref_arg = true; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_STRUCT_TYPE(m_type) \
+ { \
+ itype = TypeInterface::create_value_type(String(#m_type)); \
+ itype.c_type_in = #m_type "*"; \
+ itype.c_type_out = itype.cs_type; \
+ itype.cs_in_expr = "&%0"; \
+ itype.cs_in_expr_is_unsafe = true; \
+ builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2)
@@ -3370,54 +3447,56 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_STRUCT_TYPE(AABB)
INSERT_STRUCT_TYPE(Color)
INSERT_STRUCT_TYPE(Plane)
+ INSERT_STRUCT_TYPE(Vector4)
+ INSERT_STRUCT_TYPE(Vector4i)
+ INSERT_STRUCT_TYPE(Projection)
#undef INSERT_STRUCT_TYPE
// bool
itype = TypeInterface::create_value_type(String("bool"));
- {
- // MonoBoolean <---> bool
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "bool";
- itype.c_type_in = "MonoBoolean";
- itype.c_type_out = itype.c_type_in;
- itype.c_arg_in = "&%s_in";
- }
- itype.im_type_in = itype.name;
- itype.im_type_out = itype.name;
+ itype.cs_in_expr = "%0.ToGodotBool()";
+ itype.cs_out = "%5return %0(%1).ToBool();";
+ itype.c_type = "godot_bool";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.c_type;
+ itype.c_arg_in = "&%s";
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
builtin_types.insert(itype.cname, itype);
// Integer types
{
// C interface for 'uint32_t' is the same as that of enums. Remember to apply
// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
-#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \
- { \
- itype = TypeInterface::create_value_type(String(m_name)); \
- { \
- itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \
- itype.c_out = "\treturn (%0)%1;\n"; \
- itype.c_type = #m_c_type; \
- itype.c_arg_in = "&%s_in"; \
- } \
- itype.c_type_in = #m_c_type_in_out; \
- itype.c_type_out = itype.c_type_in; \
- itype.im_type_in = itype.name; \
- itype.im_type_out = itype.name; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_INT_TYPE(m_name, m_int_struct_name) \
+ { \
+ itype = TypeInterface::create_value_type(String(m_name)); \
+ if (itype.name != "long" && itype.name != "ulong") { \
+ itype.c_in = "%5%0 %1_in = %1;\n"; \
+ itype.c_out = "%5return (%0)%1;\n"; \
+ itype.c_type = "long"; \
+ itype.c_arg_in = "&%s_in"; \
+ } else { \
+ itype.c_arg_in = "&%s"; \
+ } \
+ itype.c_type_in = itype.name; \
+ itype.c_type_out = itype.name; \
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
+ builtin_types.insert(itype.cname, itype); \
}
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
- INSERT_INT_TYPE("sbyte", int8_t, int64_t);
- INSERT_INT_TYPE("short", int16_t, int64_t);
- INSERT_INT_TYPE("int", int32_t, int64_t);
- INSERT_INT_TYPE("long", int64_t, int64_t);
- INSERT_INT_TYPE("byte", uint8_t, int64_t);
- INSERT_INT_TYPE("ushort", uint16_t, int64_t);
- INSERT_INT_TYPE("uint", uint32_t, int64_t);
- INSERT_INT_TYPE("ulong", uint64_t, int64_t);
+ INSERT_INT_TYPE("sbyte", "Int8");
+ INSERT_INT_TYPE("short", "Int16");
+ INSERT_INT_TYPE("int", "Int32");
+ INSERT_INT_TYPE("long", "Int64");
+ INSERT_INT_TYPE("byte", "UInt8");
+ INSERT_INT_TYPE("ushort", "UInt16");
+ INSERT_INT_TYPE("uint", "UInt32");
+ INSERT_INT_TYPE("ulong", "UInt64");
+
+#undef INSERT_INT_TYPE
}
// Floating point types
@@ -3427,18 +3506,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "float";
itype.cname = itype.name;
itype.proxy_name = "float";
+ itype.cs_type = itype.proxy_name;
{
// The expected type for 'float' in ptrcall is 'double'
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_in = "%5%0 %1_in = %1;\n";
+ itype.c_out = "%5return (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "float";
- itype.c_type_out = "float";
itype.c_arg_in = "&%s_in";
}
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_in = itype.proxy_name;
+ itype.c_type_out = itype.proxy_name;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
builtin_types.insert(itype.cname, itype);
// double
@@ -3446,15 +3524,12 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "double";
itype.cname = itype.name;
itype.proxy_name = "double";
- {
- itype.c_type = "double";
- itype.c_type_in = "double";
- itype.c_type_out = "double";
- itype.c_arg_in = "&%s";
- }
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type = "double";
+ itype.c_arg_in = "&%s";
+ itype.c_type_in = itype.proxy_name;
+ itype.c_type_out = itype.proxy_name;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
builtin_types.insert(itype.cname, itype);
}
@@ -3463,15 +3538,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "String";
itype.cname = itype.name;
itype.proxy_name = "string";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "MonoString*";
- itype.c_type_out = "MonoString*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
+ itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_string";
+ itype.c_type_in = itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n";
builtin_types.insert(itype.cname, itype);
// StringName
@@ -3479,17 +3554,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "StringName";
itype.cname = itype.name;
itype.proxy_name = "StringName";
- itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n";
- itype.c_out = "\treturn memnew(StringName(%1));\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in_expr = "(%1)(%0?.NativeValue ?? default)";
+ // Cannot pass null StringName to ptrcall
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_string_name";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n";
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
builtin_types.insert(itype.cname, itype);
// NodePath
@@ -3497,15 +3572,16 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "NodePath";
itype.cname = itype.name;
itype.proxy_name = "NodePath";
- itype.c_out = "\treturn memnew(NodePath(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in_expr = "(%1)(%0?.NativeValue ?? default)";
+ // Cannot pass null NodePath to ptrcall
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_node_path";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
builtin_types.insert(itype.cname, itype);
// RID
@@ -3513,112 +3589,101 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "RID";
itype.cname = itype.name;
itype.proxy_name = "RID";
- itype.c_out = "\treturn memnew(RID(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.c_arg_in = "&%s";
+ itype.c_type = itype.cs_type;
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.c_type;
builtin_types.insert(itype.cname, itype);
// Variant
itype = TypeInterface();
itype.name = "Variant";
itype.cname = itype.name;
- itype.proxy_name = "object";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
+ itype.proxy_name = "Variant";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "object";
- itype.im_type_out = itype.proxy_name;
+ itype.c_in = "%5%0 %1_in = (%0)%1.NativeVar;\n";
+ itype.c_out = "%5return Variant.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_variant";
+ itype.c_type_in = itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
builtin_types.insert(itype.cname, itype);
// Callable
itype = TypeInterface::create_value_type(String("Callable"));
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n";
- itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n";
+ itype.cs_in_expr = "%0";
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(in %1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(in %1);\n";
itype.c_arg_in = "&%s_in";
- itype.c_type_in = "GDMonoMarshal::M_Callable*";
- itype.c_type_out = "GDMonoMarshal::M_Callable";
- itype.cs_in = "ref %s";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.im_type_out = "out " + itype.cs_type;
- itype.ret_as_byref_arg = true;
+ itype.c_type = "godot_callable";
+ itype.c_type_in = "in " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
builtin_types.insert(itype.cname, itype);
// Signal
itype = TypeInterface();
itype.name = "Signal";
itype.cname = itype.name;
- itype.proxy_name = "SignalInfo";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n";
- itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "GDMonoMarshal::M_SignalInfo*";
- itype.c_type_out = "GDMonoMarshal::M_SignalInfo";
- itype.cs_in = "ref %s";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
+ itype.proxy_name = "Signal";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.cs_type;
- itype.im_type_out = "out " + itype.cs_type;
- itype.ret_as_byref_arg = true;
+ itype.cs_in_expr = "%0";
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(in %1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(in %1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_signal";
+ itype.c_type_in = "in " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
builtin_types.insert(itype.cname, itype);
// VarArg (fictitious type to represent variable arguments)
itype = TypeInterface();
itype.name = "VarArg";
itype.cname = itype.name;
- itype.proxy_name = "object[]";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n";
+ itype.proxy_name = "Variant[]";
+ itype.cs_type = "params Variant[]";
+ itype.cs_in_expr = "%0 ?? Array.Empty<Variant>()";
+ // c_type, c_in and c_arg_in are hard-coded in the generator.
+ // c_out and c_type_out are not applicable to VarArg.
itype.c_arg_in = "&%s_in";
- itype.c_type = "Array";
- itype.c_type_in = "MonoArray*";
- itype.cs_type = "params object[]";
- itype.im_type_in = "object[]";
+ itype.c_type_in = "Variant[]";
builtin_types.insert(itype.cname, itype);
-#define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \
- { \
- itype = TypeInterface(); \
- itype.name = #m_name; \
- itype.cname = itype.name; \
- itype.proxy_name = #m_proxy_t "[]"; \
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
- itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
- itype.c_arg_in = "&%s_in"; \
- itype.c_type = #m_type; \
- itype.c_type_in = "MonoArray*"; \
- itype.c_type_out = "MonoArray*"; \
- itype.cs_type = itype.proxy_name; \
- itype.im_type_in = itype.proxy_name; \
- itype.im_type_out = itype.proxy_name; \
- builtin_types.insert(itype.name, itype); \
+#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
+ { \
+ itype = TypeInterface(); \
+ itype.name = #m_name; \
+ itype.cname = itype.name; \
+ itype.proxy_name = #m_proxy_t "[]"; \
+ itype.cs_type = itype.proxy_name; \
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
+ itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
+ itype.c_arg_in = "&%s_in"; \
+ itype.c_type = #m_managed_type; \
+ itype.c_type_in = itype.proxy_name; \
+ itype.c_type_out = itype.proxy_name; \
+ itype.c_type_is_disposable_struct = true; \
+ builtin_types.insert(itype.name, itype); \
}
-#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
+#define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t)
- INSERT_ARRAY(PackedInt32Array, int);
- INSERT_ARRAY(PackedInt64Array, long);
- INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte);
+ INSERT_ARRAY(PackedInt32Array, godot_packed_int32_array, int);
+ INSERT_ARRAY(PackedInt64Array, godot_packed_int64_array, long);
+ INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, godot_packed_byte_array, byte);
- INSERT_ARRAY(PackedFloat32Array, float);
- INSERT_ARRAY(PackedFloat64Array, double);
+ INSERT_ARRAY(PackedFloat32Array, godot_packed_float32_array, float);
+ INSERT_ARRAY(PackedFloat64Array, godot_packed_float64_array, double);
- INSERT_ARRAY(PackedStringArray, string);
+ INSERT_ARRAY(PackedStringArray, godot_packed_string_array, string);
- INSERT_ARRAY(PackedColorArray, Color);
- INSERT_ARRAY(PackedVector2Array, Vector2);
- INSERT_ARRAY(PackedVector3Array, Vector3);
+ INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color);
+ INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2);
+ INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
#undef INSERT_ARRAY
@@ -3628,15 +3693,25 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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 %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in_expr = "(%1)(%0 ?? new()).NativeValue";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_array";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ builtin_types.insert(itype.cname, itype);
+
+ // Array_@generic
+ // Re-use Array's itype
+ itype.name = "Array_@generic";
+ itype.cname = itype.name;
+ itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToArray(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)";
builtin_types.insert(itype.cname, itype);
// Dictionary
@@ -3645,15 +3720,25 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
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 %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in_expr = "(%1)(%0 ?? new()).NativeValue";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s";
+ itype.c_type = "godot_dictionary";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ builtin_types.insert(itype.cname, itype);
+
+ // Dictionary_@generic
+ // Re-use Dictionary's itype
+ itype.name = "Dictionary_@generic";
+ itype.cname = itype.name;
+ itype.cs_out = "%5return new %2(%0(%1));";
+ // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case
+ itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionary(%0)";
+ itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
@@ -3661,12 +3746,10 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "void";
itype.cname = itype.name;
itype.proxy_name = itype.name;
- itype.c_type = itype.name;
+ itype.cs_type = itype.proxy_name;
+ itype.c_type = itype.proxy_name;
itype.c_type_in = itype.c_type;
itype.c_type_out = itype.c_type;
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
}
@@ -3701,7 +3784,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
+ ienum.is_flags = CoreConstants::is_global_constant_bitfield(i);
List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
if (enum_match) {
enum_match->get().constants.push_back(iconstant);
@@ -3791,21 +3874,18 @@ void BindingsGenerator::_initialize() {
// Generate internal calls (after populating type interfaces and global constants)
- core_custom_icalls.clear();
- editor_custom_icalls.clear();
-
for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
- _generate_method_icalls(E.value);
+ const TypeInterface &itype = E.value;
+ Error err = _populate_method_icalls_table(itype);
+ ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name);
}
initialized = true;
}
static String generate_all_glue_option = "--generate-mono-glue";
-static String generate_cs_glue_option = "--generate-mono-cs-glue";
-static String generate_cpp_glue_option = "--generate-mono-cpp-glue";
-static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) {
+static void handle_cmdline_options(String glue_dir_path) {
BindingsGenerator bindings_generator;
bindings_generator.set_log_print_enabled(true);
@@ -3814,43 +3894,25 @@ static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, Str
return;
}
- if (glue_dir_path.length()) {
- if (bindings_generator.generate_glue(glue_dir_path) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue.");
- }
-
- if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
- }
- }
+ CRASH_COND(glue_dir_path.is_empty());
- if (cs_dir_path.length()) {
- if (bindings_generator.generate_cs_api(cs_dir_path) != OK) {
- ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API.");
- }
+ if (bindings_generator.generate_cs_api(glue_dir_path.path_join(API_SOLUTION_NAME)) != OK) {
+ ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
}
+}
- if (cpp_dir_path.length()) {
- if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
- ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
- }
- }
+static void cleanup_and_exit_godot() {
+ // Exit once done
+ Main::cleanup(true);
+ ::exit(0);
}
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- const int NUM_OPTIONS = 2;
-
String glue_dir_path;
- String cs_dir_path;
- String cpp_dir_path;
-
- int options_left = NUM_OPTIONS;
-
- bool exit_godot = false;
const List<String>::Element *elem = p_cmdline_args.front();
- while (elem && options_left) {
+ while (elem) {
if (elem->get() == generate_all_glue_option) {
const List<String>::Element *path_elem = elem->next();
@@ -3859,48 +3921,20 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
- exit_godot = true;
+ // Exit once done with invalid command line arguments
+ cleanup_and_exit_godot();
}
- --options_left;
- } else if (elem->get() == generate_cs_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cs_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cs_glue_option + ": No output directory specified.");
- exit_godot = true;
- }
-
- --options_left;
- } else if (elem->get() == generate_cpp_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cpp_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cpp_glue_option + ": No output directory specified.");
- exit_godot = true;
- }
-
- --options_left;
+ break;
}
elem = elem->next();
}
- if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
- handle_cmdline_options(glue_dir_path, cs_dir_path, cpp_dir_path);
- exit_godot = true;
- }
-
- if (exit_godot) {
+ if (glue_dir_path.length()) {
+ handle_cmdline_options(glue_dir_path);
// Exit once done
- Main::cleanup(true);
- ::exit(0);
+ cleanup_and_exit_godot();
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index ee170e4558..cef8246032 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* bindings_generator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* bindings_generator.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 BINDINGS_GENERATOR_H
#define BINDINGS_GENERATOR_H
@@ -209,7 +209,7 @@ class BindingsGenerator {
String name;
StringName cname;
- int type_parameter_count;
+ int type_parameter_count = 0;
/**
* Identifier name of the base class.
@@ -229,6 +229,23 @@ class BindingsGenerator {
bool is_ref_counted = false;
/**
+ * Determines whether the native return value of this type must be disposed
+ * by the generated internal call (think of `godot_string`, whose destructor
+ * must be called). Some structs that are disposable may still disable this
+ * flag if the ownership is transferred.
+ */
+ bool c_type_is_disposable_struct = false;
+
+ /**
+ * Determines whether the native return value of this type must be zero initialized
+ * before its address is passed to ptrcall. This is required for types whose destructor
+ * is called before being assigned the return value by `PtrToArg::encode`, e.g.:
+ * Array, Dictionary, String, StringName, Variant.
+ * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`.
+ */
+ bool c_ret_needs_default_initialization = false;
+
+ /**
* Used only by Object-derived types.
* Determines if this type is not abstract (incomplete).
* e.g.: CanvasItem cannot be instantiated.
@@ -242,31 +259,34 @@ class BindingsGenerator {
*/
bool memory_own = false;
- /**
- * This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value
- * with internal calls, so we must use pointers instead. Returns must be replace with out parameters.
- * In this case, [c_out] and [cs_out] must have a different format, explained below.
- * The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM).
- */
- bool ret_as_byref_arg = false;
-
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
// !! When renaming those fields, make sure to rename their references in the comments
// --- C INTERFACE ---
- static const char *DEFAULT_VARARG_C_IN;
+ /**
+ * One or more statements that transform the parameter before being passed as argument of a ptrcall.
+ * If the statement adds a local that must be passed as the argument instead of the parameter,
+ * the expression with the name of that local must be specified with [c_arg_in].
+ * Formatting elements:
+ * %0: [c_type] of the parameter
+ * %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
+ */
+ String c_in;
/**
- * One or more statements that manipulate the parameter before being passed as argument of a ptrcall.
+ * One or more statements that transform the parameter before being passed as argument of a vararg call.
* If the statement adds a local that must be passed as the argument instead of the parameter,
* the name of that local must be specified with [c_arg_in].
- * For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead.
* Formatting elements:
* %0: [c_type] of the parameter
* %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
*/
- String c_in;
+ String c_in_vararg;
/**
* Determines the expression that will be passed as argument to ptrcall.
@@ -291,7 +311,8 @@ class BindingsGenerator {
* %0: [c_type_out] of the return type
* %1: name of the variable to be returned
* %2: [name] of the return type
- * %3: name of the parameter that must be assigned the return value
+ * %3-4: reserved
+ * %5: indentation text
*/
String c_out;
@@ -327,7 +348,21 @@ class BindingsGenerator {
* An expression that overrides the way the parameter is passed to the internal call.
* If empty, the parameter is passed as is.
* Formatting elements:
- * %0 or %s: name of the parameter
+ * %0: name of the parameter
+ * %1: [c_type] of the parameter
+ */
+ String cs_in_expr;
+ bool cs_in_expr_is_unsafe = false;
+
+ /**
+ * One or more statements that transform the parameter before being passed to the internal call.
+ * If the statement adds a local that must be passed as the argument instead of the parameter,
+ * the expression with the name of that local must be specified with [cs_in_expr].
+ * Formatting elements:
+ * %0: [c_type] of the parameter
+ * %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
*/
String cs_in;
@@ -338,7 +373,9 @@ class BindingsGenerator {
* %0: internal method name
* %1: internal method call arguments without surrounding parenthesis
* %2: [cs_type] of the return type
- * %3: [im_type_out] of the return type
+ * %3: [c_type_out] of the return type
+ * %4: reserved
+ * %5: indentation text
*/
String cs_out;
@@ -349,14 +386,20 @@ class BindingsGenerator {
String cs_type;
/**
- * Type used for parameters of internal call methods.
+ * Formatting elements:
+ * %0: input expression of type `in godot_variant`
+ * %1: [cs_type] of this type
+ * %2: [name] of this type
*/
- String im_type_in;
+ String cs_variant_to_managed;
/**
- * Type used for the return type of internal call methods.
+ * Formatting elements:
+ * %0: input expression
+ * %1: [cs_type] of this type
+ * %2: [name] of this type
*/
- String im_type_out;
+ String cs_managed_to_variant;
const DocData::ClassDoc *class_doc = nullptr;
@@ -366,6 +409,8 @@ class BindingsGenerator {
List<MethodInterface> methods;
List<SignalInterface> signals_;
+ bool has_virtual_methods = false;
+
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
for (const MethodInterface &E : methods) {
if (E.cname == p_cname) {
@@ -432,8 +477,8 @@ class BindingsGenerator {
itype.c_type = itype.name;
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_in = itype.proxy_name + "*";
+ itype.c_type_out = itype.proxy_name;
itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name];
}
@@ -467,65 +512,32 @@ class BindingsGenerator {
return itype;
}
- static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) {
- r_itype.name = p_cname;
- r_itype.cname = p_cname;
- r_itype.proxy_name = r_itype.name;
-
- r_itype.c_type = r_itype.name;
- r_itype.c_type_in = "MonoObject*";
- r_itype.c_type_out = "MonoObject*";
- r_itype.cs_type = r_itype.proxy_name;
- r_itype.im_type_in = r_itype.proxy_name;
- r_itype.im_type_out = r_itype.proxy_name;
- }
-
- static void postsetup_enum_type(TypeInterface &r_enum_itype) {
- // C interface for enums is the same as that of 'uint32_t'. Remember to apply
- // any of the changes done here to the 'uint32_t' type interface as well.
+ static void postsetup_enum_type(TypeInterface &r_enum_itype);
- r_enum_itype.c_arg_in = "&%s_in";
- {
- // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
- r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- r_enum_itype.c_out = "\treturn (%0)%1;\n";
- r_enum_itype.c_type = "int64_t";
- }
- r_enum_itype.c_type_in = "int32_t";
- r_enum_itype.c_type_out = r_enum_itype.c_type_in;
-
- r_enum_itype.cs_type = r_enum_itype.proxy_name;
- r_enum_itype.cs_in = "(int)%s";
- r_enum_itype.cs_out = "return (%2)%0(%1);";
- r_enum_itype.im_type_in = "int";
- r_enum_itype.im_type_out = "int";
- r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
+ TypeInterface() {
+ static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)";
+ static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)";
+ cs_variant_to_managed = default_cs_variant_to_managed;
+ cs_managed_to_variant = default_cs_managed_to_variant;
}
-
- TypeInterface() {}
};
struct InternalCall {
String name;
- String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
- String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
bool editor_only = false;
- InternalCall() {}
+ bool is_vararg = false;
+ bool is_static = false;
+ TypeReference return_type;
+ List<TypeReference> argument_types;
- InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
- name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
- unique_sig = p_unique_sig;
- editor_only = false;
- }
+ _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); }
+
+ InternalCall() {}
- InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
+ InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) {
name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = api_type == ClassDB::API_EDITOR;
}
@@ -540,7 +552,6 @@ class BindingsGenerator {
HashMap<StringName, TypeInterface> obj_types;
- HashMap<StringName, TypeInterface> placeholder_types;
HashMap<StringName, TypeInterface> builtin_types;
HashMap<StringName, TypeInterface> enum_types;
@@ -548,13 +559,9 @@ class BindingsGenerator {
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
+ /// Stores the unique internal calls from [method_icalls] that are assigned to each method.
HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
- List<const InternalCall *> generated_icall_funcs;
-
- List<InternalCall> core_custom_icalls;
- List<InternalCall> editor_custom_icalls;
-
HashMap<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -571,6 +578,8 @@ class BindingsGenerator {
StringName type_String = StaticCString::create("String");
StringName type_StringName = StaticCString::create("StringName");
StringName type_NodePath = StaticCString::create("NodePath");
+ StringName type_Array_generic = StaticCString::create("Array_@generic");
+ StringName type_Dictionary_generic = StaticCString::create("Dictionary_@generic");
StringName type_at_GlobalScope = StaticCString::create("@GlobalScope");
StringName enum_Error = StaticCString::create("Error");
@@ -595,12 +604,14 @@ class BindingsGenerator {
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;
+ static constexpr int nullable_types_count = 18;
StringName nullable_types[nullable_types_count] = {
type_String,
type_StringName,
type_NodePath,
+ type_Array_generic,
+ type_Dictionary_generic,
StaticCString::create(_STR(Array)),
StaticCString::create(_STR(Dictionary)),
StaticCString::create(_STR(Callable)),
@@ -636,17 +647,6 @@ class BindingsGenerator {
NameCache name_cache;
- const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) {
- const List<InternalCall>::Element *it = p_list.front();
- while (it) {
- if (it->get().name == p_name) {
- return it;
- }
- it = it->next();
- }
- return nullptr;
- }
-
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
for (const ConstantInterface &E : p_constants) {
if (E.name == p_name) {
@@ -657,18 +657,38 @@ class BindingsGenerator {
return nullptr;
}
- inline String get_unique_sig(const TypeInterface &p_type) {
- if (p_type.is_ref_counted) {
- return "Ref";
- } else if (p_type.is_object_type) {
+ inline String get_arg_unique_sig(const TypeInterface &p_type) {
+ // For parameters, we treat reference and non-reference derived types the same.
+ if (p_type.is_object_type) {
return "Obj";
} else if (p_type.is_enum) {
return "int";
+ } else if (p_type.cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type.cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
}
return p_type.name;
}
+ inline String get_ret_unique_sig(const TypeInterface *p_type) {
+ // Reference derived return types are treated differently.
+ if (p_type->is_ref_counted) {
+ return "Ref";
+ } else if (p_type->is_object_type) {
+ return "Obj";
+ } else if (p_type->is_enum) {
+ return "int";
+ } else if (p_type->cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type->cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
+ }
+
+ return p_type->name;
+ }
+
String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
void _append_xml_method(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);
@@ -682,13 +702,13 @@ class BindingsGenerator {
int _determine_enum_prefix(const EnumInterface &p_ienum);
void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
- void _generate_method_icalls(const TypeInterface &p_itype);
+ Error _populate_method_icalls_table(const TypeInterface &p_itype);
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_type_name_from_meta(Variant::Type p_type, GodotTypeInfo::Metadata p_meta);
StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
@@ -706,11 +726,11 @@ class BindingsGenerator {
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
+ Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output);
+
void _generate_array_extensions(StringBuilder &p_output);
void _generate_global_constants(StringBuilder &p_output);
- Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output);
-
Error _save_file(const String &p_path, const StringBuilder &p_content);
void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -721,15 +741,12 @@ public:
Error generate_cs_core_project(const String &p_proj_dir);
Error generate_cs_editor_project(const String &p_proj_dir);
Error generate_cs_api(const String &p_output_dir);
- Error generate_glue(const String &p_output_dir);
_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
_FORCE_INLINE_ bool is_initialized() { return initialized; }
- static uint32_t get_version();
-
static void handle_cmdline_args(const List<String> &p_cmdline_args);
BindingsGenerator() {
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index a1789412f4..ae02e16256 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* code_completion.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* code_completion.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "code_completion.h"
@@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "scene/gui/control.h"
#include "scene/main/node.h"
+#include "scene/theme/theme_db.h"
namespace gdmono {
@@ -139,7 +140,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
}
} break;
case CompletionKind::RESOURCE_PATHS: {
- if (bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ if (bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), suggestions);
}
} break;
@@ -162,9 +163,9 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
}
if (dir_access->dir_exists(filename)) {
- directories.push_back(dir_access->get_current_dir().plus_file(filename));
+ directories.push_back(dir_access->get_current_dir().path_join(filename));
} else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) {
- suggestions.push_back(quoted(dir_access->get_current_dir().plus_file(filename)));
+ suggestions.push_back(quoted(dir_access->get_current_dir().path_join(filename)));
}
filename = dir_access->get_next();
@@ -172,7 +173,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
}
} break;
case CompletionKind::SHADER_PARAMS: {
- print_verbose("Shared params completion for C# not implemented.");
+ print_verbose("Shader uniforms completion for C# is not implemented yet.");
} break;
case CompletionKind::SIGNALS: {
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
@@ -195,7 +196,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
Node *base = _try_find_owner_node_in_tree(script);
if (base && Object::cast_to<Control>(base)) {
List<StringName> sn;
- Theme::get_default()->get_color_list(base->get_class(), &sn);
+ ThemeDB::get_singleton()->get_default_theme()->get_color_list(base->get_class(), &sn);
for (const StringName &E : sn) {
suggestions.push_back(quoted(E));
@@ -207,7 +208,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
Node *base = _try_find_owner_node_in_tree(script);
if (base && Object::cast_to<Control>(base)) {
List<StringName> sn;
- Theme::get_default()->get_constant_list(base->get_class(), &sn);
+ ThemeDB::get_singleton()->get_default_theme()->get_constant_list(base->get_class(), &sn);
for (const StringName &E : sn) {
suggestions.push_back(quoted(E));
@@ -219,7 +220,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
Node *base = _try_find_owner_node_in_tree(script);
if (base && Object::cast_to<Control>(base)) {
List<StringName> sn;
- Theme::get_default()->get_font_list(base->get_class(), &sn);
+ ThemeDB::get_singleton()->get_default_theme()->get_font_list(base->get_class(), &sn);
for (const StringName &E : sn) {
suggestions.push_back(quoted(E));
@@ -231,7 +232,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
Node *base = _try_find_owner_node_in_tree(script);
if (base && Object::cast_to<Control>(base)) {
List<StringName> sn;
- Theme::get_default()->get_font_size_list(base->get_class(), &sn);
+ ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(base->get_class(), &sn);
for (const StringName &E : sn) {
suggestions.push_back(quoted(E));
@@ -243,7 +244,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
Node *base = _try_find_owner_node_in_tree(script);
if (base && Object::cast_to<Control>(base)) {
List<StringName> sn;
- Theme::get_default()->get_stylebox_list(base->get_class(), &sn);
+ ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(base->get_class(), &sn);
for (const StringName &E : sn) {
suggestions.push_back(quoted(E));
diff --git a/modules/mono/editor/code_completion.h b/modules/mono/editor/code_completion.h
index 82b592003b..89d19b10e0 100644
--- a/modules/mono/editor/code_completion.h
+++ b/modules/mono/editor/code_completion.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* code_completion.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* code_completion.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 CODE_COMPLETION_H
#define CODE_COMPLETION_H
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 8b1b44852f..ad9ab66cc9 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* editor_internal_calls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_internal_calls.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_internal_calls.h"
@@ -39,184 +39,88 @@
#include "core/version.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
#include "editor/plugins/script_editor_plugin.h"
#include "main/main.h"
#include "../csharp_script.h"
-#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/macos_utils.h"
#include "code_completion.h"
-#include "godotsharp_export.h"
-MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
-}
+#include "../interop_types.h"
-MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
-#else
- return nullptr;
+#ifdef __cplusplus
+extern "C" {
#endif
-}
-MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
-#else
- return nullptr;
-#endif
+void godot_icall_GodotSharpDirs_ResMetadataDir(godot_string *r_dest) {
+ memnew_placement(r_dest, String(GodotSharpDirs::get_res_metadata_dir()));
}
-MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
-#else
- return nullptr;
-#endif
+void godot_icall_GodotSharpDirs_MonoUserDir(godot_string *r_dest) {
+ memnew_placement(r_dest, String(GodotSharpDirs::get_mono_user_dir()));
}
-MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
+void godot_icall_GodotSharpDirs_BuildLogsDirs(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
+ memnew_placement(r_dest, String(GodotSharpDirs::get_build_logs_dir()));
#else
return nullptr;
#endif
}
-MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
+void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) {
#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
+ memnew_placement(r_dest, String(GodotSharpDirs::get_data_editor_tools_dir()));
#else
return nullptr;
#endif
}
-MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
-#ifdef TOOLS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
-#else
- return nullptr;
-#endif
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
-}
-
-MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
-#ifdef WINDOWS_ENABLED
- return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
-#else
- return nullptr;
-#endif
-}
-
-void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
- String label = GDMonoMarshal::mono_string_to_godot(p_label);
+void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
+ String task = *reinterpret_cast<const String *>(p_task);
+ String label = *reinterpret_cast<const String *>(p_label);
EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
}
-void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
+void godot_icall_EditorProgress_Dispose(const godot_string *p_task) {
+ String task = *reinterpret_cast<const String *>(p_task);
EditorNode::progress_end_task(task);
}
-MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
- String task = GDMonoMarshal::mono_string_to_godot(p_task);
- String state = GDMonoMarshal::mono_string_to_godot(p_state);
+bool godot_icall_EditorProgress_Step(const godot_string *p_task, const godot_string *p_state, int32_t p_step, bool p_force_refresh) {
+ String task = *reinterpret_cast<const String *>(p_task);
+ String state = *reinterpret_cast<const String *>(p_state);
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
-uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
- MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
- Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
- String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
- String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
- Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
-
- return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
-}
-
-MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
- String config = GDMonoMarshal::mono_string_to_godot(p_config);
- String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);
- return GDMonoMarshal::mono_string_from_godot(error_str);
+void godot_icall_Internal_FullExportTemplatesDir(godot_string *r_dest) {
+ String full_templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().path_join(VERSION_FULL_CONFIG);
+ memnew_placement(r_dest, String(full_templates_dir));
}
-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);
-}
-
-MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
- String path = GDMonoMarshal::mono_string_to_godot(p_path);
- return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
-}
-
-MonoBoolean godot_icall_Internal_IsMacOSAppBundleInstalled(MonoString *p_bundle_id) {
+bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle_id) {
#ifdef MACOS_ENABLED
- String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
- return (MonoBoolean)macos_is_app_bundle_installed(bundle_id);
+ String bundle_id = *reinterpret_cast<const String *>(p_bundle_id);
+ return (bool)macos_is_app_bundle_installed(bundle_id);
#else
(void)p_bundle_id; // UNUSED
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
-MonoBoolean godot_icall_Internal_GodotIs32Bits() {
+bool godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
-MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
+bool godot_icall_Internal_GodotIsRealTDouble() {
#ifdef REAL_T_IS_DOUBLE
- return (MonoBoolean) true;
+ return (bool)true;
#else
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
@@ -224,23 +128,15 @@ void godot_icall_Internal_GodotMainIteration() {
Main::iteration();
}
-uint64_t godot_icall_Internal_GetCoreApiHash() {
- return ClassDB::get_api_hash(ClassDB::API_CORE);
-}
-
-uint64_t godot_icall_Internal_GetEditorApiHash() {
- return ClassDB::get_api_hash(ClassDB::API_EDITOR);
-}
-
-MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
+bool godot_icall_Internal_IsAssembliesReloadingNeeded() {
#ifdef GD_MONO_HOT_RELOAD
- return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
+ return (bool)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
#else
- return (MonoBoolean) false;
+ return (bool)false;
#endif
}
-void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
+void godot_icall_Internal_ReloadAssemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
#endif
@@ -250,22 +146,13 @@ void godot_icall_Internal_EditorDebuggerNodeReloadScripts() {
EditorDebuggerNode::get_singleton()->reload_scripts();
}
-MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
- Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
- return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
+bool godot_icall_Internal_ScriptEditorEdit(Resource *p_resource, int32_t p_line, int32_t p_col, bool p_grab_focus) {
+ Ref<Resource> resource = p_resource;
+ return (bool)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
}
void godot_icall_Internal_EditorNodeShowScriptScreen() {
- EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
-}
-
-MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
-#ifdef WINDOWS_ENABLED
- String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
- return GDMonoMarshal::mono_string_from_godot(install_root_dir);
-#else
- return nullptr;
-#endif
+ EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT);
}
void godot_icall_Internal_EditorRunPlay() {
@@ -283,114 +170,93 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
}
}
-MonoArray *godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, MonoString *p_script_file) {
- String script_file = GDMonoMarshal::mono_string_to_godot(p_script_file);
+void godot_icall_Internal_CodeCompletionRequest(int32_t p_kind, const godot_string *p_script_file, godot_packed_array *r_ret) {
+ String script_file = *reinterpret_cast<const String *>(p_script_file);
PackedStringArray suggestions = gdmono::get_code_completion((gdmono::CompletionKind)p_kind, script_file);
- return GDMonoMarshal::PackedStringArray_to_mono_array(suggestions);
+ memnew_placement(r_ret, PackedStringArray(suggestions));
}
float godot_icall_Globals_EditorScale() {
return EDSCALE;
}
-MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+void godot_icall_Globals_GlobalDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_variant *p_default_value, bool p_restart_if_changed, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
+ Variant default_value = *reinterpret_cast<const Variant *>(p_default_value);
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) {
+ String setting = *reinterpret_cast<const String *>(p_setting);
Ref<Shortcut> result = ED_GET_SHORTCUT(setting);
- return GDMonoMarshal::variant_to_mono_object(result);
+ memnew_placement(r_result, Variant(result));
}
-MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
- String text = GDMonoMarshal::mono_string_to_godot(p_text);
- return GDMonoMarshal::mono_string_from_godot(TTR(text));
+void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) {
+ String text = *reinterpret_cast<const String *>(p_text);
+ memnew_placement(r_dest, String(TTR(text)));
}
-MonoString *godot_icall_Utils_OS_GetPlatformName() {
+void godot_icall_Utils_OS_GetPlatformName(godot_string *r_dest) {
String os_name = OS::get_singleton()->get_name();
- return GDMonoMarshal::mono_string_from_godot(os_name);
+ memnew_placement(r_dest, String(os_name));
}
-MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_path) {
+bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(const godot_string *p_file_path) {
#ifdef UNIX_ENABLED
- String file_path = GDMonoMarshal::mono_string_to_godot(p_file_path);
+ String file_path = *reinterpret_cast<const String *>(p_file_path);
return access(file_path.utf8().get_data(), X_OK) == 0;
#else
ERR_FAIL_V(false);
#endif
}
-void register_editor_internal_calls() {
- // GodotSharpDirs
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
- GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
-
- // EditorProgress
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose);
- GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step);
-
- // ExportPlugin
- GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
-
- // Internals
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- 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_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);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest);
-
- // Globals
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut);
- GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
-
- // Utils.OS
- GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName);
- GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess);
+#ifdef __cplusplus
+}
+#endif
+
+// The order in this array must match the declaration order of
+// the methods in 'GodotTools/Internals/Internal.cs'.
+static const void *unmanaged_callbacks[]{
+ (void *)godot_icall_GodotSharpDirs_ResMetadataDir,
+ (void *)godot_icall_GodotSharpDirs_MonoUserDir,
+ (void *)godot_icall_GodotSharpDirs_BuildLogsDirs,
+ (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir,
+ (void *)godot_icall_EditorProgress_Create,
+ (void *)godot_icall_EditorProgress_Dispose,
+ (void *)godot_icall_EditorProgress_Step,
+ (void *)godot_icall_Internal_FullExportTemplatesDir,
+ (void *)godot_icall_Internal_IsMacOSAppBundleInstalled,
+ (void *)godot_icall_Internal_GodotIs32Bits,
+ (void *)godot_icall_Internal_GodotIsRealTDouble,
+ (void *)godot_icall_Internal_GodotMainIteration,
+ (void *)godot_icall_Internal_IsAssembliesReloadingNeeded,
+ (void *)godot_icall_Internal_ReloadAssemblies,
+ (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts,
+ (void *)godot_icall_Internal_ScriptEditorEdit,
+ (void *)godot_icall_Internal_EditorNodeShowScriptScreen,
+ (void *)godot_icall_Internal_EditorRunPlay,
+ (void *)godot_icall_Internal_EditorRunStop,
+ (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts,
+ (void *)godot_icall_Internal_CodeCompletionRequest,
+ (void *)godot_icall_Globals_EditorScale,
+ (void *)godot_icall_Globals_GlobalDef,
+ (void *)godot_icall_Globals_EditorDef,
+ (void *)godot_icall_Globals_EditorShortcut,
+ (void *)godot_icall_Globals_TTR,
+ (void *)godot_icall_Utils_OS_GetPlatformName,
+ (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess,
+};
+
+const void **godotsharp::get_editor_interop_funcs(int32_t &r_size) {
+ r_size = sizeof(unmanaged_callbacks);
+ return unmanaged_callbacks;
}
diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h
index 8262ac211a..48da02987a 100644
--- a/modules/mono/editor/editor_internal_calls.h
+++ b/modules/mono/editor/editor_internal_calls.h
@@ -1,36 +1,40 @@
-/*************************************************************************/
-/* editor_internal_calls.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_internal_calls.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_INTERNAL_CALLS_H
#define EDITOR_INTERNAL_CALLS_H
-void register_editor_internal_calls();
+#include "core/typedefs.h"
+
+namespace godotsharp {
+const void **get_editor_interop_funcs(int32_t &r_size);
+}
#endif // EDITOR_INTERNAL_CALLS_H
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
deleted file mode 100644
index f9ea403334..0000000000
--- a/modules/mono/editor/godotsharp_export.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*************************************************************************/
-/* godotsharp_export.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 "godotsharp_export.h"
-
-#include <mono/metadata/image.h>
-
-#include "core/config/project_settings.h"
-#include "core/io/file_access_pack.h"
-#include "core/os/os.h"
-
-#include "../mono_gd/gd_mono.h"
-#include "../mono_gd/gd_mono_assembly.h"
-#include "../mono_gd/gd_mono_cache.h"
-#include "../utils/macros.h"
-
-namespace GodotSharpExport {
-
-MonoAssemblyName *new_mono_assembly_name() {
- // Mono has no public API to create an empty MonoAssemblyName and the struct is private.
- // As such the only way to create it is with a stub name and then clear it.
-
- MonoAssemblyName *aname = mono_assembly_name_new("stub");
- CRASH_COND(aname == nullptr);
- mono_assembly_name_free(aname); // Frees the string fields, not the struct
- return aname;
-}
-
-struct AssemblyRefInfo {
- String name;
- uint16_t major = 0;
- uint16_t minor = 0;
- uint16_t build = 0;
- uint16_t revision = 0;
-};
-
-AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
- const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
-
- uint32_t cols[MONO_ASSEMBLYREF_SIZE];
-
- mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
-
- return {
- String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])),
- (uint16_t)cols[MONO_ASSEMBLYREF_MAJOR_VERSION],
- (uint16_t)cols[MONO_ASSEMBLYREF_MINOR_VERSION],
- (uint16_t)cols[MONO_ASSEMBLYREF_BUILD_NUMBER],
- (uint16_t)cols[MONO_ASSEMBLYREF_REV_NUMBER]
- };
-}
-
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *reusable_aname, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
- MonoImage *image = p_assembly->get_image();
-
- for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
- AssemblyRefInfo ref_info = get_assemblyref_name(image, i);
-
- const String &ref_name = ref_info.name;
-
- if (r_assembly_dependencies.has(ref_name)) {
- continue;
- }
-
- mono_assembly_get_assemblyref(image, i, reusable_aname);
-
- GDMonoAssembly *ref_assembly = nullptr;
- if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
- ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
- }
-
- r_assembly_dependencies[ref_name] = ref_assembly->get_path();
-
- Error err = get_assembly_dependencies(ref_assembly, reusable_aname, p_search_dirs, r_assembly_dependencies);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
- }
-
- return OK;
-}
-
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
- const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) {
- MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
- ERR_FAIL_NULL_V(export_domain, FAILED);
- _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
-
- _GDMONO_SCOPE_DOMAIN_(export_domain);
-
- Vector<String> search_dirs;
- GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
-
- if (p_custom_bcl_dir.length()) {
- // Only one mscorlib can be loaded. We need this workaround to make sure we get it from the right BCL directory.
- r_assembly_dependencies["mscorlib"] = p_custom_bcl_dir.plus_file("mscorlib.dll").simplify_path();
- }
-
- for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
- String assembly_name = *key;
- String assembly_path = p_initial_assemblies[*key];
-
- GDMonoAssembly *assembly = nullptr;
- bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
-
- ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
-
- MonoAssemblyName *reusable_aname = new_mono_assembly_name();
- SCOPE_EXIT { mono_free(reusable_aname); };
-
- Error err = get_assembly_dependencies(assembly, reusable_aname, search_dirs, r_assembly_dependencies);
- if (err != OK) {
- return err;
- }
- }
-
- return OK;
-}
-} // namespace GodotSharpExport
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
deleted file mode 100644
index 60620b5f4d..0000000000
--- a/modules/mono/editor/godotsharp_export.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*************************************************************************/
-/* godotsharp_export.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 GODOTSHARP_EXPORT_H
-#define GODOTSHARP_EXPORT_H
-
-#include "core/error/error_list.h"
-#include "core/string/ustring.h"
-#include "core/variant/dictionary.h"
-
-#include "../mono_gd/gd_mono_header.h"
-
-namespace GodotSharpExport {
-
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
-
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
- const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
-} // namespace GodotSharpExport
-
-#endif // GODOTSHARP_EXPORT_H
diff --git a/modules/mono/editor/hostfxr_resolver.cpp b/modules/mono/editor/hostfxr_resolver.cpp
new file mode 100644
index 0000000000..786272b28c
--- /dev/null
+++ b/modules/mono/editor/hostfxr_resolver.cpp
@@ -0,0 +1,335 @@
+/**************************************************************************/
+/* hostfxr_resolver.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+/*
+Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost
+*/
+
+/*
+The MIT License (MIT)
+
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+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 "hostfxr_resolver.h"
+
+#include "core/config/engine.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/os/os.h"
+
+#ifdef WINDOWS_ENABLED
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "../utils/path_utils.h"
+#include "semver.h"
+
+// We don't use libnethost as it gives us issues with some compilers.
+// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the
+// same function names for easier comparing in case we need to update this in the future.
+
+namespace {
+
+String get_hostfxr_file_name() {
+#if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED)
+ return "hostfxr.dll";
+#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
+ return "libhostfxr.dylib";
+#else
+ return "libhostfxr.so";
+#endif
+}
+
+bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) {
+ godotsharp::SemVerParser sem_ver_parser;
+
+ bool found_ver = false;
+ godotsharp::SemVer latest_ver;
+ String latest_ver_str;
+
+ Ref<DirAccess> da = DirAccess::open(fxr_root);
+ da->list_dir_begin();
+ for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) {
+ if (!da->current_is_dir() || dir == "." || dir == "..") {
+ continue;
+ }
+
+ String ver = dir.get_file();
+
+ godotsharp::SemVer fx_ver;
+ if (sem_ver_parser.parse(ver, fx_ver)) {
+ if (!found_ver || fx_ver > latest_ver) {
+ latest_ver = fx_ver;
+ latest_ver_str = ver;
+ found_ver = true;
+ }
+ }
+ }
+
+ if (!found_ver) {
+ return false;
+ }
+
+ String fxr_with_ver = path::join(fxr_root, latest_ver_str);
+ String hostfxr_file_path = path::join(fxr_with_ver, get_hostfxr_file_name());
+
+ ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver);
+
+ r_fxr_path = hostfxr_file_path;
+
+ return true;
+}
+
+#ifdef WINDOWS_ENABLED
+typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
+
+BOOL is_wow64() {
+ BOOL wow64 = FALSE;
+
+ LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
+
+ if (fnIsWow64Process) {
+ if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) {
+ wow64 = FALSE;
+ }
+ }
+
+ return wow64;
+}
+#endif
+
+static const char *arch_name_map[][2] = {
+ { "arm32", "arm" },
+ { "arm64", "arm64" },
+ { "rv64", "riscv64" },
+ { "x86_64", "x64" },
+ { "x86_32", "x86" },
+ { nullptr, nullptr }
+};
+
+String get_dotnet_arch() {
+ String arch = Engine::get_singleton()->get_architecture_name();
+
+ int idx = 0;
+ while (arch_name_map[idx][0] != nullptr) {
+ if (arch_name_map[idx][0] == arch) {
+ return arch_name_map[idx][1];
+ }
+ idx++;
+ }
+
+ return "";
+}
+
+bool get_default_installation_dir(String &r_dotnet_root) {
+#if defined(WINDOWS_ENABLED)
+ String program_files_env;
+ if (is_wow64()) {
+ // Running x86 on x64, looking for x86 install
+ program_files_env = "ProgramFiles(x86)";
+ } else {
+ program_files_env = "ProgramFiles";
+ }
+
+ String program_files_dir = OS::get_singleton()->get_environment(program_files_env);
+
+ if (program_files_dir.is_empty()) {
+ return false;
+ }
+
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
+ // When emulating x64 on arm
+ String dotnet_root_emulated = path::join(program_files_dir, "dotnet", "x64");
+ if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet.exe"))) {
+ r_dotnet_root = dotnet_root_emulated;
+ return true;
+ }
+#endif
+
+ r_dotnet_root = path::join(program_files_dir, "dotnet");
+ return true;
+#elif defined(MACOS_ENABLED)
+ r_dotnet_root = "/usr/local/share/dotnet";
+
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
+ // When emulating x64 on arm
+ String dotnet_root_emulated = path::join(r_dotnet_root, "x64");
+ if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet"))) {
+ r_dotnet_root = dotnet_root_emulated;
+ return true;
+ }
+#endif
+
+ return true;
+#else
+ r_dotnet_root = "/usr/share/dotnet";
+ return true;
+#endif
+}
+
+bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) {
+ Error err = OK;
+ Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err);
+
+ if (f.is_null() || err != OK) {
+ return false;
+ }
+
+ String line = f->get_line();
+
+ if (line.is_empty()) {
+ return false;
+ }
+
+ r_dotnet_root = line;
+ return true;
+}
+
+bool get_dotnet_self_registered_dir(String &r_dotnet_root) {
+#if defined(WINDOWS_ENABLED)
+ String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();
+ Char16String value = String("InstallLocation").utf16();
+
+ HKEY hkey = NULL;
+ LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);
+ if (result != ERROR_SUCCESS) {
+ return false;
+ }
+
+ DWORD size = 0;
+ result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size);
+ if (result != ERROR_SUCCESS || size == 0) {
+ RegCloseKey(hkey);
+ return false;
+ }
+
+ Vector<WCHAR> buffer;
+ buffer.resize(size / sizeof(WCHAR));
+ result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size);
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(hkey);
+ return false;
+ }
+
+ r_dotnet_root = String::utf16((const char16_t *)buffer.ptr());
+ RegCloseKey(hkey);
+ return true;
+#else
+ String install_location_file = path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower());
+ if (get_install_location_from_file(install_location_file, r_dotnet_root)) {
+ return true;
+ }
+
+ if (FileAccess::exists(install_location_file)) {
+ // Don't try with the legacy location, this will fall back to the hard-coded default install location
+ return false;
+ }
+
+ String legacy_install_location_file = path::join("/etc/dotnet", "install_location");
+ return get_install_location_from_file(legacy_install_location_file, r_dotnet_root);
+#endif
+}
+
+bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) {
+ String env_value = OS::get_singleton()->get_environment(p_env_key);
+
+ if (!env_value.is_empty()) {
+ env_value = path::realpath(env_value);
+
+ if (DirAccess::exists(env_value)) {
+ r_dotnet_root = env_value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool get_dotnet_root_from_env(String &r_dotnet_root) {
+ String dotnet_root_env = "DOTNET_ROOT";
+ String arch_for_env = get_dotnet_arch();
+
+ if (!arch_for_env.is_empty()) {
+ // DOTNET_ROOT_<arch>
+ if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) {
+ return true;
+ }
+ }
+
+#ifdef WINDOWS_ENABLED
+ // WoW64-only: DOTNET_ROOT(x86)
+ if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) {
+ return true;
+ }
+#endif
+
+ // DOTNET_ROOT
+ return get_file_path_from_env(dotnet_root_env, r_dotnet_root);
+}
+
+} //namespace
+
+bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {
+ String fxr_dir = path::join(p_dotnet_root, "host", "fxr");
+ ERR_FAIL_COND_V_MSG(!DirAccess::exists(fxr_dir), false, "The host fxr folder does not exist: " + fxr_dir);
+ return get_latest_fxr(fxr_dir, r_fxr_path);
+}
+
+bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {
+ if (!get_dotnet_root_from_env(r_dotnet_root) &&
+ !get_dotnet_self_registered_dir(r_dotnet_root) &&
+ !get_default_installation_dir(r_dotnet_root)) {
+ return false;
+ }
+
+ return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path);
+}
diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/editor/hostfxr_resolver.h
index 5be60d4930..398a58c730 100644
--- a/modules/mono/utils/mono_reg_utils.h
+++ b/modules/mono/editor/hostfxr_resolver.h
@@ -1,54 +1,45 @@
-/*************************************************************************/
-/* mono_reg_utils.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 MONO_REG_UTILS_H
-#define MONO_REG_UTILS_H
-
-#ifdef WINDOWS_ENABLED
+/**************************************************************************/
+/* hostfxr_resolver.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 HOSTFXR_RESOLVER_H
+#define HOSTFXR_RESOLVER_H
#include "core/string/ustring.h"
-struct MonoRegInfo {
- String version;
- String install_root_dir;
- String assembly_dir;
- String config_dir;
- String bin_dir;
-};
-
-namespace MonoRegUtils {
+namespace godotsharp {
+namespace hostfxr_resolver {
-MonoRegInfo find_mono();
-String find_msbuild_tools_path();
-} // namespace MonoRegUtils
+bool try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_out_fxr_path);
+bool try_get_path(String &r_out_dotnet_root, String &r_out_fxr_path);
-#endif // WINDOWS_ENABLED
+} //namespace hostfxr_resolver
+} //namespace godotsharp
-#endif // MONO_REG_UTILS_H
+#endif // HOSTFXR_RESOLVER_H
diff --git a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
index 2ca81ab7cd..fbad482cf6 100644
--- a/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
+++ b/modules/mono/editor/script_templates/CharacterBody2D/basic_movement.cs
@@ -8,16 +8,16 @@ public partial class _CLASS_ : _BASE_
public const float Speed = 300.0f;
public const float JumpVelocity = -400.0f;
- // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
- public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
+ // Get the gravity from the project settings to be synced with RigidBody nodes.
+ public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
- public override void _PhysicsProcess(float delta)
+ public override void _PhysicsProcess(double delta)
{
Vector2 velocity = Velocity;
// Add the gravity.
if (!IsOnFloor())
- velocity.y += gravity * delta;
+ velocity.y += gravity * (float)delta;
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
diff --git a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
index a6935fe497..abed246a1e 100644
--- a/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
+++ b/modules/mono/editor/script_templates/CharacterBody3D/basic_movement.cs
@@ -8,25 +8,25 @@ public partial class _CLASS_ : _BASE_
public const float Speed = 5.0f;
public const float JumpVelocity = 4.5f;
- // Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
- public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
+ // Get the gravity from the project settings to be synced with RigidBody nodes.
+ public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();
- public override void _PhysicsProcess(float delta)
+ public override void _PhysicsProcess(double delta)
{
Vector3 velocity = Velocity;
// Add the gravity.
if (!IsOnFloor())
- velocity.y -= gravity * delta;
+ velocity.y -= gravity * (float)delta;
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
- velocity.y = JumpVelocity;
+ velocity.y = JumpVelocity;
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
- Vector3 direction = Transform.basis.Xform(new Vector3(inputDir.x, 0, inputDir.y)).Normalized();
+ Vector3 direction = (Transform.basis * new Vector3(inputDir.x, 0, inputDir.y)).Normalized();
if (direction != Vector3.Zero)
{
velocity.x = direction.x * Speed;
diff --git a/modules/mono/editor/script_templates/Node/default.cs b/modules/mono/editor/script_templates/Node/default.cs
index 4c86d1666f..74ece028fc 100644
--- a/modules/mono/editor/script_templates/Node/default.cs
+++ b/modules/mono/editor/script_templates/Node/default.cs
@@ -11,7 +11,7 @@ public partial class _CLASS_ : _BASE_
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
- public override void _Process(float delta)
+ public override void _Process(double delta)
{
}
}
diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
index a1b93e7daa..cd335934db 100644
--- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
@@ -20,42 +20,42 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return "";
}
- public override int _GetReturnIconType()
+ public override long _GetReturnIconType()
{
return 0;
}
- public override int _GetInputPortCount()
+ public override long _GetInputPortCount()
{
return 0;
}
- public override string _GetInputPortName(int port)
+ public override string _GetInputPortName(long port)
{
return "";
}
- public override int _GetInputPortType(int port)
+ public override long _GetInputPortType(long port)
{
return 0;
}
- public override int _GetOutputPortCount()
+ public override long _GetOutputPortCount()
{
return 1;
}
- public override string _GetOutputPortName(int port)
+ public override string _GetOutputPortName(long port)
{
return "result";
}
- public override int _GetOutputPortType(int port)
+ public override long _GetOutputPortType(long port)
{
return 0;
}
- public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type)
+ public override string _GetCode(Godot.Collections.Array<string> inputVars, Godot.Collections.Array<string> outputVars, Shader.Mode mode, VisualShader.Type type)
{
return "";
}
diff --git a/modules/mono/editor/semver.cpp b/modules/mono/editor/semver.cpp
new file mode 100644
index 0000000000..1ca3005420
--- /dev/null
+++ b/modules/mono/editor/semver.cpp
@@ -0,0 +1,149 @@
+/**************************************************************************/
+/* semver.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "semver.h"
+
+bool godotsharp::SemVer::parse_digit_only_field(const String &p_field, uint64_t &r_result) {
+ if (p_field.is_empty()) {
+ return false;
+ }
+
+ int64_t integer = 0;
+
+ for (int i = 0; i < p_field.length(); i++) {
+ char32_t c = p_field[i];
+ if (is_digit(c)) {
+ bool overflow = ((uint64_t)integer > UINT64_MAX / 10) || ((uint64_t)integer == UINT64_MAX / 10 && c > '5');
+ ERR_FAIL_COND_V_MSG(overflow, false, "Cannot represent '" + p_field + "' as a 64-bit unsigned integer, since the value is too large.");
+ integer *= 10;
+ integer += c - '0';
+ } else {
+ return false;
+ }
+ }
+
+ r_result = (uint64_t)integer;
+ return true;
+}
+
+int godotsharp::SemVer::cmp(const godotsharp::SemVer &p_a, const godotsharp::SemVer &p_b) {
+ if (p_a.major != p_b.major) {
+ return p_a.major > p_b.major ? 1 : -1;
+ }
+
+ if (p_a.minor != p_b.minor) {
+ return p_a.minor > p_b.minor ? 1 : -1;
+ }
+
+ if (p_a.patch != p_b.patch) {
+ return p_a.patch > p_b.patch ? 1 : -1;
+ }
+
+ if (p_a.prerelease.is_empty() && p_b.prerelease.is_empty()) {
+ return 0;
+ }
+
+ if (p_a.prerelease.is_empty() || p_b.prerelease.is_empty()) {
+ return p_a.prerelease.is_empty() ? 1 : -1;
+ }
+
+ if (p_a.prerelease != p_b.prerelease) {
+ // This could be optimized, but I'm too lazy
+
+ Vector<String> a_field_set = p_a.prerelease.split(".");
+ Vector<String> b_field_set = p_b.prerelease.split(".");
+
+ int a_field_count = a_field_set.size();
+ int b_field_count = b_field_set.size();
+
+ int min_field_count = MIN(a_field_count, b_field_count);
+
+ for (int i = 0; i < min_field_count; i++) {
+ const String &a_field = a_field_set[i];
+ const String &b_field = b_field_set[i];
+
+ if (a_field == b_field) {
+ continue;
+ }
+
+ uint64_t a_num;
+ bool a_is_digit_only = parse_digit_only_field(a_field, a_num);
+
+ uint64_t b_num;
+ bool b_is_digit_only = parse_digit_only_field(b_field, b_num);
+
+ if (a_is_digit_only && b_is_digit_only) {
+ // Identifiers consisting of only digits are compared numerically.
+
+ if (a_num == b_num) {
+ continue;
+ }
+
+ return a_num > b_num ? 1 : -1;
+ }
+
+ if (a_is_digit_only || b_is_digit_only) {
+ // Numeric identifiers always have lower precedence than non-numeric identifiers.
+ return b_is_digit_only ? 1 : -1;
+ }
+
+ // Identifiers with letters or hyphens are compared lexically in ASCII sort order.
+ return a_field > b_field ? 1 : -1;
+ }
+
+ if (a_field_count != b_field_count) {
+ // A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.
+ return a_field_count > b_field_count ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+bool godotsharp::SemVerParser::parse(const String &p_ver_text, godotsharp::SemVer &r_semver) {
+ if (!regex.is_valid() && regex.get_pattern().is_empty()) {
+ regex.compile("^(?P<major>0|[1-9]\\d*)\\.(?P<minor>0|[1-9]\\d*)\\.(?P<patch>0|[1-9]\\d*)(?:-(?P<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$");
+ ERR_FAIL_COND_V(!regex.is_valid(), false);
+ }
+
+ Ref<RegExMatch> match = regex.search(p_ver_text);
+
+ if (match.is_valid()) {
+ r_semver = SemVer(
+ match->get_string("major").to_int(),
+ match->get_string("minor").to_int(),
+ match->get_string("patch").to_int(),
+ match->get_string("prerelease"),
+ match->get_string("buildmetadata"));
+ return true;
+ }
+
+ return false;
+}
diff --git a/modules/mono/editor/semver.h b/modules/mono/editor/semver.h
new file mode 100644
index 0000000000..2f65e99ef5
--- /dev/null
+++ b/modules/mono/editor/semver.h
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* semver.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 SEMVER_H
+#define SEMVER_H
+
+#include "core/string/ustring.h"
+#include "modules/regex/regex.h"
+
+// <sys/sysmacros.h> is included somewhere, which defines major(dev) to gnu_dev_major(dev)
+#if defined(major)
+#undef major
+#endif
+#if defined(minor)
+#undef minor
+#endif
+
+namespace godotsharp {
+
+struct SemVer {
+private:
+ static bool parse_digit_only_field(const String &p_field, uint64_t &r_result);
+
+ static int cmp(const SemVer &p_a, const SemVer &p_b);
+
+public:
+ int major = 0;
+ int minor = 0;
+ int patch = 0;
+ String prerelease;
+ String build_metadata;
+
+ bool operator==(const SemVer &b) const {
+ return cmp(*this, b) == 0;
+ }
+
+ bool operator!=(const SemVer &b) const {
+ return !operator==(b);
+ }
+
+ bool operator<(const SemVer &b) const {
+ return cmp(*this, b) < 0;
+ }
+
+ bool operator>(const SemVer &b) const {
+ return cmp(*this, b) > 0;
+ }
+
+ bool operator<=(const SemVer &b) const {
+ return cmp(*this, b) <= 0;
+ }
+
+ bool operator>=(const SemVer &b) const {
+ return cmp(*this, b) >= 0;
+ }
+
+ SemVer() {}
+
+ SemVer(int p_major, int p_minor, int p_patch,
+ const String &p_prerelease, const String &p_build_metadata) :
+ major(p_major),
+ minor(p_minor),
+ patch(p_patch),
+ prerelease(p_prerelease),
+ build_metadata(p_build_metadata) {
+ }
+};
+
+struct SemVerParser {
+private:
+ RegEx regex;
+
+public:
+ bool parse(const String &p_ver_text, SemVer &r_semver);
+};
+
+} //namespace godotsharp
+
+#endif // SEMVER_H
diff --git a/modules/mono/glue/GodotSharp/.editorconfig b/modules/mono/glue/GodotSharp/.editorconfig
new file mode 100644
index 0000000000..d4e71b1bd9
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/.editorconfig
@@ -0,0 +1,8 @@
+[**/Generated/**.cs]
+# Validate parameter is non-null before using it
+# Useful for generated code, as it disables nullable
+dotnet_diagnostic.CA1062.severity = error
+# CA1069: Enums should not have duplicate values
+dotnet_diagnostic.CA1069.severity = none
+# CA1708: Identifiers should differ by more than case
+dotnet_diagnostic.CA1708.severity = none
diff --git a/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
new file mode 100644
index 0000000000..2dc350d4f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/ExternalAnnotations/System.Runtime.InteropServices.xml
@@ -0,0 +1,5 @@
+<assembly name="System.Runtime.InteropServices">
+ <member name="T:System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute">
+ <attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor" />
+ </member>
+</assembly>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
new file mode 100644
index 0000000000..ae51c07386
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/CallbacksInfo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal readonly struct CallbacksData
+{
+ public CallbacksData(INamedTypeSymbol nativeTypeSymbol, INamedTypeSymbol funcStructSymbol)
+ {
+ NativeTypeSymbol = nativeTypeSymbol;
+ FuncStructSymbol = funcStructSymbol;
+ Methods = NativeTypeSymbol.GetMembers()
+ .Where(symbol => symbol is IMethodSymbol { IsPartialDefinition: true })
+ .Cast<IMethodSymbol>()
+ .ToImmutableArray();
+ }
+
+ public INamedTypeSymbol NativeTypeSymbol { get; }
+
+ public INamedTypeSymbol FuncStructSymbol { get; }
+
+ public ImmutableArray<IMethodSymbol> Methods { get; }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
new file mode 100644
index 0000000000..d3726d69f0
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Common.cs
@@ -0,0 +1,65 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class Common
+{
+ public static void ReportNonPartialUnmanagedCallbacksClass(
+ GeneratorExecutionContext context,
+ ClassDeclarationSyntax cds, INamedTypeSymbol symbol
+ )
+ {
+ string message =
+ "Missing partial modifier on declaration of type '" +
+ $"{symbol.FullQualifiedNameOmitGlobal()}' which has attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute '{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' " +
+ "must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0001",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ cds.GetLocation(),
+ cds.SyntaxTree.FilePath));
+ }
+
+ public static void ReportNonPartialUnmanagedCallbacksOuterClass(
+ GeneratorExecutionContext context,
+ TypeDeclarationSyntax outerTypeDeclSyntax
+ )
+ {
+ var outerSymbol = context.Compilation
+ .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
+ .GetDeclaredSymbol(outerTypeDeclSyntax);
+
+ string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
+ namedTypeSymbol.FullQualifiedNameOmitGlobal() :
+ "type not found";
+
+ string message =
+ $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
+ $"which contains one or more subclasses with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}'";
+
+ string description = $"{message}. Classes with attribute " +
+ $"'{GeneratorClasses.GenerateUnmanagedCallbacksAttr}' and their " +
+ "containing types must be declared with the partial modifier.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-INTERNAL-G0002",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ outerTypeDeclSyntax.GetLocation(),
+ outerTypeDeclSyntax.SyntaxTree.FilePath));
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
new file mode 100644
index 0000000000..37f7005d01
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
@@ -0,0 +1,129 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+internal static class ExtensionMethods
+{
+ public static AttributeData? GetGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false);
+
+ private static bool HasGenerateUnmanagedCallbacksAttribute(
+ this ClassDeclarationSyntax cds, Compilation compilation,
+ out INamedTypeSymbol? symbol
+ )
+ {
+ var sm = compilation.GetSemanticModel(cds.SyntaxTree);
+
+ var classTypeSymbol = sm.GetDeclaredSymbol(cds);
+ if (classTypeSymbol == null)
+ {
+ symbol = null;
+ return false;
+ }
+
+ if (!classTypeSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false))
+ {
+ symbol = null;
+ return false;
+ }
+
+ symbol = classTypeSymbol;
+ return true;
+ }
+
+ private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
+ => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
+
+ public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
+ this IEnumerable<ClassDeclarationSyntax> source,
+ Compilation compilation
+ )
+ {
+ foreach (var cds in source)
+ {
+ if (cds.HasGenerateUnmanagedCallbacksAttribute(compilation, out var symbol))
+ yield return (cds, symbol!);
+ }
+ }
+
+ public static bool IsNested(this TypeDeclarationSyntax cds)
+ => cds.Parent is TypeDeclarationSyntax;
+
+ public static bool IsPartial(this TypeDeclarationSyntax cds)
+ => cds.Modifiers.Any(SyntaxKind.PartialKeyword);
+
+ public static bool AreAllOuterTypesPartial(
+ this TypeDeclarationSyntax cds,
+ out TypeDeclarationSyntax? typeMissingPartial
+ )
+ {
+ SyntaxNode? outerSyntaxNode = cds.Parent;
+
+ while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax)
+ {
+ if (!outerTypeDeclSyntax.IsPartial())
+ {
+ typeMissingPartial = outerTypeDeclSyntax;
+ return false;
+ }
+
+ outerSyntaxNode = outerSyntaxNode.Parent;
+ }
+
+ typeMissingPartial = null;
+ return true;
+ }
+
+ public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ string? keyword = namedTypeSymbol.DeclaringSyntaxReferences
+ .OfType<TypeDeclarationSyntax>().FirstOrDefault()?
+ .Keyword.Text;
+
+ return keyword ?? namedTypeSymbol.TypeKind switch
+ {
+ TypeKind.Interface => "interface",
+ TypeKind.Struct => "struct",
+ _ => "class"
+ };
+ }
+
+ public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
+ {
+ return symbol.IsGenericType ?
+ string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
+ symbol.Name;
+ }
+
+ private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+ private static SymbolDisplayFormat FullyQualifiedFormatIncludeGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Included);
+
+ public static string FullQualifiedNameOmitGlobal(this ITypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
+
+ public static string FullQualifiedNameOmitGlobal(this INamespaceSymbol namespaceSymbol)
+ => namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+
+ public static string FullQualifiedNameIncludeGlobal(this ITypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatIncludeGlobal);
+
+ public static string FullQualifiedNameIncludeGlobal(this INamespaceSymbol namespaceSymbol)
+ => namespaceSymbol.ToDisplayString(FullyQualifiedFormatIncludeGlobal);
+
+ public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
+ => qualifiedName
+ // AddSource() doesn't support angle brackets
+ .Replace("<", "(Of ")
+ .Replace(">", ")");
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
new file mode 100644
index 0000000000..1bbb33f5a1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/GeneratorClasses.cs
@@ -0,0 +1,6 @@
+namespace Godot.SourceGenerators.Internal;
+
+internal static class GeneratorClasses
+{
+ public const string GenerateUnmanagedCallbacksAttr = "Godot.SourceGenerators.Internal.GenerateUnmanagedCallbacksAttribute";
+}
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
new file mode 100644
index 0000000000..4d1a5bb76c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/Godot.SourceGenerators.Internal.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.10.0" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
new file mode 100644
index 0000000000..3226ca79e5
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs
@@ -0,0 +1,463 @@
+using System.Text;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators.Internal;
+
+[Generator]
+public class UnmanagedCallbacksGenerator : ISourceGenerator
+{
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForPostInitialization(ctx => { GenerateAttribute(ctx); });
+ }
+
+ public void Execute(GeneratorExecutionContext context)
+ {
+ INamedTypeSymbol[] unmanagedCallbacksClasses = context
+ .Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ .SelectUnmanagedCallbacksClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial())
+ {
+ if (x.cds.IsNested() && !x.cds.AreAllOuterTypesPartial(out var typeMissingPartial))
+ {
+ Common.ReportNonPartialUnmanagedCallbacksOuterClass(context, typeMissingPartial!);
+ return false;
+ }
+
+ return true;
+ }
+
+ Common.ReportNonPartialUnmanagedCallbacksClass(context, x.cds, x.symbol);
+ return false;
+ })
+ .Select(x => x.symbol)
+ )
+ .Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
+ .ToArray();
+
+ foreach (var symbol in unmanagedCallbacksClasses)
+ {
+ var attr = symbol.GetGenerateUnmanagedCallbacksAttribute();
+ if (attr == null || attr.ConstructorArguments.Length != 1)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var funcStructType = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value;
+ if (funcStructType == null)
+ {
+ // TODO: Report error or throw exception, this is an invalid case and should never be reached
+ System.Diagnostics.Debug.Fail("FAILED!");
+ continue;
+ }
+
+ var data = new CallbacksData(symbol, funcStructType);
+ GenerateInteropMethodImplementations(context, data);
+ GenerateUnmanagedCallbacksStruct(context, data);
+ }
+ }
+
+ private void GenerateAttribute(GeneratorPostInitializationContext context)
+ {
+ string source = @"using System;
+
+namespace Godot.SourceGenerators.Internal
+{
+internal class GenerateUnmanagedCallbacksAttribute : Attribute
+{
+ public Type FuncStructType { get; }
+
+ public GenerateUnmanagedCallbacksAttribute(Type funcStructType)
+ {
+ FuncStructType = funcStructType;
+ }
+}
+}";
+
+ context.AddSource("GenerateUnmanagedCallbacksAttribute.generated",
+ SourceText.From(source, Encoding.UTF8));
+ }
+
+ private void GenerateInteropMethodImplementations(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.NativeTypeSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+ var methodSource = new StringBuilder();
+ var methodCallArguments = new StringBuilder();
+ var methodSourceAfterCall = new StringBuilder();
+
+ source.Append(
+ @"using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ source.Append($"unsafe partial class {symbol.Name}\n");
+ source.Append("{\n");
+ source.Append($" private static {data.FuncStructSymbol.FullQualifiedNameIncludeGlobal()} _unmanagedCallbacks;\n\n");
+
+ foreach (var callback in data.Methods)
+ {
+ methodSource.Clear();
+ methodCallArguments.Clear();
+ methodSourceAfterCall.Clear();
+
+ source.Append(" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]\n");
+ source.Append($" {SyntaxFacts.GetText(callback.DeclaredAccessibility)} ");
+
+ if (callback.IsStatic)
+ source.Append("static ");
+
+ source.Append("partial ");
+ source.Append(callback.ReturnType.FullQualifiedNameIncludeGlobal());
+ source.Append(' ');
+ source.Append(callback.Name);
+ source.Append('(');
+
+ for (int i = 0; i < callback.Parameters.Length; i++)
+ {
+ var parameter = callback.Parameters[i];
+
+ source.Append(parameter.ToDisplayString());
+ source.Append(' ');
+ source.Append(parameter.Name);
+
+ if (parameter.RefKind == RefKind.Out)
+ {
+ // Only assign default if the parameter won't be passed by-ref or copied later.
+ if (IsGodotInteropStruct(parameter.Type))
+ methodSource.Append($" {parameter.Name} = default;\n");
+ }
+
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type))
+ {
+ methodSource.Append(" ");
+ AppendCustomUnsafeAsPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append(varName);
+ }
+ else if (parameter.Type.IsValueType)
+ {
+ methodSource.Append(" ");
+ AppendCopyToStackAndGetPointer(methodSource, parameter, out string varName);
+ methodCallArguments.Append($"&{varName}");
+
+ if (parameter.RefKind is RefKind.Out or RefKind.Ref)
+ {
+ methodSourceAfterCall.Append($" {parameter.Name} = {varName};\n");
+ }
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(methodCallArguments, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Name);
+ }
+ }
+ else
+ {
+ methodCallArguments.Append(parameter.Name);
+ }
+
+ if (i < callback.Parameters.Length - 1)
+ {
+ source.Append(", ");
+ methodCallArguments.Append(", ");
+ }
+ }
+
+ source.Append(")\n");
+ source.Append(" {\n");
+
+ source.Append(methodSource);
+ source.Append(" ");
+
+ if (!callback.ReturnsVoid)
+ {
+ if (methodSourceAfterCall.Length != 0)
+ source.Append($"{callback.ReturnType.FullQualifiedNameIncludeGlobal()} ret = ");
+ else
+ source.Append("return ");
+ }
+
+ source.Append($"_unmanagedCallbacks.{callback.Name}(");
+ source.Append(methodCallArguments);
+ source.Append(");\n");
+
+ if (methodSourceAfterCall.Length != 0)
+ {
+ source.Append(methodSourceAfterCall);
+
+ if (!callback.ReturnsVoid)
+ source.Append(" return ret;\n");
+ }
+
+ source.Append(" }\n\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("\n}");
+
+ source.Append("\n\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{data.NativeTypeSymbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private void GenerateUnmanagedCallbacksStruct(GeneratorExecutionContext context, CallbacksData data)
+ {
+ var symbol = data.FuncStructSymbol;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedNameOmitGlobal() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+ bool isInnerClass = symbol.ContainingType != null;
+
+ var source = new StringBuilder();
+
+ source.Append(
+ @"using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#pragma warning disable CA1707 // Disable warning: Identifiers should not contain underscores
+
+");
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append("\n{\n");
+ }
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("partial ");
+ source.Append(containingType.GetDeclarationKeyword());
+ source.Append(" ");
+ source.Append(containingType.NameWithTypeParameters());
+ source.Append("\n{\n");
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ source.Append("[StructLayout(LayoutKind.Sequential)]\n");
+ source.Append($"unsafe partial struct {symbol.Name}\n{{\n");
+
+ foreach (var callback in data.Methods)
+ {
+ source.Append(" ");
+ source.Append(callback.DeclaredAccessibility == Accessibility.Public ? "public " : "internal ");
+
+ source.Append("delegate* unmanaged<");
+
+ foreach (var parameter in callback.Parameters)
+ {
+ if (IsByRefParameter(parameter))
+ {
+ if (IsGodotInteropStruct(parameter.Type) || parameter.Type.IsValueType)
+ {
+ AppendPointerType(source, parameter.Type);
+ }
+ else
+ {
+ // If it's a by-ref param and we can't get the pointer
+ // just pass it by-ref and let it be pinned.
+ AppendRefKind(source, parameter.RefKind)
+ .Append(' ')
+ .Append(parameter.Type.FullQualifiedNameIncludeGlobal());
+ }
+ }
+ else
+ {
+ source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
+ }
+
+ source.Append(", ");
+ }
+
+ source.Append(callback.ReturnType.FullQualifiedNameIncludeGlobal());
+ source.Append($"> {callback.Name};\n");
+ }
+
+ source.Append("}\n");
+
+ if (isInnerClass)
+ {
+ var containingType = symbol.ContainingType;
+
+ while (containingType != null)
+ {
+ source.Append("}\n"); // outer class
+
+ containingType = containingType.ContainingType;
+ }
+ }
+
+ if (hasNamespace)
+ source.Append("}\n");
+
+ source.Append("\n#pragma warning restore CA1707\n");
+
+ context.AddSource($"{symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()}.generated",
+ SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static bool IsGodotInteropStruct(ITypeSymbol type) =>
+ GodotInteropStructs.Contains(type.FullQualifiedNameOmitGlobal());
+
+ private static bool IsByRefParameter(IParameterSymbol parameter) =>
+ parameter.RefKind is RefKind.In or RefKind.Out or RefKind.Ref;
+
+ private static StringBuilder AppendRefKind(StringBuilder source, RefKind refKind) =>
+ refKind switch
+ {
+ RefKind.In => source.Append("in"),
+ RefKind.Out => source.Append("out"),
+ RefKind.Ref => source.Append("ref"),
+ _ => source,
+ };
+
+ private static void AppendPointerType(StringBuilder source, ITypeSymbol type)
+ {
+ source.Append(type.FullQualifiedNameIncludeGlobal());
+ source.Append('*');
+ }
+
+ private static void AppendCustomUnsafeAsPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_ptr";
+
+ AppendPointerType(source, parameter.Type);
+ source.Append(' ');
+ source.Append(varName);
+ source.Append(" = ");
+
+ source.Append('(');
+ AppendPointerType(source, parameter.Type);
+ source.Append(')');
+
+ if (parameter.RefKind == RefKind.In)
+ source.Append("CustomUnsafe.ReadOnlyRefAsPointer(in ");
+ else
+ source.Append("CustomUnsafe.AsPointer(ref ");
+
+ source.Append(parameter.Name);
+
+ source.Append(");\n");
+ }
+
+ private static void AppendCopyToStackAndGetPointer(StringBuilder source, IParameterSymbol parameter,
+ out string varName)
+ {
+ varName = $"{parameter.Name}_copy";
+
+ source.Append(parameter.Type.FullQualifiedNameIncludeGlobal());
+ source.Append(' ');
+ source.Append(varName);
+ if (parameter.RefKind is RefKind.In or RefKind.Ref)
+ {
+ source.Append(" = ");
+ source.Append(parameter.Name);
+ }
+
+ source.Append(";\n");
+ }
+
+ private static readonly string[] GodotInteropStructs =
+ {
+ "Godot.NativeInterop.godot_ref",
+ "Godot.NativeInterop.godot_variant_call_error",
+ "Godot.NativeInterop.godot_variant",
+ "Godot.NativeInterop.godot_string",
+ "Godot.NativeInterop.godot_string_name",
+ "Godot.NativeInterop.godot_node_path",
+ "Godot.NativeInterop.godot_signal",
+ "Godot.NativeInterop.godot_callable",
+ "Godot.NativeInterop.godot_array",
+ "Godot.NativeInterop.godot_dictionary",
+ "Godot.NativeInterop.godot_packed_byte_array",
+ "Godot.NativeInterop.godot_packed_int32_array",
+ "Godot.NativeInterop.godot_packed_int64_array",
+ "Godot.NativeInterop.godot_packed_float32_array",
+ "Godot.NativeInterop.godot_packed_float64_array",
+ "Godot.NativeInterop.godot_packed_string_array",
+ "Godot.NativeInterop.godot_packed_vector2_array",
+ "Godot.NativeInterop.godot_packed_vector3_array",
+ "Godot.NativeInterop.godot_packed_color_array",
+ };
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
new file mode 100644
index 0000000000..e720d3878c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/GodotPlugins.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
+ <Nullable>enable</Nullable>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+
+ <!-- To generate the .runtimeconfig.json file-->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\GodotSharp\GodotSharp.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
new file mode 100644
index 0000000000..4ce02d221e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
@@ -0,0 +1,286 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+using Godot.NativeInterop;
+
+namespace GodotPlugins
+{
+ public static class Main
+ {
+ // IMPORTANT:
+ // Keeping strong references to the AssemblyLoadContext (our PluginLoadContext) prevents
+ // it from being unloaded. To avoid issues, we wrap the reference in this class, and mark
+ // all the methods that access it as non-inlineable. This way we prevent local references
+ // (either real or introduced by the JIT) to escape the scope of these methods due to
+ // inlining, which could keep the AssemblyLoadContext alive while trying to unload.
+ private sealed class PluginLoadContextWrapper
+ {
+ private PluginLoadContext? _pluginLoadContext;
+
+ public string? AssemblyLoadedPath
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _pluginLoadContext?.AssemblyLoadedPath;
+ }
+
+ public bool IsCollectible
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _pluginLoadContext?.IsCollectible ?? false;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
+ AssemblyName assemblyName,
+ string pluginPath,
+ ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext,
+ bool isCollectible
+ )
+ {
+ var wrapper = new PluginLoadContextWrapper();
+ wrapper._pluginLoadContext = new PluginLoadContext(
+ pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
+ var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
+ return (assembly, wrapper);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public WeakReference CreateWeakReference()
+ {
+ return new WeakReference(_pluginLoadContext, trackResurrection: true);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal void Unload()
+ {
+ _pluginLoadContext?.Unload();
+ _pluginLoadContext = null;
+ }
+ }
+
+ private static readonly List<AssemblyName> SharedAssemblies = new();
+ private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
+ private static Assembly? _editorApiAssembly;
+ private static PluginLoadContextWrapper? _projectLoadContext;
+ private static bool _editorHint = false;
+
+ private static readonly AssemblyLoadContext MainLoadContext =
+ AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
+ AssemblyLoadContext.Default;
+
+ private static DllImportResolver? _dllImportResolver;
+
+ // Right now we do it this way for simplicity as hot-reload is disabled. It will need to be changed later.
+ [UnmanagedCallersOnly]
+ // ReSharper disable once UnusedMember.Local
+ private static unsafe godot_bool InitializeFromEngine(IntPtr godotDllHandle, godot_bool editorHint,
+ PluginsCallbacks* pluginsCallbacks, ManagedCallbacks* managedCallbacks,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ _editorHint = editorHint.ToBool();
+
+ _dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
+
+ SharedAssemblies.Add(CoreApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
+
+ AlcReloadCfg.Configure(alcReloadEnabled: _editorHint);
+ NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+
+ if (_editorHint)
+ {
+ _editorApiAssembly = Assembly.Load("GodotSharpEditor");
+ SharedAssemblies.Add(_editorApiAssembly.GetName());
+ NativeLibrary.SetDllImportResolver(_editorApiAssembly, _dllImportResolver);
+ }
+
+ *pluginsCallbacks = new()
+ {
+ LoadProjectAssemblyCallback = &LoadProjectAssembly,
+ LoadToolsAssemblyCallback = &LoadToolsAssembly,
+ UnloadProjectPluginCallback = &UnloadProjectPlugin,
+ };
+
+ *managedCallbacks = ManagedCallbacks.Create();
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct PluginsCallbacks
+ {
+ public unsafe delegate* unmanaged<char*, godot_string*, godot_bool> LoadProjectAssemblyCallback;
+ public unsafe delegate* unmanaged<char*, IntPtr, int, IntPtr> LoadToolsAssemblyCallback;
+ public unsafe delegate* unmanaged<godot_bool> UnloadProjectPluginCallback;
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe godot_bool LoadProjectAssembly(char* nAssemblyPath, godot_string* outLoadedAssemblyPath)
+ {
+ try
+ {
+ if (_projectLoadContext != null)
+ return godot_bool.True; // Already loaded
+
+ string assemblyPath = new(nAssemblyPath);
+
+ (var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
+
+ string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
+ *outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
+
+ ScriptManagerBridge.LookupScriptsInAssembly(projectAssembly);
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ private static unsafe IntPtr LoadToolsAssembly(char* nAssemblyPath,
+ IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ try
+ {
+ string assemblyPath = new(nAssemblyPath);
+
+ if (_editorApiAssembly == null)
+ throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
+
+ var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
+
+ NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
+
+ var method = assembly.GetType("GodotTools.GodotSharpEditor")?
+ .GetMethod("InternalCreateInstance",
+ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (method == null)
+ {
+ throw new MissingMethodException("GodotTools.GodotSharpEditor",
+ "InternalCreateInstance");
+ }
+
+ return (IntPtr?)method
+ .Invoke(null, new object[] { unmanagedCallbacks, unmanagedCallbacksSize })
+ ?? IntPtr.Zero;
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath, bool isCollectible)
+ {
+ string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
+
+ var sharedAssemblies = new List<string>();
+
+ foreach (var sharedAssembly in SharedAssemblies)
+ {
+ string? sharedAssemblyName = sharedAssembly.Name;
+ if (sharedAssemblyName != null)
+ sharedAssemblies.Add(sharedAssemblyName);
+ }
+
+ return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
+ new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext, isCollectible);
+ }
+
+ [UnmanagedCallersOnly]
+ private static godot_bool UnloadProjectPlugin()
+ {
+ try
+ {
+ return UnloadPlugin(ref _projectLoadContext).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ Console.Error.WriteLine(e);
+ return godot_bool.False;
+ }
+ }
+
+ private static bool UnloadPlugin(ref PluginLoadContextWrapper? pluginLoadContext)
+ {
+ try
+ {
+ if (pluginLoadContext == null)
+ return true;
+
+ if (!pluginLoadContext.IsCollectible)
+ {
+ Console.Error.WriteLine("Cannot unload a non-collectible assembly load context.");
+ return false;
+ }
+
+ Console.WriteLine("Unloading assembly load context...");
+
+ var alcWeakReference = pluginLoadContext.CreateWeakReference();
+
+ pluginLoadContext.Unload();
+ pluginLoadContext = null;
+
+ int startTimeMs = Environment.TickCount;
+ bool takingTooLong = false;
+
+ while (alcWeakReference.IsAlive)
+ {
+ GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
+ GC.WaitForPendingFinalizers();
+
+ if (!alcWeakReference.IsAlive)
+ break;
+
+ int elapsedTimeMs = Environment.TickCount - startTimeMs;
+
+ if (!takingTooLong && elapsedTimeMs >= 2000)
+ {
+ takingTooLong = true;
+
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
+ }
+ else if (elapsedTimeMs >= 5000)
+ {
+ // TODO: How to log from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(
+ "Failed to unload assemblies. Possible causes: Strong GC handles, running threads, etc.");
+
+ return false;
+ }
+ }
+
+ Console.WriteLine("Assembly load context unloaded successfully.");
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ // TODO: How to log exceptions from GodotPlugins? (delegate pointer?)
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
new file mode 100644
index 0000000000..344b76a202
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/PluginLoadContext.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace GodotPlugins
+{
+ public class PluginLoadContext : AssemblyLoadContext
+ {
+ private readonly AssemblyDependencyResolver _resolver;
+ private readonly ICollection<string> _sharedAssemblies;
+ private readonly AssemblyLoadContext _mainLoadContext;
+
+ public string? AssemblyLoadedPath { get; private set; }
+
+ public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
+ AssemblyLoadContext mainLoadContext, bool isCollectible)
+ : base(isCollectible)
+ {
+ _resolver = new AssemblyDependencyResolver(pluginPath);
+ _sharedAssemblies = sharedAssemblies;
+ _mainLoadContext = mainLoadContext;
+ }
+
+ protected override Assembly? Load(AssemblyName assemblyName)
+ {
+ if (assemblyName.Name == null)
+ return null;
+
+ if (_sharedAssemblies.Contains(assemblyName.Name))
+ return _mainLoadContext.LoadFromAssemblyName(assemblyName);
+
+ string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ AssemblyLoadedPath = assemblyPath;
+
+ // Load in memory to prevent locking the file
+ using var assemblyFile = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ string pdbPath = Path.ChangeExtension(assemblyPath, ".pdb");
+
+ if (File.Exists(pdbPath))
+ {
+ using var pdbFile = File.Open(pdbPath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return LoadFromStream(assemblyFile, pdbFile);
+ }
+
+ return LoadFromStream(assemblyFile);
+ }
+
+ return null;
+ }
+
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
+ if (libraryPath != null)
+ return LoadUnmanagedDllFromPath(libraryPath);
+
+ return IntPtr.Zero;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln
index 4896d0a07d..8db42c2d1a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp.sln
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln
@@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\Go
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotPlugins", "GodotPlugins\GodotPlugins.csproj", "{944B77DB-497B-47F5-A1E3-81C35E3E9D4E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{7749662B-E30C-419A-B745-13852573360A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +22,13 @@ Global
{8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {944B77DB-497B-47F5-A1E3-81C35E3E9D4E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7749662B-E30C-419A-B745-13852573360A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
new file mode 100644
index 0000000000..65f33e43a8
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp.sln.DotSettings
@@ -0,0 +1,8 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GC/@EntryIndexedValue">GC</s:String>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=alcs/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=gdextension/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=godotsharp/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=icall/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=quat/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/UserDictionary/Words/=vcall/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 850ae7fc3b..0e46b63b59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -25,7 +20,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector3 Position
{
- get { return _position; }
+ readonly get { return _position; }
set { _position = value; }
}
@@ -36,7 +31,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector3 Size
{
- get { return _size; }
+ readonly get { return _size; }
set { _size = value; }
}
@@ -50,7 +45,7 @@ namespace Godot
/// </value>
public Vector3 End
{
- get { return _position + _size; }
+ readonly get { return _position + _size; }
set { _size = value - _position; }
}
@@ -59,7 +54,7 @@ namespace Godot
/// the most-negative corner is the origin and the size is positive.
/// </summary>
/// <returns>The modified <see cref="AABB"/>.</returns>
- public AABB Abs()
+ public readonly AABB Abs()
{
Vector3 end = End;
Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z));
@@ -71,7 +66,7 @@ namespace Godot
/// to <see cref="Position"/> + (<see cref="Size"/> / 2).
/// </summary>
/// <returns>The center.</returns>
- public Vector3 GetCenter()
+ public readonly Vector3 GetCenter()
{
return _position + (_size * 0.5f);
}
@@ -83,7 +78,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not this <see cref="AABB"/> encloses <paramref name="with"/>.
/// </returns>
- public bool Encloses(AABB with)
+ public readonly bool Encloses(AABB with)
{
Vector3 srcMin = _position;
Vector3 srcMax = _position + _size;
@@ -103,7 +98,7 @@ namespace Godot
/// </summary>
/// <param name="point">The point to include.</param>
/// <returns>The expanded <see cref="AABB"/>.</returns>
- public AABB Expand(Vector3 point)
+ public readonly AABB Expand(Vector3 point)
{
Vector3 begin = _position;
Vector3 end = _position + _size;
@@ -138,20 +133,14 @@ namespace Godot
}
/// <summary>
- /// Returns the area of the <see cref="AABB"/>.
- /// </summary>
- /// <returns>The area.</returns>
- public real_t GetArea()
- {
- return _size.x * _size.y * _size.z;
- }
-
- /// <summary>
/// Gets the position of one of the 8 endpoints of the <see cref="AABB"/>.
/// </summary>
/// <param name="idx">Which endpoint to get.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="idx"/> is less than 0 or greater than 7.
+ /// </exception>
/// <returns>An endpoint of the <see cref="AABB"/>.</returns>
- public Vector3 GetEndpoint(int idx)
+ public readonly Vector3 GetEndpoint(int idx)
{
switch (idx)
{
@@ -183,7 +172,7 @@ namespace Godot
/// Returns the normalized longest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A vector representing the normalized longest axis of the <see cref="AABB"/>.</returns>
- public Vector3 GetLongestAxis()
+ public readonly Vector3 GetLongestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
real_t maxSize = _size.x;
@@ -206,7 +195,7 @@ namespace Godot
/// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns>
- public Vector3.Axis GetLongestAxisIndex()
+ public readonly Vector3.Axis GetLongestAxisIndex()
{
var axis = Vector3.Axis.X;
real_t maxSize = _size.x;
@@ -229,7 +218,7 @@ namespace Godot
/// Returns the scalar length of the longest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>The scalar length of the longest axis of the <see cref="AABB"/>.</returns>
- public real_t GetLongestAxisSize()
+ public readonly real_t GetLongestAxisSize()
{
real_t maxSize = _size.x;
@@ -246,7 +235,7 @@ namespace Godot
/// Returns the normalized shortest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A vector representing the normalized shortest axis of the <see cref="AABB"/>.</returns>
- public Vector3 GetShortestAxis()
+ public readonly Vector3 GetShortestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
real_t maxSize = _size.x;
@@ -269,7 +258,7 @@ namespace Godot
/// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns>
- public Vector3.Axis GetShortestAxisIndex()
+ public readonly Vector3.Axis GetShortestAxisIndex()
{
var axis = Vector3.Axis.X;
real_t maxSize = _size.x;
@@ -292,7 +281,7 @@ namespace Godot
/// Returns the scalar length of the shortest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>The scalar length of the shortest axis of the <see cref="AABB"/>.</returns>
- public real_t GetShortestAxisSize()
+ public readonly real_t GetShortestAxisSize()
{
real_t maxSize = _size.x;
@@ -311,7 +300,7 @@ namespace Godot
/// </summary>
/// <param name="dir">The direction to find support for.</param>
/// <returns>A vector representing the support.</returns>
- public Vector3 GetSupport(Vector3 dir)
+ public readonly Vector3 GetSupport(Vector3 dir)
{
Vector3 halfExtents = _size * 0.5f;
Vector3 ofs = _position + halfExtents;
@@ -323,11 +312,20 @@ namespace Godot
}
/// <summary>
+ /// Returns the volume of the <see cref="AABB"/>.
+ /// </summary>
+ /// <returns>The volume.</returns>
+ public readonly real_t GetVolume()
+ {
+ return _size.x * _size.y * _size.z;
+ }
+
+ /// <summary>
/// Returns a copy of the <see cref="AABB"/> grown a given amount of units towards all the sides.
/// </summary>
/// <param name="by">The amount to grow by.</param>
/// <returns>The grown <see cref="AABB"/>.</returns>
- public AABB Grow(real_t by)
+ public readonly AABB Grow(real_t by)
{
AABB res = this;
@@ -342,30 +340,6 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if the <see cref="AABB"/> is flat or empty,
- /// or <see langword="false"/> otherwise.
- /// </summary>
- /// <returns>
- /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area.
- /// </returns>
- public bool HasNoArea()
- {
- return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f;
- }
-
- /// <summary>
- /// Returns <see langword="true"/> if the <see cref="AABB"/> has no surface (no size),
- /// or <see langword="false"/> otherwise.
- /// </summary>
- /// <returns>
- /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area.
- /// </returns>
- public bool HasNoSurface()
- {
- return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f;
- }
-
- /// <summary>
/// Returns <see langword="true"/> if the <see cref="AABB"/> contains a point,
/// or <see langword="false"/> otherwise.
/// </summary>
@@ -373,7 +347,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="AABB"/> contains <paramref name="point"/>.
/// </returns>
- public bool HasPoint(Vector3 point)
+ public readonly bool HasPoint(Vector3 point)
{
if (point.x < _position.x)
return false;
@@ -392,11 +366,39 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if the <see cref="AABB"/>
+ /// has a surface or a length, and <see langword="false"/>
+ /// if the <see cref="AABB"/> is empty (all components
+ /// of <see cref="Size"/> are zero or negative).
+ /// </summary>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has surface.
+ /// </returns>
+ public readonly bool HasSurface()
+ {
+ return _size.x > 0.0f || _size.y > 0.0f || _size.z > 0.0f;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> has
+ /// area, and <see langword="false"/> if the <see cref="AABB"/>
+ /// is linear, empty, or has a negative <see cref="Size"/>.
+ /// See also <see cref="GetVolume"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has volume.
+ /// </returns>
+ public readonly bool HasVolume()
+ {
+ return _size.x > 0.0f && _size.y > 0.0f && _size.z > 0.0f;
+ }
+
+ /// <summary>
/// Returns the intersection of this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other <see cref="AABB"/>.</param>
/// <returns>The clipped <see cref="AABB"/>.</returns>
- public AABB Intersection(AABB with)
+ public readonly AABB Intersection(AABB with)
{
Vector3 srcMin = _position;
Vector3 srcMax = _position + _size;
@@ -435,48 +437,25 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>
/// A <see langword="bool"/> for whether or not they are intersecting.
/// </returns>
- public bool Intersects(AABB with, bool includeBorders = false)
+ public readonly bool Intersects(AABB with)
{
- if (includeBorders)
- {
- if (_position.x > with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x < with._position.x)
- return false;
- if (_position.y > with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y < with._position.y)
- return false;
- if (_position.z > with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z < with._position.z)
- return false;
- }
- else
- {
- if (_position.x >= with._position.x + with._size.x)
- return false;
- if (_position.x + _size.x <= with._position.x)
- return false;
- if (_position.y >= with._position.y + with._size.y)
- return false;
- if (_position.y + _size.y <= with._position.y)
- return false;
- if (_position.z >= with._position.z + with._size.z)
- return false;
- if (_position.z + _size.z <= with._position.z)
- return false;
- }
+ if (_position.x >= with._position.x + with._size.x)
+ return false;
+ if (_position.x + _size.x <= with._position.x)
+ return false;
+ if (_position.y >= with._position.y + with._size.y)
+ return false;
+ if (_position.y + _size.y <= with._position.y)
+ return false;
+ if (_position.z >= with._position.z + with._size.z)
+ return false;
+ if (_position.z + _size.z <= with._position.z)
+ return false;
return true;
}
@@ -488,7 +467,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the <see cref="Plane"/>.
/// </returns>
- public bool IntersectsPlane(Plane plane)
+ public readonly bool IntersectsPlane(Plane plane)
{
Vector3[] points =
{
@@ -529,7 +508,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the line segment.
/// </returns>
- public bool IntersectsSegment(Vector3 from, Vector3 to)
+ public readonly bool IntersectsSegment(Vector3 from, Vector3 to)
{
real_t min = 0f;
real_t max = 1f;
@@ -584,11 +563,21 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other <see cref="AABB"/>.</param>
/// <returns>The merged <see cref="AABB"/>.</returns>
- public AABB Merge(AABB with)
+ public readonly AABB Merge(AABB with)
{
Vector3 beg1 = _position;
Vector3 beg2 = with._position;
@@ -700,14 +689,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the AABB and the object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is AABB)
- {
- return Equals((AABB)obj);
- }
-
- return false;
+ return obj is AABB other && Equals(other);
}
/// <summary>
@@ -717,7 +701,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other AABB.</param>
/// <returns>Whether or not the AABBs are exactly equal.</returns>
- public bool Equals(AABB other)
+ public readonly bool Equals(AABB other)
{
return _position == other._position && _size == other._size;
}
@@ -728,7 +712,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other AABB to compare.</param>
/// <returns>Whether or not the AABBs structures are approximately equal.</returns>
- public bool IsEqualApprox(AABB other)
+ public readonly bool IsEqualApprox(AABB other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
}
@@ -737,7 +721,7 @@ namespace Godot
/// Serves as the hash function for <see cref="AABB"/>.
/// </summary>
/// <returns>A hash code for this AABB.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
@@ -746,7 +730,7 @@ namespace Godot
/// Converts this <see cref="AABB"/> to a string.
/// </summary>
/// <returns>A string representation of this AABB.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"{_position}, {_size}";
}
@@ -755,7 +739,7 @@ namespace Godot
/// Converts this <see cref="AABB"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this AABB.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index a412047196..df306e5244 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -1,47 +1,35 @@
using System;
using System.Collections.Generic;
using System.Collections;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class ArraySafeHandle : SafeHandle
- {
- public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Array.godot_icall_Array_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
- public class Array : IList, IDisposable
+ public sealed class Array :
+ IList<Variant>,
+ IReadOnlyList<Variant>,
+ ICollection,
+ IDisposable
{
- private ArraySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_array.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
@@ -49,12 +37,12 @@ namespace Godot.Collections
/// </summary>
/// <param name="collection">The collection of elements to construct from.</param>
/// <returns>A new Godot Array.</returns>
- public Array(IEnumerable collection) : this()
+ public Array(IEnumerable<Variant> collection) : this()
{
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
- foreach (object element in collection)
+ foreach (Variant element in collection)
Add(element);
}
@@ -63,31 +51,126 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The objects to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params object[] array) : this()
+ public Array(Variant[] array) : this()
{
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ public Array(Span<StringName> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ public Array(Span<NodePath> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ public Array(Span<RID> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ // We must use ReadOnlySpan instead of Span here as this can accept implicit conversions
+ // from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are
+ // fine as long as the array is not mutated. However, Span does this type checking at
+ // instantiation, so it's not possible to use it even when not mutating anything.
+ // ReSharper disable once RedundantNameQualifier
+ public Array(ReadOnlySpan<Godot.Object> array) : this()
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+
+ NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+
+ int length = array.Length;
+
+ Resize(length);
+
+ for (int i = 0; i < length; i++)
+ this[i] = array[i];
+ }
+
+ private Array(godot_array nativeValueToOwn)
+ {
+ NativeValue = (godot_array.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_array_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- internal Array(ArraySafeHandle handle)
+ // Explicit name to make it very clear
+ internal static Array CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array(nativeValueToOwn);
+
+ ~Array()
{
- _safeHandle = handle;
+ Dispose(false);
}
- internal Array(IntPtr handle)
+ /// <summary>
+ /// Disposes of this <see cref="Array"/>.
+ /// </summary>
+ public void Dispose()
{
- _safeHandle = new ArraySafeHandle(handle);
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
- internal IntPtr GetPtr()
+ public void Dispose(bool disposing)
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
- return _safeHandle.DangerousGetHandle();
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+ }
}
/// <summary>
@@ -97,7 +180,10 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
- return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
+ godot_array newArray;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_duplicate(ref self, deep.ToGodotBool(), out newArray);
+ return CreateTakingOwnershipOfDisposableValue(newArray);
}
/// <summary>
@@ -107,7 +193,8 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return godot_icall_Array_Resize(GetPtr(), newSize);
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_resize(ref self, newSize);
}
/// <summary>
@@ -115,7 +202,8 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- godot_icall_Array_Shuffle(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_shuffle(ref self);
}
/// <summary>
@@ -126,94 +214,136 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array operator +(Array left, Array right)
{
- return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
- }
-
- // IDisposable
-
- /// <summary>
- /// Disposes of this <see cref="Array"/>.
- /// </summary>
- public void Dispose()
- {
- if (_disposed)
- return;
-
- if (_safeHandle != null)
+ if (left == null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ if (right == null)
+ return new Array();
+
+ return right.Duplicate(deep: false);
}
- _disposed = true;
- }
+ if (right == null)
+ return left.Duplicate(deep: false);
- // IList
+ int leftCount = left.Count;
+ int rightCount = right.Count;
- bool IList.IsReadOnly => false;
+ Array newArray = left.Duplicate(deep: false);
+ newArray.Resize(leftCount + rightCount);
- bool IList.IsFixedSize => false;
+ for (int i = 0; i < rightCount; i++)
+ newArray[i + leftCount] = right[i];
+
+ return newArray;
+ }
/// <summary>
- /// Returns the object at the given <paramref name="index"/>.
+ /// Returns the item at the given <paramref name="index"/>.
/// </summary>
- /// <value>The object at the given <paramref name="index"/>.</value>
- public object this[int index]
+ /// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
+ public unsafe Variant this[int index]
{
- get => godot_icall_Array_At(GetPtr(), index);
- set => godot_icall_Array_SetAt(GetPtr(), index, value);
+ get
+ {
+ GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return Variant.CreateCopyingBorrowed(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = value.CopyNativeVariant();
+ }
}
/// <summary>
- /// Adds an object to the end of this <see cref="Array"/>.
+ /// Adds an item to the end of this <see cref="Array"/>.
/// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
- /// <param name="value">The object to add.</param>
- /// <returns>The new size after adding the object.</returns>
- public int Add(object value) => godot_icall_Array_Add(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to add.</param>
+ public void Add(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
+ }
/// <summary>
- /// Checks if this <see cref="Array"/> contains the given object.
+ /// Checks if this <see cref="Array"/> contains the given item.
/// </summary>
- /// <param name="value">The item to look for.</param>
- /// <returns>Whether or not this array contains the given object.</returns>
- public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to look for.</param>
+ /// <returns>Whether or not this array contains the given item.</returns>
+ public bool Contains(Variant item) => IndexOf(item) != -1;
/// <summary>
/// Erases all items from this <see cref="Array"/>.
/// </summary>
- public void Clear() => godot_icall_Array_Clear(GetPtr());
+ public void Clear() => Resize(0);
/// <summary>
- /// Searches this <see cref="Array"/> for an object
+ /// Searches this <see cref="Array"/> for an item
/// and returns its index or -1 if not found.
/// </summary>
- /// <param name="value">The object to search for.</param>
- /// <returns>The index of the object, or -1 if not found.</returns>
- public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value);
+ /// <param name="item">The <see cref="Variant"/> item to search for.</param>
+ /// <returns>The index of the item, or -1 if not found.</returns>
+ public int IndexOf(Variant item)
+ {
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
+ }
/// <summary>
- /// Inserts a new object at a given position in the array.
+ /// Inserts a new item at a given position in the array.
/// The position must be a valid position of an existing item,
/// or the position at the end of the array.
/// Existing items will be moved to the right.
/// </summary>
/// <param name="index">The index to insert at.</param>
- /// <param name="value">The object to insert.</param>
- public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
+ /// <param name="item">The <see cref="Variant"/> item to insert.</param>
+ public void Insert(int index, Variant item)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ godot_variant variantValue = (godot_variant)item.NativeVar;
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
+ }
/// <summary>
- /// Removes the first occurrence of the specified <paramref name="value"/>
+ /// Removes the first occurrence of the specified <paramref name="item"/>
/// from this <see cref="Array"/>.
/// </summary>
- /// <param name="value">The value to remove.</param>
- public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value);
+ /// <param name="item">The value to remove.</param>
+ public bool Remove(Variant item)
+ {
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
+ }
/// <summary>
/// Removes an element from this <see cref="Array"/> by index.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
- public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index);
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_remove_at(ref self, index);
+ }
// ICollection
@@ -222,28 +352,77 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Array_Count(GetPtr());
-
- object ICollection.SyncRoot => this;
+ public int Count => NativeValue.DangerousSelfRef.Size;
bool ICollection.IsSynchronized => false;
+ object ICollection.SyncRoot => false;
+
+ bool ICollection<Variant>.IsReadOnly => false;
+
/// <summary>
/// Copies the elements of this <see cref="Array"/> to the given
- /// untyped C# array, starting at the given index.
+ /// <see cref="Variant"/> C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(Variant[] array, int arrayIndex)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), "Value cannot be null.");
+
+ if (arrayIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ int count = Count;
+
+ if (array.Length < (arrayIndex + count))
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
+
+ unsafe
+ {
+ for (int i = 0; i < count; i++)
+ {
+ array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ arrayIndex++;
+ }
+ }
+ }
+
+ void ICollection.CopyTo(System.Array array, int index)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
+ {
+ throw new ArgumentOutOfRangeException(nameof(index),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
+
+ int count = Count;
- // Internal call may throw ArgumentException
- godot_icall_Array_CopyTo(GetPtr(), array, index);
+ if (array.Length < (index + count))
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
+
+ unsafe
+ {
+ for (int i = 0; i < count; i++)
+ {
+ object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
+ array.SetValue(boxedVariant, index);
+ index++;
+ }
+ }
}
// IEnumerable
@@ -252,7 +431,7 @@ namespace Godot.Collections
/// Gets an enumerator for this <see cref="Array"/>.
/// </summary>
/// <returns>An enumerator.</returns>
- public IEnumerator GetEnumerator()
+ public IEnumerator<Variant> GetEnumerator()
{
int count = Count;
@@ -262,77 +441,42 @@ namespace Godot.Collections
}
}
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
/// <summary>
/// Converts this <see cref="Array"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString()
{
- return godot_icall_Array_ToString(GetPtr());
+ var self = (godot_array)NativeValue;
+ NativeFuncs.godotsharp_array_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At(IntPtr ptr, int index);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_Add(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal void GetVariantBorrowElementAt(int index, out godot_variant elem)
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ GetVariantBorrowElementAtUnchecked(index, out elem);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
+ /// <summary>
+ /// The variant returned via the <paramref name="elem"/> parameter is owned by the Array and must not be disposed.
+ /// </summary>
+ internal unsafe void GetVariantBorrowElementAtUnchecked(int index, out godot_variant elem)
+ {
+ elem = NativeValue.DangerousSelfRef.Elements[index];
+ }
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Array_ToString(IntPtr ptr);
+ internal interface IGenericGodotArray
+ {
+ public Array UnderlyingArray { get; }
}
/// <summary>
@@ -342,16 +486,35 @@ namespace Godot.Collections
/// such as arrays or <see cref="List{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
- public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
+ [SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
+ [SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
+ public sealed class Array<[MustBeVariant] T> :
+ IList<T>,
+ IReadOnlyList<T>,
+ ICollection<T>,
+ IEnumerable<T>,
+ IGenericGodotArray
{
- private Array _objectArray;
+ private static godot_variant ToVariantFunc(in Array<T> godotArray) =>
+ VariantUtils.CreateFromArray(godotArray);
- internal static int elemTypeEncoding;
- internal static IntPtr elemTypeClass;
+ private static Array<T> FromVariantFunc(in godot_variant variant) =>
+ VariantUtils.ConvertToArray<T>(variant);
- static Array()
+ static unsafe Array()
{
- Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass);
+ VariantUtils.GenericConversion<Array<T>>.ToVariantCb = &ToVariantFunc;
+ VariantUtils.GenericConversion<Array<T>>.FromVariantCb = &FromVariantFunc;
+ }
+
+ private readonly Array _underlyingArray;
+
+ Array IGenericGodotArray.UnderlyingArray => _underlyingArray;
+
+ internal ref godot_array.movable NativeValue
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingArray.NativeValue;
}
/// <summary>
@@ -359,7 +522,7 @@ namespace Godot.Collections
/// </summary>
public Array()
{
- _objectArray = new Array();
+ _underlyingArray = new Array();
}
/// <summary>
@@ -370,9 +533,12 @@ namespace Godot.Collections
public Array(IEnumerable<T> collection)
{
if (collection == null)
- throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
+ throw new ArgumentNullException(nameof(collection));
+
+ _underlyingArray = new Array();
- _objectArray = new Array(collection);
+ foreach (T element in collection)
+ Add(element);
}
/// <summary>
@@ -380,13 +546,15 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The items to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
- public Array(params T[] array) : this()
+ public Array(T[] array) : this()
{
if (array == null)
- {
- throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
- }
- _objectArray = new Array(array);
+ throw new ArgumentNullException(nameof(array));
+
+ _underlyingArray = new Array();
+
+ foreach (T element in array)
+ Add(element);
}
/// <summary>
@@ -395,23 +563,12 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
- _objectArray = array;
+ _underlyingArray = array;
}
- internal Array(IntPtr handle)
- {
- _objectArray = new Array(handle);
- }
-
- internal Array(ArraySafeHandle handle)
- {
- _objectArray = new Array(handle);
- }
-
- internal IntPtr GetPtr()
- {
- return _objectArray.GetPtr();
- }
+ // Explicit name to make it very clear
+ internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
+ => new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
@@ -419,7 +576,7 @@ namespace Godot.Collections
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
- return from._objectArray;
+ return from?._underlyingArray;
}
/// <summary>
@@ -429,7 +586,7 @@ namespace Godot.Collections
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
- return new Array<T>(_objectArray.Duplicate(deep));
+ return new Array<T>(_underlyingArray.Duplicate(deep));
}
/// <summary>
@@ -439,7 +596,7 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return _objectArray.Resize(newSize);
+ return _underlyingArray.Resize(newSize);
}
/// <summary>
@@ -447,7 +604,7 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- _objectArray.Shuffle();
+ _underlyingArray.Shuffle();
}
/// <summary>
@@ -458,7 +615,18 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
- return new Array<T>(left._objectArray + right._objectArray);
+ if (left == null)
+ {
+ if (right == null)
+ return new Array<T>();
+
+ return right.Duplicate(deep: false);
+ }
+
+ if (right == null)
+ return left.Duplicate(deep: false);
+
+ return new Array<T>(left._underlyingArray + right._underlyingArray);
}
// IList<T>
@@ -467,10 +635,23 @@ namespace Godot.Collections
/// Returns the value at the given <paramref name="index"/>.
/// </summary>
/// <value>The value at the given <paramref name="index"/>.</value>
- public T this[int index]
+ public unsafe T this[int index]
{
- get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
- set { _objectArray[index] = value; }
+ get
+ {
+ _underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
+ return VariantUtils.ConvertTo<T>(borrowElem);
+ }
+ set
+ {
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ var self = (godot_array)_underlyingArray.NativeValue;
+ godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
+ godot_variant* itemPtr = &ptrw[index];
+ (*itemPtr).Dispose();
+ *itemPtr = VariantUtils.CreateFrom(value);
+ }
}
/// <summary>
@@ -481,7 +662,9 @@ namespace Godot.Collections
/// <returns>The index of the item, or -1 if not found.</returns>
public int IndexOf(T item)
{
- return _objectArray.IndexOf(item);
+ using var variantValue = VariantUtils.CreateFrom(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
/// <summary>
@@ -494,7 +677,12 @@ namespace Godot.Collections
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
- _objectArray.Insert(index, item);
+ if (index < 0 || index > Count)
+ throw new ArgumentOutOfRangeException(nameof(index));
+
+ using var variantValue = VariantUtils.CreateFrom(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
/// <summary>
@@ -503,7 +691,7 @@ namespace Godot.Collections
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
- _objectArray.RemoveAt(index);
+ _underlyingArray.RemoveAt(index);
}
// ICollection<T>
@@ -513,10 +701,7 @@ namespace Godot.Collections
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectArray.Count; }
- }
+ public int Count => _underlyingArray.Count;
bool ICollection<T>.IsReadOnly => false;
@@ -528,7 +713,9 @@ namespace Godot.Collections
/// <returns>The new size after adding the item.</returns>
public void Add(T item)
{
- _objectArray.Add(item);
+ using var variantValue = VariantUtils.CreateFrom(item);
+ var self = (godot_array)_underlyingArray.NativeValue;
+ _ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}
/// <summary>
@@ -536,7 +723,7 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
- _objectArray.Clear();
+ _underlyingArray.Clear();
}
/// <summary>
@@ -544,10 +731,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="item">The item to look for.</param>
/// <returns>Whether or not this array contains the given item.</returns>
- public bool Contains(T item)
- {
- return _objectArray.Contains(item);
- }
+ public bool Contains(T item) => IndexOf(item) != -1;
/// <summary>
/// Copies the elements of this <see cref="Array{T}"/> to the given
@@ -561,19 +745,22 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
-
- // TODO This may be quite slow because every element access is an internal call.
- // It could be moved entirely to an internal call if we find out how to do the cast there.
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
+ }
- int count = _objectArray.Count;
+ int count = Count;
if (array.Length < (arrayIndex + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ {
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ }
for (int i = 0; i < count; i++)
{
- array[arrayIndex] = (T)this[i];
+ array[arrayIndex] = this[i];
arrayIndex++;
}
}
@@ -586,7 +773,14 @@ namespace Godot.Collections
/// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
- return Array.godot_icall_Array_Remove(GetPtr(), item);
+ int index = IndexOf(item);
+ if (index >= 0)
+ {
+ RemoveAt(index);
+ return true;
+ }
+
+ return false;
}
// IEnumerable<T>
@@ -597,23 +791,26 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
- int count = _objectArray.Count;
+ int count = _underlyingArray.Count;
for (int i = 0; i < count; i++)
{
- yield return (T)this[i];
+ yield return this[i];
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
- public override string ToString() => _objectArray.ToString();
+ public override string ToString() => _underlyingArray.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Array<T> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Array<T>(Variant from) => from.AsGodotArray<T>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index 2febf37f05..acdae83d2e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -1,32 +1,50 @@
using System;
+using System.Diagnostics.CodeAnalysis;
+
+#nullable enable
namespace Godot
{
/// <summary>
- /// An attribute that determines if an assembly has scripts. If so, what types of scripts the assembly has.
+ /// Attribute that determines that the assembly contains Godot scripts and, optionally, the
+ /// collection of types that implement scripts; otherwise, retrieving the types requires lookup.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
- private readonly bool requiresLookup;
- private readonly System.Type[] scriptTypes;
+ /// <summary>
+ /// If the Godot scripts contained in the assembly require lookup
+ /// and can't rely on <see cref="ScriptTypes"/>.
+ /// </summary>
+ [MemberNotNullWhen(false, nameof(ScriptTypes))]
+ public bool RequiresLookup { get; }
/// <summary>
- /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// The collection of types that implement a Godot script.
+ /// </summary>
+ public Type[]? ScriptTypes { get; }
+
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance
+ /// that requires lookup to get the Godot scripts.
/// </summary>
public AssemblyHasScriptsAttribute()
{
- requiresLookup = true;
+ RequiresLookup = true;
+ ScriptTypes = null;
}
/// <summary>
- /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// Constructs a new AssemblyHasScriptsAttribute instance
+ /// that includes the Godot script types and requires no lookup.
/// </summary>
- /// <param name="scriptTypes">The specified type(s) of scripts.</param>
- public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
+ /// <param name="scriptTypes">The collection of types that implement a Godot script.</param>
+ public AssemblyHasScriptsAttribute(Type[] scriptTypes)
{
- requiresLookup = false;
- this.scriptTypes = scriptTypes;
+ RequiresLookup = false;
+ ScriptTypes = scriptTypes;
}
}
}
+
+#nullable restore
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
deleted file mode 100644
index 0b00878e8c..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-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 46eb128d37..a48d79091f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -3,23 +3,30 @@ using System;
namespace Godot
{
/// <summary>
- /// An attribute used to export objects.
+ /// Exports the annotated member as a property of the Godot Object.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
- public class ExportAttribute : Attribute
+ public sealed class ExportAttribute : Attribute
{
- private PropertyHint hint;
- private string hintString;
+ /// <summary>
+ /// Optional hint that determines how the property should be handled by the editor.
+ /// </summary>
+ public PropertyHint Hint { get; }
+
+ /// <summary>
+ /// Optional string that can contain additional metadata for the <see cref="Hint"/>.
+ /// </summary>
+ public string HintString { get; }
/// <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>
+ /// <param name="hint">The hint for the exported property.</param>
+ /// <param name="hintString">A string that may contain additional metadata for the hint.</param>
public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
{
- this.hint = hint;
- this.hintString = hintString;
+ Hint = hint;
+ HintString = hintString;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs
new file mode 100644
index 0000000000..2ae55acd3e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportCategoryAttribute.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new category for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportCategoryAttribute : Attribute
+ {
+ /// <summary>
+ /// Name of the category.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Define a new category for the following exported properties.
+ /// </summary>
+ /// <param name="name">The name of the category.</param>
+ public ExportCategoryAttribute(string name)
+ {
+ Name = name;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs
new file mode 100644
index 0000000000..82bd446640
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportGroupAttribute.cs
@@ -0,0 +1,34 @@
+using System;
+
+#nullable enable
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new group for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportGroupAttribute : Attribute
+ {
+ /// <summary>
+ /// Name of the group.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// If provided, the prefix that all properties must have to be considered part of the group.
+ /// </summary>
+ public string? Prefix { get; }
+
+ /// <summary>
+ /// Define a new group for the following exported properties.
+ /// </summary>
+ /// <param name="name">The name of the group.</param>
+ /// <param name="prefix">If provided, the group would make group to only consider properties that have this prefix.</param>
+ public ExportGroupAttribute(string name, string prefix = "")
+ {
+ Name = name;
+ Prefix = prefix;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs
new file mode 100644
index 0000000000..3282b466f6
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportSubgroupAttribute.cs
@@ -0,0 +1,34 @@
+using System;
+
+#nullable enable
+
+namespace Godot
+{
+ /// <summary>
+ /// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+ public sealed class ExportSubgroupAttribute : Attribute
+ {
+ /// <summary>
+ /// Name of the subgroup.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// If provided, the prefix that all properties must have to be considered part of the subgroup.
+ /// </summary>
+ public string? Prefix { get; }
+
+ /// <summary>
+ /// Define a new subgroup for the following exported properties. This helps to organize properties in the Inspector dock.
+ /// </summary>
+ /// <param name="name">The name of the subgroup.</param>
+ /// <param name="prefix">If provided, the subgroup would make group to only consider properties that have this prefix.</param>
+ public ExportSubgroupAttribute(string name, string prefix = "")
+ {
+ Name = name;
+ Prefix = prefix;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
deleted file mode 100644
index 8d4ff0fdb7..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// An attribute for a method.
- /// </summary>
- [AttributeUsage(AttributeTargets.Method)]
- internal class GodotMethodAttribute : Attribute
- {
- private string methodName;
-
- 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/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
new file mode 100644
index 0000000000..23088378d1
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Attribute that restricts generic type parameters to be only types
+ /// that can be marshaled from/to a <see cref="Variant"/>.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.GenericParameter)]
+ public class MustBeVariantAttribute : Attribute { }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
index fb37838ffa..afee926464 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
@@ -19,17 +19,17 @@ namespace Godot
/// <summary>
/// If the method will also be called locally; otherwise, it is only called remotely.
/// </summary>
- public bool CallLocal { get; set; } = false;
+ public bool CallLocal { get; init; } = false;
/// <summary>
/// Transfer mode for the annotated method.
/// </summary>
- public MultiplayerPeer.TransferModeEnum TransferMode { get; set; } = MultiplayerPeer.TransferModeEnum.Reliable;
+ public MultiplayerPeer.TransferModeEnum TransferMode { get; init; } = MultiplayerPeer.TransferModeEnum.Reliable;
/// <summary>
/// Transfer channel for the annotated mode.
/// </summary>
- public int TransferChannel { get; set; } = 0;
+ public int TransferChannel { get; init; } = 0;
/// <summary>
/// Constructs a <see cref="RPCAttribute"/> instance.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 3ebb6612de..f05bcdac38 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -8,7 +8,10 @@ namespace Godot
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
- private string path;
+ /// <summary>
+ /// File path to the script.
+ /// </summary>
+ public string Path { get; }
/// <summary>
/// Constructs a new ScriptPathAttribute instance.
@@ -16,7 +19,7 @@ namespace Godot
/// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
- this.path = path;
+ Path = path;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
index 07a214f543..38e68a89d5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
@@ -2,6 +2,6 @@ using System;
namespace Godot
{
- [AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
+ [AttributeUsage(AttributeTargets.Delegate)]
public class SignalAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 37bdc42c2d..b57317e1d0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -34,7 +29,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Column0"/> and array index <c>[0]</c>.</value>
public Vector3 x
{
- get => Column0;
+ readonly get => Column0;
set => Column0 = value;
}
@@ -44,7 +39,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Column1"/> and array index <c>[1]</c>.</value>
public Vector3 y
{
- get => Column1;
+ readonly get => Column1;
set => Column1 = value;
}
@@ -54,7 +49,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Column2"/> and array index <c>[2]</c>.</value>
public Vector3 z
{
- get => Column2;
+ readonly get => Column2;
set => Column2 = value;
}
@@ -85,7 +80,7 @@ namespace Godot
/// <value>Equivalent to <see cref="x"/> and array index <c>[0]</c>.</value>
public Vector3 Column0
{
- get => new Vector3(Row0.x, Row1.x, Row2.x);
+ readonly get => new Vector3(Row0.x, Row1.x, Row2.x);
set
{
Row0.x = value.x;
@@ -100,7 +95,7 @@ namespace Godot
/// <value>Equivalent to <see cref="y"/> and array index <c>[1]</c>.</value>
public Vector3 Column1
{
- get => new Vector3(Row0.y, Row1.y, Row2.y);
+ readonly get => new Vector3(Row0.y, Row1.y, Row2.y);
set
{
Row0.y = value.x;
@@ -115,7 +110,7 @@ namespace Godot
/// <value>Equivalent to <see cref="z"/> and array index <c>[2]</c>.</value>
public Vector3 Column2
{
- get => new Vector3(Row0.z, Row1.z, Row2.z);
+ readonly get => new Vector3(Row0.z, Row1.z, Row2.z);
set
{
Row0.z = value.x;
@@ -125,38 +120,16 @@ namespace Godot
}
/// <summary>
- /// The scale of this basis.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value>
- public Vector3 Scale
- {
- get
- {
- real_t detSign = Mathf.Sign(Determinant());
- return detSign * new Vector3
- (
- Column0.Length(),
- Column1.Length(),
- Column2.Length()
- );
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- Column0 *= value.x;
- Column1 *= value.y;
- Column2 *= value.z;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector3"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> is not 0, 1, 2 or 3.
+ /// </exception>
/// <value>The basis column.</value>
public Vector3 this[int column]
{
- get
+ readonly get
{
switch (column)
{
@@ -167,7 +140,7 @@ namespace Godot
case 2:
return Column2;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -184,7 +157,7 @@ namespace Godot
Column2 = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -197,7 +170,7 @@ namespace Godot
/// <value>The matrix element.</value>
public real_t this[int column, int row]
{
- get
+ readonly get
{
return this[column][row];
}
@@ -236,7 +209,7 @@ namespace Godot
/// and is usually considered invalid.
/// </summary>
/// <returns>The determinant of the basis matrix.</returns>
- public real_t Determinant()
+ public readonly real_t Determinant()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2];
@@ -246,54 +219,256 @@ namespace Godot
}
/// <summary>
- /// Returns the basis's rotation in the form of Euler angles
- /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last).
- /// The returned vector contains the rotation angles in
- /// the format (X angle, Y angle, Z angle).
+ /// Returns the basis's rotation in the form of Euler angles.
+ /// The Euler order depends on the [param order] parameter,
+ /// by default it uses the YXZ convention: when decomposing,
+ /// first Z, then X, and Y last. The returned vector contains
+ /// the rotation angles in the format (X angle, Y angle, Z angle).
///
/// Consider using the <see cref="GetRotationQuaternion"/> method instead, which
/// returns a <see cref="Quaternion"/> quaternion instead of Euler angles.
/// </summary>
+ /// <param name="order">The Euler order to use. By default, use YXZ order (most common).</param>
/// <returns>A <see cref="Vector3"/> representing the basis rotation in Euler angles.</returns>
- public Vector3 GetEuler()
+ public readonly Vector3 GetEuler(EulerOrder order = EulerOrder.Yxz)
{
- Basis m = Orthonormalized();
-
- Vector3 euler;
- euler.z = 0.0f;
-
- real_t mzy = m.Row1[2];
-
- if (mzy < 1.0f)
+ switch (order)
{
- if (mzy > -1.0f)
+ case EulerOrder.Xyz:
{
- euler.x = Mathf.Asin(-mzy);
- euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]);
- euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]);
+ // Euler angles in XYZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz -cy*sz sy
+ // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+ Vector3 euler;
+ real_t sy = Row0[2];
+ if (sy < (1.0f - Mathf.Epsilon))
+ {
+ if (sy > -(1.0f - Mathf.Epsilon))
+ {
+ // is this a pure Y rotation?
+ if (Row1[0] == 0 && Row0[1] == 0 && Row1[2] == 0 && Row2[1] == 0 && Row1[1] == 1)
+ {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = 0;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(-Row1[2], Row2[2]);
+ euler.y = Mathf.Asin(sy);
+ euler.z = Mathf.Atan2(-Row0[1], Row0[0]);
+ }
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = -Mathf.Tau / 4.0f;
+ euler.z = 0.0f;
+ }
+ }
+ else
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = Mathf.Tau / 4.0f;
+ euler.z = 0.0f;
+ }
+ return euler;
}
- else
+ case EulerOrder.Xzy:
{
- euler.x = Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
+ // Euler angles in XZY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy -sz cz*sy
+ // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx
+ // cy*sx*sz cz*sx cx*cy+sx*sz*sy
+ Vector3 euler;
+ real_t sz = Row0[1];
+ if (sz < (1.0f - Mathf.Epsilon))
+ {
+ if (sz > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row1[1]);
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = Mathf.Asin(-sz);
+ }
+ else
+ {
+ // It's -1
+ euler.x = -Mathf.Atan2(Row1[2], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = Mathf.Tau / 4.0f;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = -Mathf.Atan2(Row1[2], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = -Mathf.Tau / 4.0f;
+ }
+ return euler;
}
- }
- else
- {
- euler.x = -Mathf.Pi * 0.5f;
- euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]);
- }
+ case EulerOrder.Yxz:
+ {
+ // Euler angles in YXZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
+ // cx*sz cx*cz -sx
+ // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
+ Vector3 euler;
+ real_t m12 = Row1[2];
+ if (m12 < (1 - Mathf.Epsilon))
+ {
+ if (m12 > -(1 - Mathf.Epsilon))
+ {
+ // is this a pure X rotation?
+ if (Row1[0] == 0 && Row0[1] == 0 && Row0[2] == 0 && Row2[0] == 0 && Row0[0] == 1)
+ {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = Mathf.Atan2(-m12, Row1[1]);
+ euler.y = 0;
+ euler.z = 0;
+ }
+ else
+ {
+ euler.x = Mathf.Asin(-m12);
+ euler.y = Mathf.Atan2(Row0[2], Row2[2]);
+ euler.z = Mathf.Atan2(Row1[0], Row1[1]);
+ }
+ }
+ else
+ { // m12 == -1
+ euler.x = Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[1], Row0[0]);
+ euler.z = 0;
+ }
+ }
+ else
+ { // m12 == 1
+ euler.x = -Mathf.Tau / 4.0f;
+ euler.y = -Mathf.Atan2(Row0[1], Row0[0]);
+ euler.z = 0;
+ }
- return euler;
+ return euler;
+ }
+ case EulerOrder.Yzx:
+ {
+ // Euler angles in YZX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
+ // sz cz*cx -cz*sx
+ // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
+ Vector3 euler;
+ real_t sz = Row1[0];
+ if (sz < (1.0f - Mathf.Epsilon))
+ {
+ if (sz > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(-Row1[2], Row1[1]);
+ euler.y = Mathf.Atan2(-Row2[0], Row0[0]);
+ euler.z = Mathf.Asin(sz);
+ }
+ else
+ {
+ // It's -1
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = -Mathf.Tau / 4.0f;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = 0.0f;
+ euler.z = Mathf.Tau / 4.0f;
+ }
+ return euler;
+ }
+ case EulerOrder.Zxy:
+ {
+ // Euler angles in ZXY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
+ // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx
+ // -cx*sy sx cx*cy
+ Vector3 euler;
+ real_t sx = Row2[1];
+ if (sx < (1.0f - Mathf.Epsilon))
+ {
+ if (sx > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Asin(sx);
+ euler.y = Mathf.Atan2(-Row2[0], Row2[2]);
+ euler.z = Mathf.Atan2(-Row0[1], Row1[1]);
+ }
+ else
+ {
+ // It's -1
+ euler.x = -Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = Mathf.Tau / 4.0f;
+ euler.y = Mathf.Atan2(Row0[2], Row0[0]);
+ euler.z = 0;
+ }
+ return euler;
+ }
+ case EulerOrder.Zyx:
+ {
+ // Euler angles in ZYX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy
+ // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
+ // -sy cy*sx cy*cx
+ Vector3 euler;
+ real_t sy = Row2[0];
+ if (sy < (1.0f - Mathf.Epsilon))
+ {
+ if (sy > -(1.0f - Mathf.Epsilon))
+ {
+ euler.x = Mathf.Atan2(Row2[1], Row2[2]);
+ euler.y = Mathf.Asin(-sy);
+ euler.z = Mathf.Atan2(Row1[0], Row0[0]);
+ }
+ else
+ {
+ // It's -1
+ euler.x = 0;
+ euler.y = Mathf.Tau / 4.0f;
+ euler.z = -Mathf.Atan2(Row0[1], Row1[1]);
+ }
+ }
+ else
+ {
+ // It's 1
+ euler.x = 0;
+ euler.y = -Mathf.Tau / 4.0f;
+ euler.z = -Mathf.Atan2(Row0[1], Row1[1]);
+ }
+ return euler;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(order));
+ }
}
- /// <summary>
- /// Returns the basis's rotation in the form of a quaternion.
- /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
- /// mind that quaternions should generally be preferred to Euler angles.
- /// </summary>
- /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns>
- internal Quaternion GetQuaternion()
+ internal readonly Quaternion GetQuaternion()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -352,7 +527,7 @@ namespace Godot
/// be preferred to Euler angles.
/// </summary>
/// <returns>The basis rotation.</returns>
- public Quaternion GetRotationQuaternion()
+ public readonly Quaternion GetRotationQuaternion()
{
Basis orthonormalizedBasis = Orthonormalized();
real_t det = orthonormalizedBasis.Determinant();
@@ -367,113 +542,25 @@ namespace Godot
}
/// <summary>
- /// Get rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
- /// </summary>
- /// <param name="index">Which row.</param>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns>
- public Vector3 GetRow(int index)
- {
- switch (index)
- {
- case 0:
- return Row0;
- case 1:
- return Row1;
- case 2:
- return Row2;
- default:
- throw new IndexOutOfRangeException();
- }
- }
-
- /// <summary>
- /// Sets rows by index. Rows are not very useful for user code,
- /// but are more efficient for some internal calculations.
- /// </summary>
- /// <param name="index">Which row.</param>
- /// <param name="value">The vector to set the row to.</param>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the <paramref name="index"/> is not 0, 1 or 2.
- /// </exception>
- public void SetRow(int index, Vector3 value)
- {
- switch (index)
- {
- case 0:
- Row0 = value;
- return;
- case 1:
- Row1 = value;
- return;
- case 2:
- Row2 = value;
- return;
- default:
- throw new IndexOutOfRangeException();
- }
- }
-
- /// <summary>
- /// This function considers a discretization of rotations into
- /// 24 points on unit sphere, lying along the vectors (x, y, z) with
- /// each component being either -1, 0, or 1, and returns the index
- /// of the point best representing the orientation of the object.
- /// It is mainly used by the <see cref="GridMap"/> editor.
- ///
- /// For further details, refer to the Godot source code.
+ /// Assuming that the matrix is the combination of a rotation and scaling,
+ /// return the absolute value of scaling factors along each axis.
/// </summary>
- /// <returns>The orthogonal index.</returns>
- public int GetOrthogonalIndex()
+ public readonly Vector3 GetScale()
{
- var orth = this;
-
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 3; j++)
- {
- var row = orth.GetRow(i);
-
- real_t v = row[j];
-
- if (v > 0.5f)
- {
- v = 1.0f;
- }
- else if (v < -0.5f)
- {
- v = -1.0f;
- }
- else
- {
- v = 0f;
- }
-
- row[j] = v;
-
- orth.SetRow(i, row);
- }
- }
-
- for (int i = 0; i < 24; i++)
- {
- if (orth == _orthoBases[i])
- {
- return i;
- }
- }
-
- return 0;
+ real_t detSign = Mathf.Sign(Determinant());
+ return detSign * new Vector3
+ (
+ Column0.Length(),
+ Column1.Length(),
+ Column2.Length()
+ );
}
/// <summary>
/// Returns the inverse of the matrix.
/// </summary>
/// <returns>The inverse matrix.</returns>
- public Basis Inverse()
+ public readonly Basis Inverse()
{
real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1];
real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2];
@@ -504,12 +591,31 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this basis is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Row0.IsFinite() && Row1.IsFinite() && Row2.IsFinite();
+ }
+
+ internal readonly Basis Lerp(Basis to, real_t weight)
+ {
+ Basis b = this;
+ b.Row0 = Row0.Lerp(to.Row0, weight);
+ b.Row1 = Row1.Lerp(to.Row1, weight);
+ b.Row2 = Row2.Lerp(to.Row2, weight);
+ return b;
+ }
+
+ /// <summary>
/// Returns the orthonormalized version of the basis matrix (useful to
/// call occasionally to avoid rounding errors for orthogonal matrices).
/// This performs a Gram-Schmidt orthonormalization on the basis of the matrix.
/// </summary>
/// <returns>An orthonormalized basis matrix.</returns>
- public Basis Orthonormalized()
+ public readonly Basis Orthonormalized()
{
Vector3 column0 = this[0];
Vector3 column1 = this[1];
@@ -531,9 +637,9 @@ namespace Godot
/// <param name="axis">The axis to rotate around. Must be normalized.</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)
+ public readonly Basis Rotated(Vector3 axis, real_t angle)
{
- return new Basis(axis, phi) * this;
+ return new Basis(axis, angle) * this;
}
/// <summary>
@@ -541,7 +647,7 @@ namespace Godot
/// </summary>
/// <param name="scale">The scale to introduce.</param>
/// <returns>The scaled basis matrix.</returns>
- public Basis Scaled(Vector3 scale)
+ public readonly Basis Scaled(Vector3 scale)
{
Basis b = this;
b.Row0 *= scale.x;
@@ -557,7 +663,7 @@ namespace Godot
/// <param name="target">The destination basis 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 basis matrix of the interpolation.</returns>
- public Basis Slerp(Basis target, real_t weight)
+ public readonly Basis Slerp(Basis target, real_t weight)
{
Quaternion from = new Quaternion(this);
Quaternion to = new Quaternion(target);
@@ -575,7 +681,7 @@ namespace Godot
/// </summary>
/// <param name="with">A vector to calculate the dot product with.</param>
/// <returns>The resulting dot product.</returns>
- public real_t Tdotx(Vector3 with)
+ public readonly real_t Tdotx(Vector3 with)
{
return Row0[0] * with[0] + Row1[0] * with[1] + Row2[0] * with[2];
}
@@ -585,7 +691,7 @@ namespace Godot
/// </summary>
/// <param name="with">A vector to calculate the dot product with.</param>
/// <returns>The resulting dot product.</returns>
- public real_t Tdoty(Vector3 with)
+ public readonly real_t Tdoty(Vector3 with)
{
return Row0[1] * with[0] + Row1[1] * with[1] + Row2[1] * with[2];
}
@@ -595,7 +701,7 @@ namespace Godot
/// </summary>
/// <param name="with">A vector to calculate the dot product with.</param>
/// <returns>The resulting dot product.</returns>
- public real_t Tdotz(Vector3 with)
+ public readonly real_t Tdotz(Vector3 with)
{
return Row0[2] * with[0] + Row1[2] * with[1] + Row2[2] * with[2];
}
@@ -604,60 +710,22 @@ namespace Godot
/// Returns the transposed version of the basis matrix.
/// </summary>
/// <returns>The transposed basis matrix.</returns>
- public Basis Transposed()
+ public readonly Basis Transposed()
{
Basis tr = this;
- real_t temp = tr.Row0[1];
- tr.Row0[1] = tr.Row1[0];
- tr.Row1[0] = temp;
+ tr.Row0[1] = Row1[0];
+ tr.Row1[0] = Row0[1];
- temp = tr.Row0[2];
- tr.Row0[2] = tr.Row2[0];
- tr.Row2[0] = temp;
+ tr.Row0[2] = Row2[0];
+ tr.Row2[0] = Row0[2];
- temp = tr.Row1[2];
- tr.Row1[2] = tr.Row2[1];
- tr.Row2[1] = temp;
+ tr.Row1[2] = Row2[1];
+ tr.Row2[1] = Row1[2];
return tr;
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by the basis matrix.
- /// </summary>
- /// <seealso cref="XformInv(Vector3)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
- return new Vector3
- (
- Row0.Dot(v),
- Row1.Dot(v),
- Row2.Dot(v)
- );
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by the transposed basis matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// basis matrix only if it represents a rotation-reflection.
- /// </summary>
- /// <seealso cref="Xform(Vector3)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- public Vector3 XformInv(Vector3 v)
- {
- return new Vector3
- (
- Row0[0] * v.x + Row1[0] * v.y + Row2[0] * v.z,
- Row0[1] * v.x + Row1[1] * v.y + Row2[1] * v.z,
- Row0[2] * v.x + Row1[2] * v.y + Row2[2] * v.z
- );
- }
-
private static readonly Basis[] _orthoBases = {
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
@@ -719,7 +787,7 @@ namespace Godot
/// <param name="quaternion">The quaternion to create the basis from.</param>
public Basis(Quaternion quaternion)
{
- real_t s = 2.0f / quaternion.LengthSquared;
+ real_t s = 2.0f / quaternion.LengthSquared();
real_t xs = quaternion.x * s;
real_t ys = quaternion.y * s;
@@ -740,63 +808,34 @@ namespace Godot
}
/// <summary>
- /// Constructs a pure rotation basis matrix from the given Euler angles
- /// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
- /// given in the vector format as (X angle, Y angle, Z angle).
- ///
- /// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which
- /// uses a <see cref="Quaternion"/> quaternion instead of Euler angles.
- /// </summary>
- /// <param name="eulerYXZ">The Euler angles to create the basis from.</param>
- public Basis(Vector3 eulerYXZ)
- {
- real_t c;
- real_t s;
-
- c = Mathf.Cos(eulerYXZ.x);
- s = Mathf.Sin(eulerYXZ.x);
- var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c);
-
- c = Mathf.Cos(eulerYXZ.y);
- s = Mathf.Sin(eulerYXZ.y);
- var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c);
-
- c = Mathf.Cos(eulerYXZ.z);
- s = Mathf.Sin(eulerYXZ.z);
- var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1);
-
- this = ymat * xmat * zmat;
- }
-
- /// <summary>
/// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/>
/// 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="angle">The angle to rotate, in radians.</param>
- public Basis(Vector3 axis, real_t phi)
+ public Basis(Vector3 axis, real_t angle)
{
Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- real_t cosine = Mathf.Cos(phi);
- Row0.x = axisSq.x + cosine * (1.0f - axisSq.x);
- Row1.y = axisSq.y + cosine * (1.0f - axisSq.y);
- Row2.z = axisSq.z + cosine * (1.0f - axisSq.z);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+
+ Row0.x = axisSq.x + cos * (1.0f - axisSq.x);
+ Row1.y = axisSq.y + cos * (1.0f - axisSq.y);
+ Row2.z = axisSq.z + cos * (1.0f - axisSq.z);
- real_t sine = Mathf.Sin(phi);
- real_t t = 1.0f - cosine;
+ real_t t = 1.0f - cos;
real_t xyzt = axis.x * axis.y * t;
- real_t zyxs = axis.z * sine;
+ real_t zyxs = axis.z * sin;
Row0.y = xyzt - zyxs;
Row1.x = xyzt + zyxs;
xyzt = axis.x * axis.z * t;
- zyxs = axis.y * sine;
+ zyxs = axis.y * sin;
Row0.z = xyzt + zyxs;
Row2.x = xyzt - zyxs;
xyzt = axis.y * axis.z * t;
- zyxs = axis.x * sine;
+ zyxs = axis.x * sin;
Row1.z = xyzt - zyxs;
Row2.y = xyzt + zyxs;
}
@@ -819,8 +858,20 @@ namespace Godot
// We need to assign the struct fields here first so we can't do it that way...
}
- // Arguments are named such that xy is equal to calling x.y
- internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
+ /// <summary>
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
+ /// </summary>
+ /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param>
+ public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz)
{
Row0 = new Vector3(xx, yx, zx);
Row1 = new Vector3(xy, yy, zy);
@@ -828,6 +879,72 @@ namespace Godot
}
/// <summary>
+ /// Constructs a Basis matrix from Euler angles in the specified rotation order. By default, use YXZ order (most common).
+ /// </summary>
+ /// <param name="euler">The Euler angles to use.</param>
+ /// <param name="order">The order to compose the Euler angles.</param>
+ public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz)
+ {
+ (real_t sin, real_t cos) = Mathf.SinCos(euler.x);
+ Basis xmat = new Basis
+ (
+ new Vector3(1, 0, 0),
+ new Vector3(0, cos, sin),
+ new Vector3(0, -sin, cos)
+ );
+
+ (sin, cos) = Mathf.SinCos(euler.y);
+ Basis ymat = new Basis
+ (
+ new Vector3(cos, 0, -sin),
+ new Vector3(0, 1, 0),
+ new Vector3(sin, 0, cos)
+ );
+
+ (sin, cos) = Mathf.SinCos(euler.z);
+ Basis zmat = new Basis
+ (
+ new Vector3(cos, sin, 0),
+ new Vector3(-sin, cos, 0),
+ new Vector3(0, 0, 1)
+ );
+
+ switch (order)
+ {
+ case EulerOrder.Xyz:
+ return xmat * ymat * zmat;
+ case EulerOrder.Xzy:
+ return xmat * zmat * ymat;
+ case EulerOrder.Yxz:
+ return ymat * xmat * zmat;
+ case EulerOrder.Yzx:
+ return ymat * zmat * xmat;
+ case EulerOrder.Zxy:
+ return zmat * xmat * ymat;
+ case EulerOrder.Zyx:
+ return zmat * ymat * xmat;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(order));
+ }
+ }
+
+ /// <summary>
+ /// Constructs a pure scale basis matrix with no rotation or shearing.
+ /// The scale values are set as the main diagonal of the matrix,
+ /// and all of the other parts of the matrix are zero.
+ /// </summary>
+ /// <param name="scale">The scale Vector3.</param>
+ /// <returns>A pure scale Basis matrix.</returns>
+ public static Basis FromScale(Vector3 scale)
+ {
+ return new Basis(
+ scale.x, 0, 0,
+ 0, scale.y, 0,
+ 0, 0, scale.z
+ );
+ }
+
+ /// <summary>
/// Composes these two basis matrices by multiplying them
/// together. This has the effect of transforming the second basis
/// (the child) by the first basis (the parent).
@@ -846,6 +963,41 @@ namespace Godot
}
/// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the basis matrix.
+ /// </summary>
+ /// <param name="basis">The basis matrix transformation to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Basis basis, Vector3 vector)
+ {
+ return new Vector3
+ (
+ basis.Row0.Dot(vector),
+ basis.Row1.Dot(vector),
+ basis.Row2.Dot(vector)
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transposed basis matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// basis matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely transform.</param>
+ /// <param name="basis">The basis matrix transformation to apply.</param>
+ /// <returns>The inversely transformed vector.</returns>
+ public static Vector3 operator *(Vector3 vector, Basis basis)
+ {
+ return new Vector3
+ (
+ basis.Row0[0] * vector.x + basis.Row1[0] * vector.y + basis.Row2[0] * vector.z,
+ basis.Row0[1] * vector.x + basis.Row1[1] * vector.y + basis.Row2[1] * vector.z,
+ basis.Row0[2] * vector.x + basis.Row1[2] * vector.y + basis.Row2[2] * vector.z
+ );
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the basis matrices are exactly
/// equal. Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
@@ -879,14 +1031,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the basis matrix and the object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Basis)
- {
- return Equals((Basis)obj);
- }
-
- return false;
+ return obj is Basis other && Equals(other);
}
/// <summary>
@@ -896,7 +1043,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other basis.</param>
/// <returns>Whether or not the basis matrices are exactly equal.</returns>
- public bool Equals(Basis other)
+ public readonly bool Equals(Basis other)
{
return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
@@ -907,7 +1054,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other basis to compare.</param>
/// <returns>Whether or not the bases are approximately equal.</returns>
- public bool IsEqualApprox(Basis other)
+ public readonly bool IsEqualApprox(Basis other)
{
return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2);
}
@@ -916,7 +1063,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Basis"/>.
/// </summary>
/// <returns>A hash code for this basis.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode();
}
@@ -925,7 +1072,7 @@ namespace Godot
/// Converts this <see cref="Basis"/> to a string.
/// </summary>
/// <returns>A string representation of this basis.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"[X: {x}, Y: {y}, Z: {z}]";
}
@@ -934,7 +1081,7 @@ namespace Godot
/// Converts this <see cref="Basis"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this basis.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, Z: {z.ToString(format)}]";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
new file mode 100644
index 0000000000..ac2e2fae3c
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/AlcReloadCfg.cs
@@ -0,0 +1,18 @@
+namespace Godot.Bridge;
+
+public static class AlcReloadCfg
+{
+ private static bool _configured = false;
+
+ public static void Configure(bool alcReloadEnabled)
+ {
+ if (_configured)
+ return;
+
+ _configured = true;
+
+ IsAlcReloadingEnabled = alcReloadEnabled;
+ }
+
+ internal static bool IsAlcReloadingEnabled = false;
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
new file mode 100644
index 0000000000..354212da1b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/CSharpInstanceBridge.cs
@@ -0,0 +1,252 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class CSharpInstanceBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Call(IntPtr godotObjectGCHandle, godot_string_name* method,
+ godot_variant** args, int argCount, godot_variant_call_error* refCallError, godot_variant* ret)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ {
+ *ret = default;
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL;
+ return godot_bool.False;
+ }
+
+ bool methodInvoked = godotObject.InvokeGodotClassMethod(CustomUnsafe.AsRef(method),
+ new NativeVariantPtrArgs(args, argCount), out godot_variant retValue);
+
+ if (!methodInvoked)
+ {
+ *ret = default;
+ // This is important, as it tells Object::call that no method was called.
+ // Otherwise, it would prevent Object::call from calling native methods.
+ (*refCallError).Error = godot_variant_call_error_error.GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD;
+ return godot_bool.False;
+ }
+
+ *ret = retValue;
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *ret = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Set(IntPtr godotObjectGCHandle, godot_string_name* name, godot_variant* value)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.SetGodotClassPropertyValue(CustomUnsafe.AsRef(name), CustomUnsafe.AsRef(value)))
+ {
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant valueManaged = Variant.CreateCopyingBorrowed(*value);
+
+ return godotObject._Set(nameManaged, valueManaged).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool Get(IntPtr godotObjectGCHandle, godot_string_name* name,
+ godot_variant* outRet)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ throw new InvalidOperationException();
+
+ if (godotObject.GetGodotClassPropertyValue(CustomUnsafe.AsRef(name), out godot_variant outRetValue))
+ {
+ *outRet = outRetValue;
+ return godot_bool.True;
+ }
+
+ var nameManaged = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(name)));
+
+ Variant ret = godotObject._Get(nameManaged);
+
+ if (ret.VariantType == Variant.Type.Nil)
+ {
+ *outRet = default;
+ return godot_bool.False;
+ }
+
+ *outRet = ret.CopyNativeVariant();
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void CallDispose(IntPtr godotObjectGCHandle, godot_bool okIfNull)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (okIfNull.ToBool())
+ godotObject?.Dispose();
+ else
+ godotObject!.Dispose();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void CallToString(IntPtr godotObjectGCHandle, godot_string* outRes, godot_bool* outValid)
+ {
+ try
+ {
+ var self = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (self == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ var resultStr = self.ToString();
+
+ if (resultStr == null)
+ {
+ *outRes = default;
+ *outValid = godot_bool.False;
+ return;
+ }
+
+ *outRes = Marshaling.ConvertStringToNative(resultStr);
+ *outValid = godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ *outValid = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool HasMethodUnknownParams(IntPtr godotObjectGCHandle, godot_string_name* method)
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return godot_bool.False;
+
+ return godotObject.HasGodotClassMethod(CustomUnsafe.AsRef(method)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void SerializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Call OnBeforeSerialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnBeforeSerialize();
+
+ // Save instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.SaveGodotObjectData(info);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void DeserializeState(
+ IntPtr godotObjectGCHandle,
+ godot_dictionary* propertiesState,
+ godot_dictionary* signalEventsState
+ )
+ {
+ try
+ {
+ var godotObject = (Object)GCHandle.FromIntPtr(godotObjectGCHandle).Target;
+
+ if (godotObject == null)
+ return;
+
+ // Restore instance state
+
+ using var info = GodotSerializationInfo.CreateCopyingBorrowed(
+ *propertiesState, *signalEventsState);
+
+ godotObject.RestoreGodotObjectData(info);
+
+ // Call OnAfterDeserialize
+
+ // ReSharper disable once SuspiciousTypeConversion.Global
+ if (godotObject is ISerializationListener serializationListener)
+ serializationListener.OnAfterDeserialize();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
new file mode 100644
index 0000000000..456a118b90
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ internal static class GCHandleBridge
+ {
+ [UnmanagedCallersOnly]
+ internal static void FreeGCHandle(IntPtr gcHandlePtr)
+ {
+ try
+ {
+ CustomGCHandle.Free(GCHandle.FromIntPtr(gcHandlePtr));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
new file mode 100644
index 0000000000..6d20f95007
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GodotSerializationInfo.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge;
+
+public sealed class GodotSerializationInfo : IDisposable
+{
+ private readonly Collections.Dictionary _properties;
+ private readonly Collections.Dictionary _signalEvents;
+
+ public void Dispose()
+ {
+ _properties?.Dispose();
+ _signalEvents?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ _properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties);
+ _signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents);
+ }
+
+ internal static GodotSerializationInfo CreateCopyingBorrowed(
+ in godot_dictionary properties, in godot_dictionary signalEvents)
+ {
+ return new(NativeFuncs.godotsharp_dictionary_new_copy(properties),
+ NativeFuncs.godotsharp_dictionary_new_copy(signalEvents));
+ }
+
+ public void AddProperty(StringName name, Variant value)
+ {
+ _properties[name] = value;
+ }
+
+ public bool TryGetProperty(StringName name, out Variant value)
+ {
+ return _properties.TryGetValue(name, out value);
+ }
+
+ public void AddSignalEventDelegate(StringName name, Delegate eventDelegate)
+ {
+ var serializedData = new Collections.Array();
+
+ if (DelegateUtils.TrySerializeDelegate(eventDelegate, serializedData))
+ {
+ _signalEvents[name] = serializedData;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to serialize event signal delegate: {name}");
+ }
+ }
+
+ public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
+ where T : Delegate
+ {
+ if (_signalEvents.TryGetValue(name, out Variant serializedData))
+ {
+ if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate))
+ {
+ value = eventDelegate as T;
+
+ if (value == null)
+ {
+ Console.WriteLine($"Cannot cast the deserialized event signal delegate: {name}. " +
+ $"Expected '{typeof(T).FullName}'; got '{eventDelegate.GetType().FullName}'.");
+ return false;
+ }
+
+ return true;
+ }
+ else if (OS.IsStdoutVerbose())
+ {
+ Console.WriteLine($"Failed to deserialize event signal delegate: {name}");
+ }
+
+ value = null;
+ return false;
+ }
+
+ value = null;
+ return false;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
new file mode 100644
index 0000000000..44ea8fc83d
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public unsafe struct ManagedCallbacks
+ {
+ // @formatter:off
+ public delegate* unmanaged<IntPtr, godot_variant**, int, godot_bool*, void> SignalAwaiter_SignalCallback;
+ public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
+ public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
+ public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
+ public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
+ public delegate* unmanaged<godot_string_name*, IntPtr, IntPtr> ScriptManagerBridge_CreateManagedForGodotObjectBinding;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_variant**, int, godot_bool> ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
+ public delegate* unmanaged<IntPtr, godot_string_name*, void> ScriptManagerBridge_GetScriptNativeName;
+ public delegate* unmanaged<IntPtr, IntPtr, void> ScriptManagerBridge_SetGodotObjectPtr;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_bool*, void> ScriptManagerBridge_RaiseEventSignal;
+ public delegate* unmanaged<IntPtr, IntPtr, godot_bool> ScriptManagerBridge_ScriptIsOrInherits;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool> ScriptManagerBridge_AddScriptBridge;
+ public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
+ public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
+ public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
+ public delegate* unmanaged<IntPtr, godot_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+ public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
+ public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant**, int, godot_variant_call_error*, godot_variant*, godot_bool> CSharpInstanceBridge_Call;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Set;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_variant*, godot_bool> CSharpInstanceBridge_Get;
+ public delegate* unmanaged<IntPtr, godot_bool, void> CSharpInstanceBridge_CallDispose;
+ public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, void> CSharpInstanceBridge_CallToString;
+ public delegate* unmanaged<IntPtr, godot_string_name*, godot_bool> CSharpInstanceBridge_HasMethodUnknownParams;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState;
+ public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState;
+ public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
+ public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo;
+ public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
+ public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded;
+ // @formatter:on
+
+ public static ManagedCallbacks Create()
+ {
+ return new()
+ {
+ // @formatter:off
+ SignalAwaiter_SignalCallback = &SignalAwaiter.SignalCallback,
+ DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
+ DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
+ DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
+ DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
+ ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
+ ScriptManagerBridge_CreateManagedForGodotObjectBinding = &ScriptManagerBridge.CreateManagedForGodotObjectBinding,
+ ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = &ScriptManagerBridge.CreateManagedForGodotObjectScriptInstance,
+ ScriptManagerBridge_GetScriptNativeName = &ScriptManagerBridge.GetScriptNativeName,
+ ScriptManagerBridge_SetGodotObjectPtr = &ScriptManagerBridge.SetGodotObjectPtr,
+ ScriptManagerBridge_RaiseEventSignal = &ScriptManagerBridge.RaiseEventSignal,
+ ScriptManagerBridge_ScriptIsOrInherits = &ScriptManagerBridge.ScriptIsOrInherits,
+ ScriptManagerBridge_AddScriptBridge = &ScriptManagerBridge.AddScriptBridge,
+ ScriptManagerBridge_GetOrCreateScriptBridgeForPath = &ScriptManagerBridge.GetOrCreateScriptBridgeForPath,
+ ScriptManagerBridge_RemoveScriptBridge = &ScriptManagerBridge.RemoveScriptBridge,
+ ScriptManagerBridge_TryReloadRegisteredScriptWithClass = &ScriptManagerBridge.TryReloadRegisteredScriptWithClass,
+ ScriptManagerBridge_UpdateScriptClassInfo = &ScriptManagerBridge.UpdateScriptClassInfo,
+ ScriptManagerBridge_SwapGCHandleForType = &ScriptManagerBridge.SwapGCHandleForType,
+ ScriptManagerBridge_GetPropertyInfoList = &ScriptManagerBridge.GetPropertyInfoList,
+ ScriptManagerBridge_GetPropertyDefaultValues = &ScriptManagerBridge.GetPropertyDefaultValues,
+ CSharpInstanceBridge_Call = &CSharpInstanceBridge.Call,
+ CSharpInstanceBridge_Set = &CSharpInstanceBridge.Set,
+ CSharpInstanceBridge_Get = &CSharpInstanceBridge.Get,
+ CSharpInstanceBridge_CallDispose = &CSharpInstanceBridge.CallDispose,
+ CSharpInstanceBridge_CallToString = &CSharpInstanceBridge.CallToString,
+ CSharpInstanceBridge_HasMethodUnknownParams = &CSharpInstanceBridge.HasMethodUnknownParams,
+ CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState,
+ CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState,
+ GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
+ DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo,
+ DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
+ GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded,
+ // @formatter:on
+ };
+ }
+
+ public static void Create(IntPtr outManagedCallbacks)
+ => *(ManagedCallbacks*)outManagedCallbacks = Create();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
new file mode 100644
index 0000000000..85d38f9727
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/MethodInfo.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public readonly struct MethodInfo
+{
+ public StringName Name { get; init; }
+ public PropertyInfo ReturnVal { get; init; }
+ public MethodFlags Flags { get; init; }
+ public int Id { get; init; } = 0;
+ public List<PropertyInfo>? Arguments { get; init; }
+ public List<Variant>? DefaultArguments { get; init; }
+
+ public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags,
+ List<PropertyInfo>? arguments, List<Variant>? defaultArguments)
+ {
+ Name = name;
+ ReturnVal = returnVal;
+ Flags = flags;
+ Arguments = arguments;
+ DefaultArguments = defaultArguments;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
new file mode 100644
index 0000000000..0f447b93c8
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/PropertyInfo.cs
@@ -0,0 +1,24 @@
+namespace Godot.Bridge;
+
+#nullable enable
+
+public readonly struct PropertyInfo
+{
+ public Variant.Type Type { get; init; }
+ public StringName Name { get; init; }
+ public PropertyHint Hint { get; init; }
+ public string HintString { get; init; }
+ public PropertyUsageFlags Usage { get; init; }
+ public bool Exported { get; init; }
+
+ public PropertyInfo(Variant.Type type, StringName name, PropertyHint hint, string hintString,
+ PropertyUsageFlags usage, bool exported)
+ {
+ Type = type;
+ Name = name;
+ Hint = hint;
+ HintString = hintString;
+ Usage = usage;
+ Exported = exported;
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
new file mode 100644
index 0000000000..dafa83431b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -0,0 +1,1052 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.Runtime.Serialization;
+using Godot.NativeInterop;
+
+namespace Godot.Bridge
+{
+ // TODO: Make class internal once we replace LookupScriptsInAssembly (the only public member) with source generators
+ public static partial class ScriptManagerBridge
+ {
+ private static ConcurrentDictionary<AssemblyLoadContext, ConcurrentDictionary<Type, byte>>
+ _alcData = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ if (_alcData.TryRemove(alc, out var typesInAlc))
+ {
+ foreach (var type in typesInAlc.Keys)
+ {
+ if (_scriptTypeBiMap.RemoveByScriptType(type, out IntPtr scriptPtr) &&
+ !_pathTypeBiMap.TryGetScriptPath(type, out _))
+ {
+ // For scripts without a path, we need to keep the class qualified name for reloading
+ _scriptDataForReload.TryAdd(scriptPtr,
+ (type.Assembly.GetName().Name, type.FullName ?? type.ToString()));
+ }
+
+ _pathTypeBiMap.RemoveByScriptType(type);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void AddTypeForAlcReloading(Type type)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(type.Assembly);
+ if (alc == null)
+ return;
+
+ var typesInAlc = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ typesInAlc.TryAdd(type, 0);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void TrackAlcForUnloading(AssemblyLoadContext alc)
+ {
+ _ = _alcData.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ }
+
+ private static ScriptTypeBiMap _scriptTypeBiMap = new();
+ private static PathScriptTypeBiMap _pathTypeBiMap = new();
+
+ private static ConcurrentDictionary<IntPtr, (string? assemblyName, string classFullName)>
+ _scriptDataForReload = new();
+
+ [UnmanagedCallersOnly]
+ internal static void FrameCallback()
+ {
+ try
+ {
+ Dispatcher.DefaultGodotTaskScheduler?.Activate();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
+ IntPtr godotObject)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName)));
+ string nativeTypeNameStr = stringName.ToString();
+
+ Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException(
+ "Wrapper class not found for type: " + nativeTypeNameStr);
+ var obj = (Object)FormatterServices.GetUninitializedObject(nativeType);
+
+ var ctor = nativeType.GetConstructor(
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
+ null, Type.EmptyTypes, null);
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor!.Invoke(obj, null);
+
+ return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return IntPtr.Zero;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool CreateManagedForGodotObjectScriptInstance(IntPtr scriptPtr,
+ IntPtr godotObject,
+ godot_variant** args, int argCount)
+ {
+ // TODO: Optimize with source generators and delegate pointers
+
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+
+ var ctor = scriptType
+ .GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(c => c.GetParameters().Length == argCount)
+ .FirstOrDefault();
+
+ if (ctor == null)
+ {
+ if (argCount == 0)
+ {
+ throw new MissingMemberException(
+ $"Cannot create script instance. The class '{scriptType.FullName}' does not define a parameterless constructor.");
+ }
+ else
+ {
+ throw new MissingMemberException(
+ $"The class '{scriptType.FullName}' does not define a constructor that takes x parameters.");
+ }
+ }
+
+ var obj = (Object)FormatterServices.GetUninitializedObject(scriptType);
+
+ var parameters = ctor.GetParameters();
+ int paramCount = parameters.Length;
+
+ var invokeParams = new object?[paramCount];
+
+ for (int i = 0; i < paramCount; i++)
+ {
+ invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType(
+ *args[i], parameters[i].ParameterType);
+ }
+
+ obj.NativePtr = godotObject;
+
+ _ = ctor.Invoke(obj, invokeParams);
+
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetScriptNativeName(IntPtr scriptPtr, godot_string_name* outRes)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ {
+ *outRes = default;
+ return;
+ }
+
+ var native = Object.InternalGetClassNativeBase(scriptType);
+
+ var field = native?.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.Public | BindingFlags.NonPublic);
+
+ if (field == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ var nativeName = (StringName?)field.GetValue(null);
+
+ if (nativeName == null)
+ {
+ *outRes = default;
+ return;
+ }
+
+ *outRes = NativeFuncs.godotsharp_string_name_new_copy((godot_string_name)nativeName.NativeValue);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRes = default;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void SetGodotObjectPtr(IntPtr gcHandlePtr, IntPtr newPtr)
+ {
+ try
+ {
+ var target = (Object?)GCHandle.FromIntPtr(gcHandlePtr).Target;
+ if (target != null)
+ target.NativePtr = newPtr;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static Type? TypeGetProxyClass(string nativeTypeNameStr)
+ {
+ // Performance is not critical here as this will be replaced with a generated dictionary.
+
+ if (nativeTypeNameStr[0] == '_')
+ nativeTypeNameStr = nativeTypeNameStr.Substring(1);
+
+ Type? wrapperType = typeof(Object).Assembly.GetType("Godot." + nativeTypeNameStr);
+
+ if (wrapperType == null)
+ {
+ wrapperType = AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor")?
+ .GetType("Godot." + nativeTypeNameStr);
+ }
+
+ static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
+
+ if (wrapperType != null && IsStatic(wrapperType))
+ {
+ // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
+ return typeof(Object);
+ }
+
+ return wrapperType;
+ }
+
+ // Called from GodotPlugins
+ // ReSharper disable once UnusedMember.Local
+ public static void LookupScriptsInAssembly(Assembly assembly)
+ {
+ static void LookupScriptForClass(Type type)
+ {
+ var scriptPathAttr = type.GetCustomAttributes(inherit: false)
+ .OfType<ScriptPathAttribute>()
+ .FirstOrDefault();
+
+ if (scriptPathAttr == null)
+ return;
+
+ _pathTypeBiMap.Add(scriptPathAttr.Path, type);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(type);
+ }
+ }
+
+ var assemblyHasScriptsAttr = assembly.GetCustomAttributes(inherit: false)
+ .OfType<AssemblyHasScriptsAttribute>()
+ .FirstOrDefault();
+
+ if (assemblyHasScriptsAttr == null)
+ return;
+
+ if (assemblyHasScriptsAttr.RequiresLookup)
+ {
+ // This is supported for scenarios where specifying all types would be cumbersome,
+ // such as when disabling C# source generators (for whatever reason) or when using a
+ // language other than C# that has nothing similar to source generators to automate it.
+
+ var typeOfGodotObject = typeof(Object);
+
+ foreach (var type in assembly.GetTypes())
+ {
+ if (type.IsNested || type.IsGenericType)
+ continue;
+
+ if (!typeOfGodotObject.IsAssignableFrom(type))
+ continue;
+
+ LookupScriptForClass(type);
+ }
+ }
+ else
+ {
+ // This is the most likely scenario as we use C# source generators
+
+ var scriptTypes = assemblyHasScriptsAttr.ScriptTypes;
+
+ if (scriptTypes != null)
+ {
+ foreach (var type in scriptTypes)
+ {
+ if (type.IsGenericType)
+ continue;
+
+ LookupScriptForClass(type);
+ }
+ }
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void RaiseEventSignal(IntPtr ownerGCHandlePtr,
+ godot_string_name* eventSignalName, godot_variant** args, int argCount, godot_bool* outOwnerIsNull)
+ {
+ try
+ {
+ var owner = (Object?)GCHandle.FromIntPtr(ownerGCHandlePtr).Target;
+
+ if (owner == null)
+ {
+ *outOwnerIsNull = godot_bool.True;
+ return;
+ }
+
+ *outOwnerIsNull = godot_bool.False;
+
+ owner.RaiseGodotClassSignalCallbacks(CustomUnsafe.AsRef(eventSignalName),
+ new NativeVariantPtrArgs(args, argCount));
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outOwnerIsNull = godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool ScriptIsOrInherits(IntPtr scriptPtr, IntPtr scriptPtrMaybeBase)
+ {
+ try
+ {
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtr, out Type? scriptType))
+ return godot_bool.False;
+
+ if (!_scriptTypeBiMap.TryGetScriptType(scriptPtrMaybeBase, out Type? maybeBaseType))
+ return godot_bool.False;
+
+ return (scriptType == maybeBaseType || maybeBaseType.IsAssignableFrom(scriptType)).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (!_scriptTypeBiMap.IsScriptRegistered(scriptPtr))
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ return godot_bool.False;
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+ }
+ }
+
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetOrCreateScriptBridgeForPath(godot_string* scriptPath, godot_ref* outScript)
+ {
+ string scriptPathStr = Marshaling.ConvertStringToManaged(*scriptPath);
+
+ if (!_pathTypeBiMap.TryGetScriptType(scriptPathStr, out Type? scriptType))
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ return;
+ }
+
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+
+ private static unsafe void GetOrCreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ return;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+ CreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+
+ internal static unsafe void GetOrLoadOrCreateScriptForType(Type scriptType, godot_ref* outScript)
+ {
+ static bool GetPathOtherwiseGetOrCreateScript(Type scriptType, godot_ref* outScript,
+ [MaybeNullWhen(false)] out string scriptPath)
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptPtr(scriptType, out IntPtr scriptPtr))
+ {
+ // Use existing
+ NativeFuncs.godotsharp_ref_new_from_ref_counted_ptr(out *outScript, scriptPtr);
+ scriptPath = null;
+ return false;
+ }
+
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ if (_pathTypeBiMap.TryGetScriptPath(scriptType, out scriptPath))
+ return true;
+
+ CreateScriptBridgeForType(scriptType, outScript);
+ scriptPath = null;
+ return false;
+ }
+ }
+
+ if (GetPathOtherwiseGetOrCreateScript(scriptType, outScript, out string? scriptPath))
+ {
+ // This path is slower, but it's only executed for the first instantiation of the type
+
+ // This must be done outside the read-write lock, as the script resource loading can lock it
+ using godot_string scriptPathIn = Marshaling.ConvertStringToNative(scriptPath);
+ if (!NativeFuncs.godotsharp_internal_script_load(scriptPathIn, outScript).ToBool())
+ {
+ GD.PushError($"Cannot load script for type '{scriptType.FullName}'. Path: '{scriptPath}'.");
+
+ // If loading of the script fails, best we can do create a new script
+ // with no path, as we do for types without an associated script file.
+ GetOrCreateScriptBridgeForType(scriptType, outScript);
+ }
+ }
+ }
+
+ private static unsafe void CreateScriptBridgeForType(Type scriptType, godot_ref* outScript)
+ {
+ NativeFuncs.godotsharp_internal_new_csharp_script(outScript);
+ IntPtr scriptPtr = outScript->Reference;
+
+ // Caller takes care of locking
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void RemoveScriptBridge(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ _scriptTypeBiMap.Remove(scriptPtr);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static godot_bool TryReloadRegisteredScriptWithClass(IntPtr scriptPtr)
+ {
+ try
+ {
+ lock (_scriptTypeBiMap.ReadWriteLock)
+ {
+ if (_scriptTypeBiMap.TryGetScriptType(scriptPtr, out _))
+ {
+ // NOTE:
+ // Currently, we reload all scripts, not only the ones from the unloaded ALC.
+ // As such, we need to handle this case instead of treating it as an error.
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+ return godot_bool.True;
+ }
+
+ if (!_scriptDataForReload.TryGetValue(scriptPtr, out var dataForReload))
+ {
+ GD.PushError("Missing class qualified name for reloading script");
+ return godot_bool.False;
+ }
+
+ _ = _scriptDataForReload.TryRemove(scriptPtr, out _);
+
+ if (dataForReload.assemblyName == null)
+ {
+ GD.PushError(
+ $"Missing assembly name of class '{dataForReload.classFullName}' for reloading script");
+ return godot_bool.False;
+ }
+
+ var scriptType = ReflectionUtils.FindTypeInLoadedAssemblies(dataForReload.assemblyName,
+ dataForReload.classFullName);
+
+ if (scriptType == null)
+ {
+ // The class was removed, can't reload
+ return godot_bool.False;
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ if (!typeof(Godot.Object).IsAssignableFrom(scriptType))
+ {
+ // The class no longer inherits Godot.Object, can't reload
+ return godot_bool.False;
+ }
+
+ _scriptTypeBiMap.Add(scriptPtr, scriptType);
+
+ NativeFuncs.godotsharp_internal_reload_registered_script(scriptPtr);
+
+ return godot_bool.True;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
+ godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
+ godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
+ {
+ try
+ {
+ // Performance is not critical here as this will be replaced with source generators.
+ var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+
+ *outTool = scriptType.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any().ToGodotBool();
+
+ if (!(*outTool).ToBool() && scriptType.IsNested)
+ {
+ *outTool = (scriptType.DeclaringType?.GetCustomAttributes(inherit: false)
+ .OfType<ToolAttribute>()
+ .Any() ?? false).ToGodotBool();
+ }
+
+ if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
+ *outTool = godot_bool.True;
+
+ // Methods
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var methods = new Collections.Array();
+
+ Type? top = scriptType;
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ var methodList = GetMethodListForType(top);
+
+ if (methodList != null)
+ {
+ foreach (var method in methodList)
+ {
+ var methodInfo = new Collections.Dictionary();
+
+ methodInfo.Add("name", method.Name);
+
+ var methodParams = new Collections.Array();
+
+ if (method.Arguments != null)
+ {
+ foreach (var param in method.Arguments)
+ {
+ methodParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ methodInfo.Add("params", methodParams);
+
+ methods.Add(methodInfo);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outMethodsDest = NativeFuncs.godotsharp_array_new_copy(
+ (godot_array)methods.NativeValue);
+
+ // RPC functions
+
+ Collections.Dictionary rpcFunctions = new();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ foreach (var method in top.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance |
+ BindingFlags.NonPublic | BindingFlags.Public))
+ {
+ if (method.IsStatic)
+ continue;
+
+ string methodName = method.Name;
+
+ if (rpcFunctions.ContainsKey(methodName))
+ continue;
+
+ var rpcAttr = method.GetCustomAttributes(inherit: false)
+ .OfType<RPCAttribute>().FirstOrDefault();
+
+ if (rpcAttr == null)
+ continue;
+
+ var rpcConfig = new Collections.Dictionary();
+
+ rpcConfig["rpc_mode"] = (long)rpcAttr.Mode;
+ rpcConfig["call_local"] = rpcAttr.CallLocal;
+ rpcConfig["transfer_mode"] = (long)rpcAttr.TransferMode;
+ rpcConfig["channel"] = rpcAttr.TransferChannel;
+
+ rpcFunctions.Add(methodName, rpcConfig);
+ }
+
+ top = top.BaseType;
+ }
+
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)(rpcFunctions).NativeValue);
+
+ // Event signals
+
+ // Performance is not critical here as this will be replaced with source generators.
+ using var signals = new Collections.Dictionary();
+
+ top = scriptType;
+
+ while (top != null && top != native)
+ {
+ var signalList = GetSignalListForType(top);
+
+ if (signalList != null)
+ {
+ foreach (var signal in signalList)
+ {
+ string signalName = signal.Name;
+
+ if (signals.ContainsKey(signalName))
+ continue;
+
+ var signalParams = new Collections.Array();
+
+ if (signal.Arguments != null)
+ {
+ foreach (var param in signal.Arguments)
+ {
+ signalParams.Add(new Collections.Dictionary()
+ {
+ { "name", param.Name },
+ { "type", (int)param.Type },
+ { "usage", (int)param.Usage }
+ });
+ }
+ }
+
+ signals.Add(signalName, signalParams);
+ }
+ }
+
+ top = top.BaseType;
+ }
+
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new_copy(
+ (godot_dictionary)signals.NativeValue);
+
+ // Base script
+
+ var baseType = scriptType.BaseType;
+ if (baseType != null && baseType != native)
+ {
+ GetOrLoadOrCreateScriptForType(baseType, outBaseScript);
+ }
+ else
+ {
+ *outBaseScript = default;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outTool = godot_bool.False;
+ *outMethodsDest = NativeFuncs.godotsharp_array_new();
+ *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
+ *outBaseScript = default;
+ }
+ }
+
+ private static List<MethodInfo>? GetSignalListForType(Type type)
+ {
+ var getGodotSignalListMethod = type.GetMethod(
+ "GetGodotSignalList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotSignalListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotSignalListMethod.Invoke(null, null);
+ }
+
+ private static List<MethodInfo>? GetMethodListForType(Type type)
+ {
+ var getGodotMethodListMethod = type.GetMethod(
+ "GetGodotMethodList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotMethodListMethod == null)
+ return null;
+
+ return (List<MethodInfo>?)getGodotMethodListMethod.Invoke(null, null);
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_info
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_string HintString;
+ public int Type;
+ public int Hint;
+ public int Usage;
+ public godot_bool Exported;
+
+ public void Dispose()
+ {
+ HintString.Dispose();
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyInfoList(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ Type scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ GetPropertyInfoListForType(scriptType, scriptPtr, addPropInfoFunc);
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static unsafe void GetPropertyInfoListForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, godot_string*, void*, int, void> addPropInfoFunc)
+ {
+ try
+ {
+ var getGodotPropertyListMethod = type.GetMethod(
+ "GetGodotPropertyList",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyListMethod == null)
+ return;
+
+ var properties = (List<PropertyInfo>?)
+ getGodotPropertyListMethod.Invoke(null, null);
+
+ if (properties == null || properties.Count <= 0)
+ return;
+
+ int length = properties.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_info)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_info* interopProperties;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_info[stackMaxLength];
+ interopProperties = aux;
+ }
+ else
+ {
+ interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_info)))!;
+ }
+
+ try
+ {
+ for (int i = 0; i < length; i++)
+ {
+ var property = properties[i];
+
+ godotsharp_property_info interopProperty = new()
+ {
+ Type = (int)property.Type,
+ Name = (godot_string_name)property.Name.NativeValue, // Not owned
+ Hint = (int)property.Hint,
+ HintString = Marshaling.ConvertStringToNative(property.HintString),
+ Usage = (int)property.Usage,
+ Exported = property.Exported.ToGodotBool()
+ };
+
+ interopProperties[i] = interopProperty;
+ }
+
+ using godot_string currentClassName = Marshaling.ConvertStringToNative(type.Name);
+
+ addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
+
+ // We're borrowing the native value of the StringName entries.
+ // The dictionary needs to be kept alive until `addPropInfoFunc` returns.
+ GC.KeepAlive(properties);
+ }
+ finally
+ {
+ for (int i = 0; i < length; i++)
+ interopProperties[i].Dispose();
+
+ if (!useStack)
+ NativeMemory.Free(interopProperties);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ // ReSharper disable once InconsistentNaming
+ [SuppressMessage("ReSharper", "NotAccessedField.Local")]
+ [StructLayout(LayoutKind.Sequential)]
+ private ref struct godotsharp_property_def_val_pair
+ {
+ // Careful with padding...
+ public godot_string_name Name; // Not owned
+ public godot_variant Value; // Not owned
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetPropertyDefaultValues(IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ Type? top = _scriptTypeBiMap.GetScriptType(scriptPtr);
+ Type native = Object.InternalGetClassNativeBase(top);
+
+ while (top != null && top != native)
+ {
+ GetPropertyDefaultValuesForType(top, scriptPtr, addDefValFunc);
+
+ top = top.BaseType;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [SkipLocalsInit]
+ private static unsafe void GetPropertyDefaultValuesForType(Type type, IntPtr scriptPtr,
+ delegate* unmanaged<IntPtr, void*, int, void> addDefValFunc)
+ {
+ try
+ {
+ var getGodotPropertyDefaultValuesMethod = type.GetMethod(
+ "GetGodotPropertyDefaultValues",
+ BindingFlags.DeclaredOnly | BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (getGodotPropertyDefaultValuesMethod == null)
+ return;
+
+ var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null);
+
+ if (defaultValuesObj == null)
+ return;
+
+ Dictionary<StringName, Variant> defaultValues;
+
+ if (defaultValuesObj is Dictionary<StringName, object> defaultValuesLegacy)
+ {
+ // We have to support this for some time, otherwise this could cause data loss for projects
+ // built with previous releases. Ideally, we should remove this before Godot 4.0 stable.
+
+ if (defaultValuesLegacy.Count <= 0)
+ return;
+
+ defaultValues = new();
+
+ foreach (var pair in defaultValuesLegacy)
+ {
+ defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue(
+ DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value));
+ }
+ }
+ else
+ {
+ defaultValues = (Dictionary<StringName, Variant>)defaultValuesObj;
+ }
+
+ if (defaultValues.Count <= 0)
+ return;
+
+ int length = defaultValues.Count;
+
+ // There's no recursion here, so it's ok to go with a big enough number for most cases
+ // stackMaxSize = stackMaxLength * sizeof(godotsharp_property_def_val_pair)
+ const int stackMaxLength = 32;
+ bool useStack = length < stackMaxLength;
+
+ godotsharp_property_def_val_pair* interopDefaultValues;
+
+ if (useStack)
+ {
+ // Weird limitation, hence the need for aux:
+ // "In the case of pointer types, you can use a stackalloc expression only in a local variable declaration to initialize the variable."
+ var aux = stackalloc godotsharp_property_def_val_pair[stackMaxLength];
+ interopDefaultValues = aux;
+ }
+ else
+ {
+ interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc(
+ (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!;
+ }
+
+ try
+ {
+ int i = 0;
+ foreach (var defaultValuePair in defaultValues)
+ {
+ godotsharp_property_def_val_pair interopProperty = new()
+ {
+ Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned
+ Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned
+ };
+
+ interopDefaultValues[i] = interopProperty;
+
+ i++;
+ }
+
+ addDefValFunc(scriptPtr, interopDefaultValues, length);
+
+ // We're borrowing the native value of the StringName and Variant entries.
+ // The dictionary needs to be kept alive until `addDefValFunc` returns.
+ GC.KeepAlive(defaultValues);
+ }
+ finally
+ {
+ if (!useStack)
+ NativeMemory.Free(interopDefaultValues);
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool SwapGCHandleForType(IntPtr oldGCHandlePtr, IntPtr* outNewGCHandlePtr,
+ godot_bool createWeak)
+ {
+ try
+ {
+ var oldGCHandle = GCHandle.FromIntPtr(oldGCHandlePtr);
+
+ object? target = oldGCHandle.Target;
+
+ if (target == null)
+ {
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.False; // Called after the managed side was collected, so nothing to do here
+ }
+
+ // Release the current weak handle and replace it with a strong handle.
+ var newGCHandle = createWeak.ToBool() ?
+ CustomGCHandle.AllocWeak(target) :
+ CustomGCHandle.AllocStrong(target);
+
+ CustomGCHandle.Free(oldGCHandle);
+ *outNewGCHandlePtr = GCHandle.ToIntPtr(newGCHandle);
+ return godot_bool.True;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outNewGCHandlePtr = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
new file mode 100644
index 0000000000..a58f6849ad
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace Godot.Bridge;
+
+#nullable enable
+
+public static partial class ScriptManagerBridge
+{
+ private class ScriptTypeBiMap
+ {
+ public readonly object ReadWriteLock = new();
+ private System.Collections.Generic.Dictionary<IntPtr, Type> _scriptTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, IntPtr> _typeScriptMap = new();
+
+ public void Add(IntPtr scriptPtr, Type scriptType)
+ {
+ // TODO: What if this is called while unloading a load context, but after we already did cleanup in preparation for unloading?
+
+ _scriptTypeMap.Add(scriptPtr, scriptType);
+ _typeScriptMap.Add(scriptType, scriptPtr);
+
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ AddTypeForAlcReloading(scriptType);
+ }
+ }
+
+ public void Remove(IntPtr scriptPtr)
+ {
+ if (_scriptTypeMap.Remove(scriptPtr, out Type? scriptType))
+ _ = _typeScriptMap.Remove(scriptType);
+ }
+
+ public bool RemoveByScriptType(Type scriptType, out IntPtr scriptPtr)
+ {
+ if (_typeScriptMap.Remove(scriptType, out scriptPtr))
+ return _scriptTypeMap.Remove(scriptPtr);
+ return false;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Type GetScriptType(IntPtr scriptPtr) => _scriptTypeMap[scriptPtr];
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(IntPtr scriptPtr, [MaybeNullWhen(false)] out Type scriptType) =>
+ _scriptTypeMap.TryGetValue(scriptPtr, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPtr(Type scriptType, out IntPtr scriptPtr) =>
+ _typeScriptMap.TryGetValue(scriptType, out scriptPtr);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsScriptRegistered(IntPtr scriptPtr) => _scriptTypeMap.ContainsKey(scriptPtr);
+ }
+
+ private class PathScriptTypeBiMap
+ {
+ private System.Collections.Generic.Dictionary<string, Type> _pathTypeMap = new();
+ private System.Collections.Generic.Dictionary<Type, string> _typePathMap = new();
+
+ public void Add(string scriptPath, Type scriptType)
+ {
+ _pathTypeMap.Add(scriptPath, scriptType);
+
+ // Due to partial classes, more than one file can point to the same type, so
+ // there could be duplicate keys in this case. We only add a type as key once.
+ _typePathMap.TryAdd(scriptType, scriptPath);
+ }
+
+ public void RemoveByScriptType(Type scriptType)
+ {
+ foreach (var pair in _pathTypeMap
+ .Where(p => p.Value == scriptType).ToArray())
+ {
+ _pathTypeMap.Remove(pair.Key);
+ }
+
+ _typePathMap.Remove(scriptType);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptType(string scriptPath, [MaybeNullWhen(false)] out Type scriptType) =>
+ _pathTypeMap.TryGetValue(scriptPath, out scriptType);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetScriptPath(Type scriptType, [MaybeNullWhen(false)] out string scriptPath) =>
+ _typePathMap.TryGetValue(scriptType, out scriptPath);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index 2722b64e6d..23b0aa9204 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -24,30 +26,33 @@ namespace Godot
/// }
/// </code>
/// </example>
- public struct Callable
+ public readonly partial struct Callable
{
private readonly Object _target;
private readonly StringName _method;
private readonly Delegate _delegate;
+ private readonly unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> _trampoline;
/// <summary>
/// Object that contains the method.
/// </summary>
public Object Target => _target;
+
/// <summary>
/// Name of the method that will be called.
/// </summary>
public StringName Method => _method;
+
/// <summary>
/// Delegate of the method that will be called.
/// </summary>
public Delegate Delegate => _delegate;
/// <summary>
- /// Converts a <see cref="Delegate"/> to a <see cref="Callable"/>.
+ /// Trampoline function pointer for dynamically invoking <see cref="Callable.Delegate"/>.
/// </summary>
- /// <param name="delegate">The delegate to convert.</param>
- public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
+ public unsafe delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> Trampoline
+ => _trampoline;
/// <summary>
/// Constructs a new <see cref="Callable"/> for the method called <paramref name="method"/>
@@ -55,33 +60,58 @@ namespace Godot
/// </summary>
/// <param name="target">Object that contains the method.</param>
/// <param name="method">Name of the method that will be called.</param>
- public Callable(Object target, StringName method)
+ public unsafe Callable(Object target, StringName method)
{
_target = target;
_method = method;
_delegate = null;
+ _trampoline = null;
}
- /// <summary>
- /// Constructs a new <see cref="Callable"/> for the given <paramref name="delegate"/>.
- /// </summary>
- /// <param name="delegate">Delegate method that will be called.</param>
- public Callable(Delegate @delegate)
+ private unsafe Callable(Delegate @delegate,
+ delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
{
- _target = null;
+ _target = @delegate?.Target as Object;
_method = null;
_delegate = @delegate;
+ _trampoline = trampoline;
}
+ private const int VarArgsSpanThreshold = 10;
+
/// <summary>
/// Calls the method represented by this <see cref="Callable"/>.
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
/// <returns>The value returned by the method.</returns>
- public object Call(params object[] args)
+ public unsafe Variant Call(params Variant[] args)
{
- return godot_icall_Callable_Call(ref this, args);
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold] :
+ new godot_variant.movable[argc];
+
+ Span<IntPtr> argsSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc IntPtr[VarArgsSpanThreshold] :
+ new IntPtr[argc];
+
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
+
+ godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
+ (godot_variant**)argsPtr, argc, out _);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
+ }
}
/// <summary>
@@ -89,15 +119,85 @@ namespace Godot
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
- public void CallDeferred(params object[] args)
+ public unsafe void CallDeferred(params Variant[] args)
{
- godot_icall_Callable_CallDeferred(ref this, args);
- }
+ using godot_callable callable = Marshaling.ConvertCallableToNative(this);
+
+ int argc = args.Length;
+
+ Span<godot_variant.movable> argsStoreSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc godot_variant.movable[VarArgsSpanThreshold] :
+ new godot_variant.movable[argc];
+
+ Span<IntPtr> argsSpan = argc <= VarArgsSpanThreshold ?
+ stackalloc IntPtr[VarArgsSpanThreshold] :
+ new IntPtr[argc];
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Callable_Call(ref Callable callable, object[] args);
+ fixed (godot_variant* varargs = &MemoryMarshal.GetReference(argsStoreSpan).DangerousSelfRef)
+ fixed (IntPtr* argsPtr = &MemoryMarshal.GetReference(argsSpan))
+ {
+ for (int i = 0; i < argc; i++)
+ {
+ varargs[i] = (godot_variant)args[i].NativeVar;
+ argsPtr[i] = new IntPtr(&varargs[i]);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Callable_CallDeferred(ref Callable callable, object[] args);
+ NativeFuncs.godotsharp_callable_call_deferred(callable, (godot_variant**)argsPtr, argc);
+ }
+ }
+
+ /// <summary>
+ /// <para>
+ /// Constructs a new <see cref="Callable"/> using the <paramref name="trampoline"/>
+ /// function pointer to dynamically invoke the given <paramref name="delegate"/>.
+ /// </para>
+ /// <para>
+ /// The parameters passed to the <paramref name="trampoline"/> function are:
+ /// </para>
+ /// <list type="number">
+ /// <item>
+ /// <term>delegateObj</term>
+ /// <description>The given <paramref name="delegate"/>, upcast to <see cref="object"/>.</description>
+ /// </item>
+ /// <item>
+ /// <term>args</term>
+ /// <description>Array of <see cref="godot_variant"/> arguments.</description>
+ /// </item>
+ /// <item>
+ /// <term>ret</term>
+ /// <description>Return value of type <see cref="godot_variant"/>.</description>
+ /// </item>
+ ///</list>
+ /// <para>
+ /// The delegate should be downcast to a more specific delegate type before invoking.
+ /// </para>
+ /// </summary>
+ /// <example>
+ /// Usage example:
+ ///
+ /// <code>
+ /// static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ /// {
+ /// if (args.Count != 1)
+ /// throw new ArgumentException($&quot;Callable expected {1} arguments but received {args.Count}.&quot;);
+ ///
+ /// TResult res = ((Func&lt;int, string&gt;)delegateObj)(
+ /// VariantConversionCallbacks.GetToManagedCallback&lt;int&gt;()(args[0])
+ /// );
+ ///
+ /// ret = VariantConversionCallbacks.GetToVariantCallback&lt;string&gt;()(res);
+ /// }
+ ///
+ /// var callable = Callable.CreateWithUnsafeTrampoline((int num) =&gt; &quot;foo&quot; + num.ToString(), &amp;Trampoline);
+ /// var res = (string)callable.Call(10);
+ /// Console.WriteLine(res);
+ /// </code>
+ /// </example>
+ /// <param name="delegate">Delegate method that will be called.</param>
+ /// <param name="trampoline">Trampoline function pointer for invoking the delegate.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe Callable CreateWithUnsafeTrampoline(Delegate @delegate,
+ delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void> trampoline)
+ => new(@delegate, trampoline);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
new file mode 100644
index 0000000000..ff385da1c9
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs
@@ -0,0 +1,480 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
+
+namespace Godot;
+
+#nullable enable
+
+public readonly partial struct Callable
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static void ThrowIfArgCountMismatch(NativeVariantPtrArgs args, int countExpected,
+ [CallerArgumentExpression("args")] string? paramName = null)
+ {
+ if (countExpected != args.Count)
+ ThrowArgCountMismatch(countExpected, args.Count, paramName);
+
+ static void ThrowArgCountMismatch(int countExpected, int countReceived, string? paramName)
+ {
+ throw new ArgumentException(
+ "Invalid argument count for invoking callable." +
+ $" Expected {countExpected} arguments, received {countReceived}.",
+ paramName);
+ }
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the given <paramref name="action"/>.
+ /// </summary>
+ /// <param name="action">Action method that will be called.</param>
+ public static unsafe Callable From(
+ Action action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 0);
+
+ ((Action)delegateObj)();
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0>(
+ Action<T0> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 1);
+
+ ((Action<T0>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1>(
+ Action<T0, T1> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 2);
+
+ ((Action<T0, T1>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2>(
+ Action<T0, T1, T2> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 3);
+
+ ((Action<T0, T1, T2>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3>(
+ Action<T0, T1, T2, T3> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 4);
+
+ ((Action<T0, T1, T2, T3>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4>(
+ Action<T0, T1, T2, T3, T4> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 5);
+
+ ((Action<T0, T1, T2, T3, T4>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5>(
+ Action<T0, T1, T2, T3, T4, T5> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 6);
+
+ ((Action<T0, T1, T2, T3, T4, T5>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6>(
+ Action<T0, T1, T2, T3, T4, T5, T6> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 7);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7>(
+ Action<T0, T1, T2, T3, T4, T5, T6, T7> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 8);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6, T7>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From(Action)"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8>(
+ Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 9);
+
+ ((Action<T0, T1, T2, T3, T4, T5, T6, T7, T8>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7]),
+ VariantUtils.ConvertTo<T8>(args[8])
+ );
+
+ ret = default;
+ }
+
+ return CreateWithUnsafeTrampoline(action, &Trampoline);
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>.
+ /// </summary>
+ /// <param name="func">Action method that will be called.</param>
+ public static unsafe Callable From<TResult>(
+ Func<TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 0);
+
+ TResult res = ((Func<TResult>)delegateObj)();
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, TResult>(
+ Func<T0, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 1);
+
+ TResult res = ((Func<T0, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, TResult>(
+ Func<T0, T1, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 2);
+
+ TResult res = ((Func<T0, T1, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, TResult>(
+ Func<T0, T1, T2, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 3);
+
+ TResult res = ((Func<T0, T1, T2, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, TResult>(
+ Func<T0, T1, T2, T3, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 4);
+
+ TResult res = ((Func<T0, T1, T2, T3, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, TResult>(
+ Func<T0, T1, T2, T3, T4, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 5);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 6);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 7);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 8);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+
+ /// <inheritdoc cref="From{TResult}(Func{TResult})"/>
+ public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>(
+ Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func
+ )
+ {
+ static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
+ {
+ ThrowIfArgCountMismatch(args, 9);
+
+ TResult res = ((Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>)delegateObj)(
+ VariantUtils.ConvertTo<T0>(args[0]),
+ VariantUtils.ConvertTo<T1>(args[1]),
+ VariantUtils.ConvertTo<T2>(args[2]),
+ VariantUtils.ConvertTo<T3>(args[3]),
+ VariantUtils.ConvertTo<T4>(args[4]),
+ VariantUtils.ConvertTo<T5>(args[5]),
+ VariantUtils.ConvertTo<T6>(args[6]),
+ VariantUtils.ConvertTo<T7>(args[7]),
+ VariantUtils.ConvertTo<T8>(args[8])
+ );
+
+ ret = VariantUtils.CreateFrom(res);
+ }
+
+ return CreateWithUnsafeTrampoline(func, &Trampoline);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index fc9d40ca48..2effdecf40 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -43,7 +44,7 @@ namespace Godot
/// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int r8
{
- get
+ readonly get
{
return (int)Math.Round(r * 255.0f);
}
@@ -59,7 +60,7 @@ namespace Godot
/// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int g8
{
- get
+ readonly get
{
return (int)Math.Round(g * 255.0f);
}
@@ -75,7 +76,7 @@ namespace Godot
/// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int b8
{
- get
+ readonly get
{
return (int)Math.Round(b * 255.0f);
}
@@ -91,7 +92,7 @@ namespace Godot
/// <value>Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255.</value>
public int a8
{
- get
+ readonly get
{
return (int)Math.Round(a * 255.0f);
}
@@ -107,7 +108,7 @@ namespace Godot
/// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHSV"/>.</value>
public float h
{
- get
+ readonly get
{
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
@@ -155,7 +156,7 @@ namespace Godot
/// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHSV"/>.</value>
public float s
{
- get
+ readonly get
{
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
@@ -176,7 +177,7 @@ namespace Godot
/// <value>Getting is equivalent to using <see cref="Math.Max(float, float)"/> on the RGB components. Setting uses <see cref="FromHSV"/>.</value>
public float v
{
- get
+ readonly get
{
return Math.Max(r, Math.Max(g, b));
}
@@ -187,6 +188,19 @@ namespace Godot
}
/// <summary>
+ /// Returns the light intensity of the color, as a value between 0.0 and 1.0 (inclusive).
+ /// This is useful when determining light or dark color. Colors with a luminance smaller
+ /// than 0.5 can be generally considered dark.
+ /// Note: <see cref="Luminance"/> relies on the color being in the linear color space to
+ /// return an accurate relative luminance value. If the color is in the sRGB color space
+ /// use <see cref="SrgbToLinear"/> to convert it to the linear color space first.
+ /// </summary>
+ public readonly float Luminance
+ {
+ get { return 0.2126f * r + 0.7152f * g + 0.0722f * b; }
+ }
+
+ /// <summary>
/// Access color components using their index.
/// </summary>
/// <value>
@@ -197,7 +211,7 @@ namespace Godot
/// </value>
public float this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -210,7 +224,7 @@ namespace Godot
case 3:
return a;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -230,7 +244,7 @@ namespace Godot
a = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -242,7 +256,7 @@ namespace Godot
/// </summary>
/// <param name="over">The color to blend over.</param>
/// <returns>This color blended over <paramref name="over"/>.</returns>
- public Color Blend(Color over)
+ public readonly Color Blend(Color over)
{
Color res;
@@ -269,7 +283,7 @@ namespace Godot
/// <param name="min">The color with minimum allowed values.</param>
/// <param name="max">The color with maximum allowed values.</param>
/// <returns>The color with all components clamped.</returns>
- public Color Clamp(Color? min = null, Color? max = null)
+ public readonly Color Clamp(Color? min = null, Color? max = null)
{
Color minimum = min ?? new Color(0, 0, 0, 0);
Color maximum = max ?? new Color(1, 1, 1, 1);
@@ -288,7 +302,7 @@ namespace Godot
/// </summary>
/// <param name="amount">The ratio to darken by.</param>
/// <returns>The darkened color.</returns>
- public Color Darkened(float amount)
+ public readonly Color Darkened(float amount)
{
Color res = this;
res.r *= 1.0f - amount;
@@ -301,7 +315,7 @@ namespace Godot
/// Returns the inverted color: <c>(1 - r, 1 - g, 1 - b, a)</c>.
/// </summary>
/// <returns>The inverted color.</returns>
- public Color Inverted()
+ public readonly Color Inverted()
{
return new Color(
1.0f - r,
@@ -317,7 +331,7 @@ namespace Godot
/// </summary>
/// <param name="amount">The ratio to lighten by.</param>
/// <returns>The darkened color.</returns>
- public Color Lightened(float amount)
+ public readonly Color Lightened(float amount)
{
Color res = this;
res.r += (1.0f - res.r) * amount;
@@ -333,33 +347,44 @@ namespace Godot
/// <param name="to">The destination color 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 color of the interpolation.</returns>
- public Color Lerp(Color to, float weight)
+ public readonly Color Lerp(Color to, real_t weight)
{
return new Color
(
- Mathf.Lerp(r, to.r, weight),
- Mathf.Lerp(g, to.g, weight),
- Mathf.Lerp(b, to.b, weight),
- Mathf.Lerp(a, to.a, weight)
+ (float)Mathf.Lerp(r, to.r, weight),
+ (float)Mathf.Lerp(g, to.g, weight),
+ (float)Mathf.Lerp(b, to.b, weight),
+ (float)Mathf.Lerp(a, to.a, weight)
);
}
/// <summary>
- /// Returns the result of the linear interpolation between
- /// this color and <paramref name="to"/> by color amount <paramref name="weight"/>.
+ /// Returns the color converted to the sRGB color space.
+ /// This method assumes the original color is in the linear color space.
+ /// See also <see cref="SrgbToLinear"/> which performs the opposite operation.
/// </summary>
- /// <param name="to">The destination color for interpolation.</param>
- /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting color of the interpolation.</returns>
- public Color Lerp(Color to, Color weight)
+ /// <returns>The sRGB color.</returns>
+ public readonly Color LinearToSrgb()
{
- return new Color
- (
- Mathf.Lerp(r, to.r, weight.r),
- Mathf.Lerp(g, to.g, weight.g),
- Mathf.Lerp(b, to.b, weight.b),
- Mathf.Lerp(a, to.a, weight.a)
- );
+ return new Color(
+ r < 0.0031308f ? 12.92f * r : (1.0f + 0.055f) * (float)Mathf.Pow(r, 1.0f / 2.4f) - 0.055f,
+ g < 0.0031308f ? 12.92f * g : (1.0f + 0.055f) * (float)Mathf.Pow(g, 1.0f / 2.4f) - 0.055f,
+ b < 0.0031308f ? 12.92f * b : (1.0f + 0.055f) * (float)Mathf.Pow(b, 1.0f / 2.4f) - 0.055f, a);
+ }
+
+ /// <summary>
+ /// Returns the color converted to linear color space.
+ /// This method assumes the original color already is in sRGB color space.
+ /// See also <see cref="LinearToSrgb"/> which performs the opposite operation.
+ /// </summary>
+ /// <returns>The color in linear color space.</returns>
+ public readonly Color SrgbToLinear()
+ {
+ return new Color(
+ r < 0.04045f ? r * (1.0f / 12.92f) : (float)Mathf.Pow((r + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+ g < 0.04045f ? g * (1.0f / 12.92f) : (float)Mathf.Pow((g + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+ b < 0.04045f ? b * (1.0f / 12.92f) : (float)Mathf.Pow((b + 0.055f) * (float)(1.0 / (1.0 + 0.055)), 2.4f),
+ a);
}
/// <summary>
@@ -368,7 +393,7 @@ namespace Godot
/// ABGR is the reversed version of the default format.
/// </summary>
/// <returns>A <see langword="uint"/> representing this color in ABGR32 format.</returns>
- public uint ToAbgr32()
+ public readonly uint ToAbgr32()
{
uint c = (byte)Math.Round(a * 255);
c <<= 8;
@@ -387,7 +412,7 @@ namespace Godot
/// ABGR is the reversed version of the default format.
/// </summary>
/// <returns>A <see langword="ulong"/> representing this color in ABGR64 format.</returns>
- public ulong ToAbgr64()
+ public readonly ulong ToAbgr64()
{
ulong c = (ushort)Math.Round(a * 65535);
c <<= 16;
@@ -406,7 +431,7 @@ namespace Godot
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
/// <returns>A <see langword="uint"/> representing this color in ARGB32 format.</returns>
- public uint ToArgb32()
+ public readonly uint ToArgb32()
{
uint c = (byte)Math.Round(a * 255);
c <<= 8;
@@ -425,7 +450,7 @@ namespace Godot
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
/// <returns>A <see langword="ulong"/> representing this color in ARGB64 format.</returns>
- public ulong ToArgb64()
+ public readonly ulong ToArgb64()
{
ulong c = (ushort)Math.Round(a * 65535);
c <<= 16;
@@ -444,7 +469,7 @@ namespace Godot
/// RGBA is Godot's default and recommended format.
/// </summary>
/// <returns>A <see langword="uint"/> representing this color in RGBA32 format.</returns>
- public uint ToRgba32()
+ public readonly uint ToRgba32()
{
uint c = (byte)Math.Round(r * 255);
c <<= 8;
@@ -463,7 +488,7 @@ namespace Godot
/// RGBA is Godot's default and recommended format.
/// </summary>
/// <returns>A <see langword="ulong"/> representing this color in RGBA64 format.</returns>
- public ulong ToRgba64()
+ public readonly ulong ToRgba64()
{
ulong c = (ushort)Math.Round(r * 65535);
c <<= 16;
@@ -483,7 +508,7 @@ namespace Godot
/// Whether or not to include alpha. If <see langword="false"/>, the color is RGB instead of RGBA.
/// </param>
/// <returns>A string for the HTML hexadecimal representation of this color.</returns>
- public string ToHTML(bool includeAlpha = true)
+ public readonly string ToHTML(bool includeAlpha = true)
{
string txt = string.Empty;
@@ -565,6 +590,10 @@ namespace Godot
/// <see cref="Colors"/> constants.
/// </summary>
/// <param name="code">The HTML color code or color name to construct from.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// A color cannot be inferred from the given <paramref name="code"/>.
+ /// It was invalid HTML and a color with that name was not found.
+ /// </exception>
public Color(string code)
{
if (HtmlIsValid(code))
@@ -595,9 +624,9 @@ namespace Godot
/// </summary>
/// <param name="rgba">A string for the HTML hexadecimal representation of this color.</param>
/// <exception name="ArgumentOutOfRangeException">
- /// Thrown when the given <paramref name="rgba"/> color code is invalid.
+ /// <paramref name="rgba"/> color code is invalid.
/// </exception>
- private static Color FromHTML(string rgba)
+ public static Color FromHTML(ReadOnlySpan<char> rgba)
{
Color c;
if (rgba.Length == 0)
@@ -611,7 +640,7 @@ namespace Godot
if (rgba[0] == '#')
{
- rgba = rgba.Substring(1);
+ rgba = rgba.Slice(1);
}
// If enabled, use 1 hex digit per channel instead of 2.
@@ -665,22 +694,22 @@ namespace Godot
if (c.r < 0)
{
- throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
+ throw new ArgumentOutOfRangeException($"Invalid color code. Red part is not valid hexadecimal: {rgba}");
}
if (c.g < 0)
{
- throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
+ throw new ArgumentOutOfRangeException($"Invalid color code. Green part is not valid hexadecimal: {rgba}");
}
if (c.b < 0)
{
- throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
+ throw new ArgumentOutOfRangeException($"Invalid color code. Blue part is not valid hexadecimal: {rgba}");
}
if (c.a < 0)
{
- throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
+ throw new ArgumentOutOfRangeException($"Invalid color code. Alpha part is not valid hexadecimal: {rgba}");
}
return c;
}
@@ -705,28 +734,59 @@ namespace Godot
/// the constants defined in <see cref="Colors"/>.
/// </summary>
/// <param name="name">The name of the color.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// A color with the given name is not found.
+ /// </exception>
/// <returns>The constructed color.</returns>
private static Color Named(string name)
{
+ if (!FindNamedColor(name, out Color color))
+ {
+ throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
+ }
+
+ return color;
+ }
+
+ /// <summary>
+ /// Returns a color according to the standardized name, with the
+ /// specified alpha value. Supported color names are the same as
+ /// the constants defined in <see cref="Colors"/>.
+ /// If a color with the given name is not found, it returns
+ /// <paramref name="default"/>.
+ /// </summary>
+ /// <param name="name">The name of the color.</param>
+ /// <param name="default">
+ /// The default color to return when a color with the given name
+ /// is not found.
+ /// </param>
+ /// <returns>The constructed color.</returns>
+ private static Color Named(string name, Color @default)
+ {
+ if (!FindNamedColor(name, out Color color))
+ {
+ return @default;
+ }
+
+ return color;
+ }
+
+ private static bool FindNamedColor(string name, out Color color)
+ {
name = name.Replace(" ", string.Empty);
name = name.Replace("-", string.Empty);
name = name.Replace("_", string.Empty);
name = name.Replace("'", string.Empty);
name = name.Replace(".", string.Empty);
- name = name.ToUpper();
-
- if (!Colors.namedColors.ContainsKey(name))
- {
- throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
- }
+ name = name.ToUpperInvariant();
- return Colors.namedColors[name];
+ return Colors.namedColors.TryGetValue(name, out color);
}
/// <summary>
- /// Constructs a color from an HSV profile, with values on the
- /// range of 0 to 1. This is equivalent to using each of
- /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient.
+ /// Constructs a color from an HSV profile. The <paramref name="hue"/>,
+ /// <paramref name="saturation"/>, and <paramref name="value"/> are typically
+ /// between 0.0 and 1.0.
/// </summary>
/// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param>
/// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param>
@@ -777,7 +837,7 @@ namespace Godot
/// <param name="hue">Output parameter for the HSV hue.</param>
/// <param name="saturation">Output parameter for the HSV saturation.</param>
/// <param name="value">Output parameter for the HSV value.</param>
- public void ToHSV(out float hue, out float saturation, out float value)
+ public readonly void ToHSV(out float hue, out float saturation, out float value)
{
float max = (float)Mathf.Max(r, Mathf.Max(g, b));
float min = (float)Mathf.Min(r, Mathf.Min(g, b));
@@ -817,9 +877,9 @@ namespace Godot
value = max;
}
- private static int ParseCol4(string str, int ofs)
+ private static int ParseCol4(ReadOnlySpan<char> str, int index)
{
- char character = str[ofs];
+ char character = str[index];
if (character >= '0' && character <= '9')
{
@@ -836,27 +896,92 @@ namespace Godot
return -1;
}
- private static int ParseCol8(string str, int ofs)
+ private static int ParseCol8(ReadOnlySpan<char> str, int index)
+ {
+ return ParseCol4(str, index) * 16 + ParseCol4(str, index + 1);
+ }
+
+ /// <summary>
+ /// Constructs a color from an OK HSL profile. The <paramref name="hue"/>,
+ /// <paramref name="saturation"/>, and <paramref name="lightness"/> are typically
+ /// between 0.0 and 1.0.
+ /// </summary>
+ /// <param name="hue">The OK HSL hue, typically on the range of 0 to 1.</param>
+ /// <param name="saturation">The OK HSL saturation, typically on the range of 0 to 1.</param>
+ /// <param name="lightness">The OK HSL lightness, typically on the range of 0 to 1.</param>
+ /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color FromOkHsl(float hue, float saturation, float lightness, float alpha = 1.0f)
+ {
+ return NativeFuncs.godotsharp_color_from_ok_hsl(hue, saturation, lightness, alpha);
+ }
+
+ /// <summary>
+ /// Encodes a <see cref="Color"/> from a RGBE9995 format integer.
+ /// See <see cref="Image.Format.Rgbe9995"/>.
+ /// </summary>
+ /// <param name="rgbe">The RGBE9995 encoded color.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color FromRgbe9995(uint rgbe)
+ {
+ float r = rgbe & 0x1ff;
+ float g = (rgbe >> 9) & 0x1ff;
+ float b = (rgbe >> 18) & 0x1ff;
+ float e = rgbe >> 27;
+ float m = (float)Mathf.Pow(2.0f, e - 15.0f - 9.0f);
+
+ float rd = r * m;
+ float gd = g * m;
+ float bd = b * m;
+
+ return new Color(rd, gd, bd, 1.0f);
+ }
+
+ /// <summary>
+ /// Constructs a color from the given string, which can be either an HTML color
+ /// code or a named color. Returns <paramref name="default"/> if the color cannot
+ /// be inferred from the string. Supported color names are the same as the
+ /// <see cref="Colors"/> constants.
+ /// </summary>
+ /// <param name="str">The HTML color code or color name.</param>
+ /// <param name="default">The fallback color to return if the color cannot be inferred.</param>
+ /// <returns>The constructed color.</returns>
+ public static Color FromString(string str, Color @default)
{
- return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
+ if (HtmlIsValid(str))
+ {
+ return FromHTML(str);
+ }
+ else
+ {
+ return Named(str, @default);
+ }
}
- private string ToHex32(float val)
+ private static string ToHex32(float val)
{
byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
return b.HexEncode();
}
- internal static bool HtmlIsValid(string color)
+ /// <summary>
+ /// Returns <see langword="true"/> if <paramref name="color"/> is a valid HTML hexadecimal
+ /// color string. The string must be a hexadecimal value (case-insensitive) of either 3,
+ /// 4, 6 or 8 digits, and may be prefixed by a hash sign (<c>#</c>). This method is
+ /// identical to <see cref="StringExtensions.IsValidHtmlColor(string)"/>.
+ /// </summary>
+ /// <param name="color">The HTML hexadecimal color string.</param>
+ /// <returns>Whether or not the string was a valid HTML hexadecimal color string.</returns>
+ public static bool HtmlIsValid(ReadOnlySpan<char> color)
{
- if (color.Length == 0)
+ if (color.IsEmpty)
{
return false;
}
if (color[0] == '#')
{
- color = color.Substring(1);
+ color = color.Slice(1);
}
// Check if the amount of hex digits is valid.
@@ -916,7 +1041,7 @@ namespace Godot
/// <c>new Color(1 - c.r, 1 - c.g, 1 - c.b, 1 - c.a)</c>.
/// </summary>
/// <param name="color">The color to invert.</param>
- /// <returns>The inverted color</returns>
+ /// <returns>The inverted color.</returns>
public static Color operator -(Color color)
{
return Colors.White - color;
@@ -1149,14 +1274,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The other object to compare.</param>
/// <returns>Whether or not the color and the other object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Color)
- {
- return Equals((Color)obj);
- }
-
- return false;
+ return obj is Color other && Equals(other);
}
/// <summary>
@@ -1166,7 +1286,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other color.</param>
/// <returns>Whether or not the colors are equal.</returns>
- public bool Equals(Color other)
+ public readonly bool Equals(Color other)
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
@@ -1177,7 +1297,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other color to compare.</param>
/// <returns>Whether or not the colors are approximately equal.</returns>
- public bool IsEqualApprox(Color other)
+ public readonly bool IsEqualApprox(Color other)
{
return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
}
@@ -1186,7 +1306,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Color"/>.
/// </summary>
/// <returns>A hash code for this color.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
}
@@ -1195,7 +1315,7 @@ namespace Godot
/// Converts this <see cref="Color"/> to a string.
/// </summary>
/// <returns>A string representation of this color.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({r}, {g}, {b}, {a})";
}
@@ -1204,7 +1324,7 @@ namespace Godot
/// Converts this <see cref="Color"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this color.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({r.ToString(format)}, {g.ToString(format)}, {b.ToString(format)}, {a.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index 68c821b447..5bce66ea87 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -10,152 +10,152 @@ namespace Godot
{
// Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- {"ALICEBLUE", new Color(0.94f, 0.97f, 1.00f)},
- {"ANTIQUEWHITE", new Color(0.98f, 0.92f, 0.84f)},
- {"AQUA", new Color(0.00f, 1.00f, 1.00f)},
- {"AQUAMARINE", new Color(0.50f, 1.00f, 0.83f)},
- {"AZURE", new Color(0.94f, 1.00f, 1.00f)},
- {"BEIGE", new Color(0.96f, 0.96f, 0.86f)},
- {"BISQUE", new Color(1.00f, 0.89f, 0.77f)},
- {"BLACK", new Color(0.00f, 0.00f, 0.00f)},
- {"BLANCHEDALMOND", new Color(1.00f, 0.92f, 0.80f)},
- {"BLUE", new Color(0.00f, 0.00f, 1.00f)},
- {"BLUEVIOLET", new Color(0.54f, 0.17f, 0.89f)},
- {"BROWN", new Color(0.65f, 0.16f, 0.16f)},
- {"BURLYWOOD", new Color(0.87f, 0.72f, 0.53f)},
- {"CADETBLUE", new Color(0.37f, 0.62f, 0.63f)},
- {"CHARTREUSE", new Color(0.50f, 1.00f, 0.00f)},
- {"CHOCOLATE", new Color(0.82f, 0.41f, 0.12f)},
- {"CORAL", new Color(1.00f, 0.50f, 0.31f)},
- {"CORNFLOWERBLUE", new Color(0.39f, 0.58f, 0.93f)},
- {"CORNSILK", new Color(1.00f, 0.97f, 0.86f)},
- {"CRIMSON", new Color(0.86f, 0.08f, 0.24f)},
- {"CYAN", new Color(0.00f, 1.00f, 1.00f)},
- {"DARKBLUE", new Color(0.00f, 0.00f, 0.55f)},
- {"DARKCYAN", new Color(0.00f, 0.55f, 0.55f)},
- {"DARKGOLDENROD", new Color(0.72f, 0.53f, 0.04f)},
- {"DARKGRAY", new Color(0.66f, 0.66f, 0.66f)},
- {"DARKGREEN", new Color(0.00f, 0.39f, 0.00f)},
- {"DARKKHAKI", new Color(0.74f, 0.72f, 0.42f)},
- {"DARKMAGENTA", new Color(0.55f, 0.00f, 0.55f)},
- {"DARKOLIVEGREEN", new Color(0.33f, 0.42f, 0.18f)},
- {"DARKORANGE", new Color(1.00f, 0.55f, 0.00f)},
- {"DARKORCHID", new Color(0.60f, 0.20f, 0.80f)},
- {"DARKRED", new Color(0.55f, 0.00f, 0.00f)},
- {"DARKSALMON", new Color(0.91f, 0.59f, 0.48f)},
- {"DARKSEAGREEN", new Color(0.56f, 0.74f, 0.56f)},
- {"DARKSLATEBLUE", new Color(0.28f, 0.24f, 0.55f)},
- {"DARKSLATEGRAY", new Color(0.18f, 0.31f, 0.31f)},
- {"DARKTURQUOISE", new Color(0.00f, 0.81f, 0.82f)},
- {"DARKVIOLET", new Color(0.58f, 0.00f, 0.83f)},
- {"DEEPPINK", new Color(1.00f, 0.08f, 0.58f)},
- {"DEEPSKYBLUE", new Color(0.00f, 0.75f, 1.00f)},
- {"DIMGRAY", new Color(0.41f, 0.41f, 0.41f)},
- {"DODGERBLUE", new Color(0.12f, 0.56f, 1.00f)},
- {"FIREBRICK", new Color(0.70f, 0.13f, 0.13f)},
- {"FLORALWHITE", new Color(1.00f, 0.98f, 0.94f)},
- {"FORESTGREEN", new Color(0.13f, 0.55f, 0.13f)},
- {"FUCHSIA", new Color(1.00f, 0.00f, 1.00f)},
- {"GAINSBORO", new Color(0.86f, 0.86f, 0.86f)},
- {"GHOSTWHITE", new Color(0.97f, 0.97f, 1.00f)},
- {"GOLD", new Color(1.00f, 0.84f, 0.00f)},
- {"GOLDENROD", new Color(0.85f, 0.65f, 0.13f)},
- {"GRAY", new Color(0.75f, 0.75f, 0.75f)},
- {"GREEN", new Color(0.00f, 1.00f, 0.00f)},
- {"GREENYELLOW", new Color(0.68f, 1.00f, 0.18f)},
- {"HONEYDEW", new Color(0.94f, 1.00f, 0.94f)},
- {"HOTPINK", new Color(1.00f, 0.41f, 0.71f)},
- {"INDIANRED", new Color(0.80f, 0.36f, 0.36f)},
- {"INDIGO", new Color(0.29f, 0.00f, 0.51f)},
- {"IVORY", new Color(1.00f, 1.00f, 0.94f)},
- {"KHAKI", new Color(0.94f, 0.90f, 0.55f)},
- {"LAVENDER", new Color(0.90f, 0.90f, 0.98f)},
- {"LAVENDERBLUSH", new Color(1.00f, 0.94f, 0.96f)},
- {"LAWNGREEN", new Color(0.49f, 0.99f, 0.00f)},
- {"LEMONCHIFFON", new Color(1.00f, 0.98f, 0.80f)},
- {"LIGHTBLUE", new Color(0.68f, 0.85f, 0.90f)},
- {"LIGHTCORAL", new Color(0.94f, 0.50f, 0.50f)},
- {"LIGHTCYAN", new Color(0.88f, 1.00f, 1.00f)},
- {"LIGHTGOLDENROD", new Color(0.98f, 0.98f, 0.82f)},
- {"LIGHTGRAY", new Color(0.83f, 0.83f, 0.83f)},
- {"LIGHTGREEN", new Color(0.56f, 0.93f, 0.56f)},
- {"LIGHTPINK", new Color(1.00f, 0.71f, 0.76f)},
- {"LIGHTSALMON", new Color(1.00f, 0.63f, 0.48f)},
- {"LIGHTSEAGREEN", new Color(0.13f, 0.70f, 0.67f)},
- {"LIGHTSKYBLUE", new Color(0.53f, 0.81f, 0.98f)},
- {"LIGHTSLATEGRAY", new Color(0.47f, 0.53f, 0.60f)},
- {"LIGHTSTEELBLUE", new Color(0.69f, 0.77f, 0.87f)},
- {"LIGHTYELLOW", new Color(1.00f, 1.00f, 0.88f)},
- {"LIME", new Color(0.00f, 1.00f, 0.00f)},
- {"LIMEGREEN", new Color(0.20f, 0.80f, 0.20f)},
- {"LINEN", new Color(0.98f, 0.94f, 0.90f)},
- {"MAGENTA", new Color(1.00f, 0.00f, 1.00f)},
- {"MAROON", new Color(0.69f, 0.19f, 0.38f)},
- {"MEDIUMAQUAMARINE", new Color(0.40f, 0.80f, 0.67f)},
- {"MEDIUMBLUE", new Color(0.00f, 0.00f, 0.80f)},
- {"MEDIUMORCHID", new Color(0.73f, 0.33f, 0.83f)},
- {"MEDIUMPURPLE", new Color(0.58f, 0.44f, 0.86f)},
- {"MEDIUMSEAGREEN", new Color(0.24f, 0.70f, 0.44f)},
- {"MEDIUMSLATEBLUE", new Color(0.48f, 0.41f, 0.93f)},
- {"MEDIUMSPRINGGREEN", new Color(0.00f, 0.98f, 0.60f)},
- {"MEDIUMTURQUOISE", new Color(0.28f, 0.82f, 0.80f)},
- {"MEDIUMVIOLETRED", new Color(0.78f, 0.08f, 0.52f)},
- {"MIDNIGHTBLUE", new Color(0.10f, 0.10f, 0.44f)},
- {"MINTCREAM", new Color(0.96f, 1.00f, 0.98f)},
- {"MISTYROSE", new Color(1.00f, 0.89f, 0.88f)},
- {"MOCCASIN", new Color(1.00f, 0.89f, 0.71f)},
- {"NAVAJOWHITE", new Color(1.00f, 0.87f, 0.68f)},
- {"NAVYBLUE", new Color(0.00f, 0.00f, 0.50f)},
- {"OLDLACE", new Color(0.99f, 0.96f, 0.90f)},
- {"OLIVE", new Color(0.50f, 0.50f, 0.00f)},
- {"OLIVEDRAB", new Color(0.42f, 0.56f, 0.14f)},
- {"ORANGE", new Color(1.00f, 0.65f, 0.00f)},
- {"ORANGERED", new Color(1.00f, 0.27f, 0.00f)},
- {"ORCHID", new Color(0.85f, 0.44f, 0.84f)},
- {"PALEGOLDENROD", new Color(0.93f, 0.91f, 0.67f)},
- {"PALEGREEN", new Color(0.60f, 0.98f, 0.60f)},
- {"PALETURQUOISE", new Color(0.69f, 0.93f, 0.93f)},
- {"PALEVIOLETRED", new Color(0.86f, 0.44f, 0.58f)},
- {"PAPAYAWHIP", new Color(1.00f, 0.94f, 0.84f)},
- {"PEACHPUFF", new Color(1.00f, 0.85f, 0.73f)},
- {"PERU", new Color(0.80f, 0.52f, 0.25f)},
- {"PINK", new Color(1.00f, 0.75f, 0.80f)},
- {"PLUM", new Color(0.87f, 0.63f, 0.87f)},
- {"POWDERBLUE", new Color(0.69f, 0.88f, 0.90f)},
- {"PURPLE", new Color(0.63f, 0.13f, 0.94f)},
- {"REBECCAPURPLE", new Color(0.40f, 0.20f, 0.60f)},
- {"RED", new Color(1.00f, 0.00f, 0.00f)},
- {"ROSYBROWN", new Color(0.74f, 0.56f, 0.56f)},
- {"ROYALBLUE", new Color(0.25f, 0.41f, 0.88f)},
- {"SADDLEBROWN", new Color(0.55f, 0.27f, 0.07f)},
- {"SALMON", new Color(0.98f, 0.50f, 0.45f)},
- {"SANDYBROWN", new Color(0.96f, 0.64f, 0.38f)},
- {"SEAGREEN", new Color(0.18f, 0.55f, 0.34f)},
- {"SEASHELL", new Color(1.00f, 0.96f, 0.93f)},
- {"SIENNA", new Color(0.63f, 0.32f, 0.18f)},
- {"SILVER", new Color(0.75f, 0.75f, 0.75f)},
- {"SKYBLUE", new Color(0.53f, 0.81f, 0.92f)},
- {"SLATEBLUE", new Color(0.42f, 0.35f, 0.80f)},
- {"SLATEGRAY", new Color(0.44f, 0.50f, 0.56f)},
- {"SNOW", new Color(1.00f, 0.98f, 0.98f)},
- {"SPRINGGREEN", new Color(0.00f, 1.00f, 0.50f)},
- {"STEELBLUE", new Color(0.27f, 0.51f, 0.71f)},
- {"TAN", new Color(0.82f, 0.71f, 0.55f)},
- {"TEAL", new Color(0.00f, 0.50f, 0.50f)},
- {"THISTLE", new Color(0.85f, 0.75f, 0.85f)},
- {"TOMATO", new Color(1.00f, 0.39f, 0.28f)},
- {"TRANSPARENT", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
- {"TURQUOISE", new Color(0.25f, 0.88f, 0.82f)},
- {"VIOLET", new Color(0.93f, 0.51f, 0.93f)},
- {"WEBGRAY", new Color(0.50f, 0.50f, 0.50f)},
- {"WEBGREEN", new Color(0.00f, 0.50f, 0.00f)},
- {"WEBMAROON", new Color(0.50f, 0.00f, 0.00f)},
- {"WEBPURPLE", new Color(0.50f, 0.00f, 0.50f)},
- {"WHEAT", new Color(0.96f, 0.87f, 0.70f)},
- {"WHITE", new Color(1.00f, 1.00f, 1.00f)},
- {"WHITESMOKE", new Color(0.96f, 0.96f, 0.96f)},
- {"YELLOW", new Color(1.00f, 1.00f, 0.00f)},
- {"YELLOWGREEN", new Color(0.60f, 0.80f, 0.20f)},
+ { "ALICEBLUE", new Color(0xF0F8FFFF) },
+ { "ANTIQUEWHITE", new Color(0xFAEBD7FF) },
+ { "AQUA", new Color(0x00FFFFFF) },
+ { "AQUAMARINE", new Color(0x7FFFD4FF) },
+ { "AZURE", new Color(0xF0FFFFFF) },
+ { "BEIGE", new Color(0xF5F5DCFF) },
+ { "BISQUE", new Color(0xFFE4C4FF) },
+ { "BLACK", new Color(0x000000FF) },
+ { "BLANCHEDALMOND", new Color(0xFFEBCDFF) },
+ { "BLUE", new Color(0x0000FFFF) },
+ { "BLUEVIOLET", new Color(0x8A2BE2FF) },
+ { "BROWN", new Color(0xA52A2AFF) },
+ { "BURLYWOOD", new Color(0xDEB887FF) },
+ { "CADETBLUE", new Color(0x5F9EA0FF) },
+ { "CHARTREUSE", new Color(0x7FFF00FF) },
+ { "CHOCOLATE", new Color(0xD2691EFF) },
+ { "CORAL", new Color(0xFF7F50FF) },
+ { "CORNFLOWERBLUE", new Color(0x6495EDFF) },
+ { "CORNSILK", new Color(0xFFF8DCFF) },
+ { "CRIMSON", new Color(0xDC143CFF) },
+ { "CYAN", new Color(0x00FFFFFF) },
+ { "DARKBLUE", new Color(0x00008BFF) },
+ { "DARKCYAN", new Color(0x008B8BFF) },
+ { "DARKGOLDENROD", new Color(0xB8860BFF) },
+ { "DARKGRAY", new Color(0xA9A9A9FF) },
+ { "DARKGREEN", new Color(0x006400FF) },
+ { "DARKKHAKI", new Color(0xBDB76BFF) },
+ { "DARKMAGENTA", new Color(0x8B008BFF) },
+ { "DARKOLIVEGREEN", new Color(0x556B2FFF) },
+ { "DARKORANGE", new Color(0xFF8C00FF) },
+ { "DARKORCHID", new Color(0x9932CCFF) },
+ { "DARKRED", new Color(0x8B0000FF) },
+ { "DARKSALMON", new Color(0xE9967AFF) },
+ { "DARKSEAGREEN", new Color(0x8FBC8FFF) },
+ { "DARKSLATEBLUE", new Color(0x483D8BFF) },
+ { "DARKSLATEGRAY", new Color(0x2F4F4FFF) },
+ { "DARKTURQUOISE", new Color(0x00CED1FF) },
+ { "DARKVIOLET", new Color(0x9400D3FF) },
+ { "DEEPPINK", new Color(0xFF1493FF) },
+ { "DEEPSKYBLUE", new Color(0x00BFFFFF) },
+ { "DIMGRAY", new Color(0x696969FF) },
+ { "DODGERBLUE", new Color(0x1E90FFFF) },
+ { "FIREBRICK", new Color(0xB22222FF) },
+ { "FLORALWHITE", new Color(0xFFFAF0FF) },
+ { "FORESTGREEN", new Color(0x228B22FF) },
+ { "FUCHSIA", new Color(0xFF00FFFF) },
+ { "GAINSBORO", new Color(0xDCDCDCFF) },
+ { "GHOSTWHITE", new Color(0xF8F8FFFF) },
+ { "GOLD", new Color(0xFFD700FF) },
+ { "GOLDENROD", new Color(0xDAA520FF) },
+ { "GRAY", new Color(0xBEBEBEFF) },
+ { "GREEN", new Color(0x00FF00FF) },
+ { "GREENYELLOW", new Color(0xADFF2FFF) },
+ { "HONEYDEW", new Color(0xF0FFF0FF) },
+ { "HOTPINK", new Color(0xFF69B4FF) },
+ { "INDIANRED", new Color(0xCD5C5CFF) },
+ { "INDIGO", new Color(0x4B0082FF) },
+ { "IVORY", new Color(0xFFFFF0FF) },
+ { "KHAKI", new Color(0xF0E68CFF) },
+ { "LAVENDER", new Color(0xE6E6FAFF) },
+ { "LAVENDERBLUSH", new Color(0xFFF0F5FF) },
+ { "LAWNGREEN", new Color(0x7CFC00FF) },
+ { "LEMONCHIFFON", new Color(0xFFFACDFF) },
+ { "LIGHTBLUE", new Color(0xADD8E6FF) },
+ { "LIGHTCORAL", new Color(0xF08080FF) },
+ { "LIGHTCYAN", new Color(0xE0FFFFFF) },
+ { "LIGHTGOLDENROD", new Color(0xFAFAD2FF) },
+ { "LIGHTGRAY", new Color(0xD3D3D3FF) },
+ { "LIGHTGREEN", new Color(0x90EE90FF) },
+ { "LIGHTPINK", new Color(0xFFB6C1FF) },
+ { "LIGHTSALMON", new Color(0xFFA07AFF) },
+ { "LIGHTSEAGREEN", new Color(0x20B2AAFF) },
+ { "LIGHTSKYBLUE", new Color(0x87CEFAFF) },
+ { "LIGHTSLATEGRAY", new Color(0x778899FF) },
+ { "LIGHTSTEELBLUE", new Color(0xB0C4DEFF) },
+ { "LIGHTYELLOW", new Color(0xFFFFE0FF) },
+ { "LIME", new Color(0x00FF00FF) },
+ { "LIMEGREEN", new Color(0x32CD32FF) },
+ { "LINEN", new Color(0xFAF0E6FF) },
+ { "MAGENTA", new Color(0xFF00FFFF) },
+ { "MAROON", new Color(0xB03060FF) },
+ { "MEDIUMAQUAMARINE", new Color(0x66CDAAFF) },
+ { "MEDIUMBLUE", new Color(0x0000CDFF) },
+ { "MEDIUMORCHID", new Color(0xBA55D3FF) },
+ { "MEDIUMPURPLE", new Color(0x9370DBFF) },
+ { "MEDIUMSEAGREEN", new Color(0x3CB371FF) },
+ { "MEDIUMSLATEBLUE", new Color(0x7B68EEFF) },
+ { "MEDIUMSPRINGGREEN", new Color(0x00FA9AFF) },
+ { "MEDIUMTURQUOISE", new Color(0x48D1CCFF) },
+ { "MEDIUMVIOLETRED", new Color(0xC71585FF) },
+ { "MIDNIGHTBLUE", new Color(0x191970FF) },
+ { "MINTCREAM", new Color(0xF5FFFAFF) },
+ { "MISTYROSE", new Color(0xFFE4E1FF) },
+ { "MOCCASIN", new Color(0xFFE4B5FF) },
+ { "NAVAJOWHITE", new Color(0xFFDEADFF) },
+ { "NAVYBLUE", new Color(0x000080FF) },
+ { "OLDLACE", new Color(0xFDF5E6FF) },
+ { "OLIVE", new Color(0x808000FF) },
+ { "OLIVEDRAB", new Color(0x6B8E23FF) },
+ { "ORANGE", new Color(0xFFA500FF) },
+ { "ORANGERED", new Color(0xFF4500FF) },
+ { "ORCHID", new Color(0xDA70D6FF) },
+ { "PALEGOLDENROD", new Color(0xEEE8AAFF) },
+ { "PALEGREEN", new Color(0x98FB98FF) },
+ { "PALETURQUOISE", new Color(0xAFEEEEFF) },
+ { "PALEVIOLETRED", new Color(0xDB7093FF) },
+ { "PAPAYAWHIP", new Color(0xFFEFD5FF) },
+ { "PEACHPUFF", new Color(0xFFDAB9FF) },
+ { "PERU", new Color(0xCD853FFF) },
+ { "PINK", new Color(0xFFC0CBFF) },
+ { "PLUM", new Color(0xDDA0DDFF) },
+ { "POWDERBLUE", new Color(0xB0E0E6FF) },
+ { "PURPLE", new Color(0xA020F0FF) },
+ { "REBECCAPURPLE", new Color(0x663399FF) },
+ { "RED", new Color(0xFF0000FF) },
+ { "ROSYBROWN", new Color(0xBC8F8FFF) },
+ { "ROYALBLUE", new Color(0x4169E1FF) },
+ { "SADDLEBROWN", new Color(0x8B4513FF) },
+ { "SALMON", new Color(0xFA8072FF) },
+ { "SANDYBROWN", new Color(0xF4A460FF) },
+ { "SEAGREEN", new Color(0x2E8B57FF) },
+ { "SEASHELL", new Color(0xFFF5EEFF) },
+ { "SIENNA", new Color(0xA0522DFF) },
+ { "SILVER", new Color(0xC0C0C0FF) },
+ { "SKYBLUE", new Color(0x87CEEBFF) },
+ { "SLATEBLUE", new Color(0x6A5ACDFF) },
+ { "SLATEGRAY", new Color(0x708090FF) },
+ { "SNOW", new Color(0xFFFAFAFF) },
+ { "SPRINGGREEN", new Color(0x00FF7FFF) },
+ { "STEELBLUE", new Color(0x4682B4FF) },
+ { "TAN", new Color(0xD2B48CFF) },
+ { "TEAL", new Color(0x008080FF) },
+ { "THISTLE", new Color(0xD8BFD8FF) },
+ { "TOMATO", new Color(0xFF6347FF) },
+ { "TRANSPARENT", new Color(0xFFFFFF00) },
+ { "TURQUOISE", new Color(0x40E0D0FF) },
+ { "VIOLET", new Color(0xEE82EEFF) },
+ { "WEBGRAY", new Color(0x808080FF) },
+ { "WEBGREEN", new Color(0x008000FF) },
+ { "WEBMAROON", new Color(0x800000FF) },
+ { "WEBPURPLE", new Color(0x800080FF) },
+ { "WHEAT", new Color(0xF5DEB3FF) },
+ { "WHITE", new Color(0xFFFFFFFF) },
+ { "WHITESMOKE", new Color(0xF5F5F5FF) },
+ { "YELLOW", new Color(0xFFFF00FF) },
+ { "YELLOWGREEN", new Color(0x9ACD32FF) },
};
#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
new file mode 100644
index 0000000000..42f19ace1a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/CustomGCHandle.cs
@@ -0,0 +1,98 @@
+#nullable enable
+
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using Godot.Bridge;
+
+namespace Godot;
+
+/// <summary>
+/// Provides a GCHandle that becomes weak when unloading the assembly load context, without having
+/// to manually replace the GCHandle. This hides all the complexity of releasing strong GC handles
+/// to allow the assembly load context to unload properly.
+///
+/// Internally, a strong CustomGCHandle actually contains a weak GCHandle, while the actual strong
+/// reference is stored in a static table.
+/// </summary>
+public static class CustomGCHandle
+{
+ // ConditionalWeakTable uses DependentHandle, so it stores weak references.
+ // Having the assembly load context as key won't prevent it from unloading.
+ private static ConditionalWeakTable<AssemblyLoadContext, object?> _alcsBeingUnloaded = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static bool IsAlcBeingUnloaded(AssemblyLoadContext alc) => _alcsBeingUnloaded.TryGetValue(alc, out _);
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<
+ AssemblyLoadContext,
+ ConcurrentDictionary<GCHandle, object>
+ > _strongReferencesByAlc = new();
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void OnAlcUnloading(AssemblyLoadContext alc)
+ {
+ _alcsBeingUnloaded.Add(alc, null);
+
+ if (_strongReferencesByAlc.TryRemove(alc, out var strongReferences))
+ {
+ strongReferences.Clear();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocStrong(object value)
+ => AllocStrong(value, value.GetType());
+
+ public static GCHandle AllocStrong(object value, Type valueType)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(valueType.Assembly);
+
+ if (alc != null)
+ {
+ var weakHandle = GCHandle.Alloc(value, GCHandleType.Weak);
+
+ if (!IsAlcBeingUnloaded(alc))
+ {
+ var strongReferences = _strongReferencesByAlc.GetOrAdd(alc,
+ static alc =>
+ {
+ alc.Unloading += OnAlcUnloading;
+ return new();
+ });
+ strongReferences.TryAdd(weakHandle, value);
+ }
+
+ return weakHandle;
+ }
+ }
+
+ return GCHandle.Alloc(value, GCHandleType.Normal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static GCHandle AllocWeak(object value) => GCHandle.Alloc(value, GCHandleType.Weak);
+
+ public static void Free(GCHandle handle)
+ {
+ if (AlcReloadCfg.IsAlcReloadingEnabled)
+ {
+ var target = handle.Target;
+
+ if (target != null)
+ {
+ var alc = AssemblyLoadContext.GetLoadContext(target.GetType().Assembly);
+
+ if (alc != null && _strongReferencesByAlc.TryGetValue(alc, out var strongReferences))
+ _ = strongReferences.TryRemove(handle, out _);
+ }
+ }
+
+ handle.Free();
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
index edfe3464ec..c4161d2ded 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs
@@ -1,13 +1,18 @@
using System;
using System.Diagnostics;
using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Text;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
internal static class DebuggingUtils
{
- internal static void AppendTypeName(this StringBuilder sb, Type type)
+ private static void AppendTypeName(this StringBuilder sb, Type type)
{
if (type.IsPrimitive)
sb.Append(type.Name);
@@ -16,21 +21,97 @@ namespace Godot
else
sb.Append(type);
- sb.Append(" ");
+ sb.Append(' ');
}
- public static void InstallTraceListener()
+ internal static void InstallTraceListener()
{
Trace.Listeners.Clear();
Trace.Listeners.Add(new GodotTraceListener());
}
- public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info
+ {
+ public godot_string File;
+ public godot_string Func;
+ public int Line;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal ref struct godot_stack_info_vector
{
- fileName = frame.GetFileName();
- fileLineNumber = frame.GetFileLineNumber();
+ private IntPtr _writeProxy;
+ private unsafe godot_stack_info* _ptr;
+
+ public readonly unsafe godot_stack_info* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public void Resize(int size)
+ {
+ if (size < 0)
+ throw new ArgumentOutOfRangeException(nameof(size));
+ var err = NativeFuncs.godotsharp_stack_info_vector_resize(ref this, size);
+ if (err != Error.Ok)
+ throw new InvalidOperationException("Failed to resize vector. Error code is: " + err.ToString());
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_stack_info_vector_destroy(ref this);
+ _ptr = null;
+ }
+ }
- MethodBase methodBase = frame.GetMethod();
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetCurrentStackInfo(void* destVector)
+ {
+ try
+ {
+ var vector = (godot_stack_info_vector*)destVector;
+ var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true);
+ int frameCount = stackTrace.FrameCount;
+
+ if (frameCount == 0)
+ return;
+
+ vector->Resize(frameCount);
+
+ int i = 0;
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ string? fileName = frame.GetFileName();
+ int fileLineNumber = frame.GetFileLineNumber();
+
+ GetStackFrameMethodDecl(frame, out string methodDecl);
+
+ godot_stack_info* stackInfo = &vector->Elements[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(fileName);
+ stackInfo->Func = Marshaling.ConvertStringToNative(methodDecl);
+ stackInfo->Line = fileLineNumber;
+
+ i++;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ internal static void GetStackFrameMethodDecl(StackFrame frame, out string methodDecl)
+ {
+ MethodBase? methodBase = frame.GetMethod();
if (methodBase == null)
{
@@ -40,18 +121,18 @@ namespace Godot
var sb = new StringBuilder();
- if (methodBase is MethodInfo)
- sb.AppendTypeName(((MethodInfo)methodBase).ReturnType);
+ if (methodBase is MethodInfo methodInfo)
+ sb.AppendTypeName(methodInfo.ReturnType);
- sb.Append(methodBase.DeclaringType.FullName);
- sb.Append(".");
+ sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>");
+ sb.Append('.');
sb.Append(methodBase.Name);
if (methodBase.IsGenericMethod)
{
Type[] genericParams = methodBase.GetGenericArguments();
- sb.Append("<");
+ sb.Append('<');
for (int j = 0; j < genericParams.Length; j++)
{
@@ -61,10 +142,10 @@ namespace Godot
sb.AppendTypeName(genericParams[j]);
}
- sb.Append(">");
+ sb.Append('>');
}
- sb.Append("(");
+ sb.Append('(');
bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
@@ -81,7 +162,7 @@ namespace Godot
sb.AppendTypeName(parameter[i].ParameterType);
}
- sb.Append(")");
+ sb.Append(')');
methodDecl = sb.ToString();
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 1dca9e6ea7..d94fbff331 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -1,14 +1,62 @@
+#nullable enable
+
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
internal static class DelegateUtils
{
+ [UnmanagedCallersOnly]
+ internal static godot_bool DelegateEquals(IntPtr delegateGCHandleA, IntPtr delegateGCHandleB)
+ {
+ try
+ {
+ var @delegateA = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleA).Target;
+ var @delegateB = (Delegate?)GCHandle.FromIntPtr(delegateGCHandleB).Target;
+ return (@delegateA! == @delegateB!).ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline,
+ godot_variant** args, int argc, godot_variant* outRet)
+ {
+ try
+ {
+ if (trampoline == null)
+ {
+ throw new ArgumentNullException(nameof(trampoline),
+ "Cannot dynamically invoke delegate because the trampoline is null.");
+ }
+
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+ var trampolineFn = (delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline;
+
+ trampolineFn(@delegate, new NativeVariantPtrArgs(args, argc), out godot_variant ret);
+
+ *outRet = ret;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outRet = default;
+ }
+ }
+
+ // TODO: Check if we should be using BindingFlags.DeclaredOnly (would give better reflection performance).
+
private enum TargetKind : uint
{
Static,
@@ -18,6 +66,11 @@ namespace Godot
internal static bool TrySerializeDelegate(Delegate @delegate, Collections.Array serializedData)
{
+ if (@delegate is null)
+ {
+ return false;
+ }
+
if (@delegate is MulticastDelegate multicastDelegate)
{
bool someDelegatesSerialized = false;
@@ -39,20 +92,20 @@ namespace Godot
}
}
- if (TrySerializeSingleDelegate(@delegate, out byte[] buffer))
+ if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer))
{
- serializedData.Add(buffer);
+ serializedData.Add((Span<byte>)buffer);
return true;
}
return false;
}
- private static bool TrySerializeSingleDelegate(Delegate @delegate, out byte[] buffer)
+ private static bool TrySerializeSingleDelegate(Delegate @delegate, [MaybeNullWhen(false)] out byte[] buffer)
{
buffer = null;
- object target = @delegate.Target;
+ object? target = @delegate.Target;
switch (target)
{
@@ -72,12 +125,14 @@ namespace Godot
return true;
}
}
+ // ReSharper disable once RedundantNameQualifier
case Godot.Object godotObject:
{
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write((ulong)TargetKind.GodotObject);
+ // ReSharper disable once RedundantCast
writer.Write((ulong)godotObject.GetInstanceId());
SerializeType(writer, @delegate.GetType());
@@ -93,7 +148,7 @@ namespace Godot
{
Type targetType = target.GetType();
- if (targetType.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
+ if (targetType.IsDefined(typeof(CompilerGeneratedAttribute), true))
{
// Compiler generated. Probably a closure. Try to serialize it.
@@ -121,8 +176,18 @@ namespace Godot
if (variantType == Variant.Type.Nil)
return false;
+ static byte[] VarToBytes(in godot_variant var)
+ {
+ NativeFuncs.godotsharp_var_to_bytes(var, false.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
+ }
+
writer.Write(field.Name);
- byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
+
+ var fieldValue = field.GetValue(target);
+ using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue);
+ byte[] valueBuffer = VarToBytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
}
@@ -139,9 +204,6 @@ namespace Godot
private static bool TrySerializeMethodInfo(BinaryWriter writer, MethodInfo methodInfo)
{
- if (methodInfo == null)
- return false;
-
SerializeType(writer, methodInfo.DeclaringType);
writer.Write(methodInfo.Name);
@@ -180,7 +242,7 @@ namespace Godot
return true;
}
- private static void SerializeType(BinaryWriter writer, Type type)
+ private static void SerializeType(BinaryWriter writer, Type? type)
{
if (type == null)
{
@@ -195,9 +257,8 @@ namespace Godot
int genericArgumentsCount = genericArgs.Length;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = genericTypeDef.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(genericTypeDef.Assembly.GetName().Name ?? "");
+ writer.Write(genericTypeDef.FullName ?? genericTypeDef.ToString());
for (int i = 0; i < genericArgs.Length; i++)
SerializeType(writer, genericArgs[i]);
@@ -207,17 +268,71 @@ namespace Godot
int genericArgumentsCount = 0;
writer.Write(genericArgumentsCount);
- string assemblyQualifiedName = type.AssemblyQualifiedName;
- Debug.Assert(assemblyQualifiedName != null);
- writer.Write(assemblyQualifiedName);
+ writer.Write(type.Assembly.GetName().Name ?? "");
+ writer.Write(type.FullName ?? type.ToString());
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TrySerializeDelegateWithGCHandle(IntPtr delegateGCHandle,
+ godot_array* nSerializedData)
+ {
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ var @delegate = (Delegate)GCHandle.FromIntPtr(delegateGCHandle).Target!;
+
+ return TrySerializeDelegate(@delegate, serializedData)
+ .ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.False;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe godot_bool TryDeserializeDelegateWithGCHandle(godot_array* nSerializedData,
+ IntPtr* delegateGCHandle)
+ {
+ try
+ {
+ var serializedData = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(*nSerializedData));
+
+ if (TryDeserializeDelegate(serializedData, out Delegate? @delegate))
+ {
+ *delegateGCHandle = GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(@delegate));
+ return godot_bool.True;
+ }
+ else
+ {
+ *delegateGCHandle = IntPtr.Zero;
+ return godot_bool.False;
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *delegateGCHandle = default;
+ return godot_bool.False;
}
}
- private static bool TryDeserializeDelegate(Collections.Array serializedData, out Delegate @delegate)
+ internal static bool TryDeserializeDelegate(Collections.Array serializedData,
+ [MaybeNullWhen(false)] out Delegate @delegate)
{
+ @delegate = null;
+
if (serializedData.Count == 1)
{
- object elem = serializedData[0];
+ var elem = serializedData[0].Obj;
+
+ if (elem == null)
+ return false;
if (elem is Collections.Array multiCastData)
return TryDeserializeDelegate(multiCastData, out @delegate);
@@ -225,20 +340,23 @@ namespace Godot
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
}
- @delegate = null;
-
var delegates = new List<Delegate>(serializedData.Count);
- foreach (object elem in serializedData)
+ foreach (Variant variantElem in serializedData)
{
+ var elem = variantElem.Obj;
+
+ if (elem == null)
+ continue;
+
if (elem is Collections.Array multiCastData)
{
- if (TryDeserializeDelegate(multiCastData, out Delegate oneDelegate))
+ if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
else
{
- if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
+ if (TryDeserializeSingleDelegate((byte[])elem, out Delegate? oneDelegate))
delegates.Add(oneDelegate);
}
}
@@ -246,11 +364,11 @@ namespace Godot
if (delegates.Count <= 0)
return false;
- @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray());
+ @delegate = delegates.Count == 1 ? delegates[0] : Delegate.Combine(delegates.ToArray())!;
return true;
}
- private static bool TryDeserializeSingleDelegate(byte[] buffer, out Delegate @delegate)
+ private static bool TryDeserializeSingleDelegate(byte[] buffer, [MaybeNullWhen(false)] out Delegate @delegate)
{
@delegate = null;
@@ -263,49 +381,59 @@ namespace Godot
{
case TargetKind.Static:
{
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo, throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, null, methodInfo);
return true;
}
case TargetKind.GodotObject:
{
ulong objectId = reader.ReadUInt64();
+ // ReSharper disable once RedundantNameQualifier
Godot.Object godotObject = GD.InstanceFromId(objectId);
if (godotObject == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
+ return false;
+
+ @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
return false;
- @delegate = Delegate.CreateDelegate(delegateType, godotObject, methodInfo);
return true;
}
case TargetKind.CompilerGenerated:
{
- Type targetType = DeserializeType(reader);
+ Type? targetType = DeserializeType(reader);
if (targetType == null)
return false;
- Type delegateType = DeserializeType(reader);
+ Type? delegateType = DeserializeType(reader);
if (delegateType == null)
return false;
- if (!TryDeserializeMethodInfo(reader, out MethodInfo methodInfo))
+ if (!TryDeserializeMethodInfo(reader, out MethodInfo? methodInfo))
return false;
int fieldCount = reader.ReadInt32();
- object recreatedTarget = Activator.CreateInstance(targetType);
+ object recreatedTarget = Activator.CreateInstance(targetType)!;
for (int i = 0; i < fieldCount; i++)
{
@@ -313,11 +441,24 @@ namespace Godot
int valueBufferLength = reader.ReadInt32();
byte[] valueBuffer = reader.ReadBytes(valueBufferLength);
- FieldInfo fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public);
- fieldInfo?.SetValue(recreatedTarget, GD.Bytes2Var(valueBuffer));
+ FieldInfo? fieldInfo = targetType.GetField(name,
+ BindingFlags.Instance | BindingFlags.Public);
+
+ if (fieldInfo != null)
+ {
+ var variantValue = GD.BytesToVar(valueBuffer);
+ object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType(
+ (godot_variant)variantValue.NativeVar, fieldInfo.FieldType);
+ fieldInfo.SetValue(recreatedTarget, managedValue);
+ }
}
- @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo);
+ @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo,
+ throwOnBindFailure: false);
+
+ if (@delegate == null)
+ return false;
+
return true;
}
default:
@@ -326,18 +467,22 @@ namespace Godot
}
}
- private static bool TryDeserializeMethodInfo(BinaryReader reader, out MethodInfo methodInfo)
+ private static bool TryDeserializeMethodInfo(BinaryReader reader,
+ [MaybeNullWhen(false)] out MethodInfo methodInfo)
{
methodInfo = null;
- Type declaringType = DeserializeType(reader);
+ Type? declaringType = DeserializeType(reader);
+
+ if (declaringType == null)
+ return false;
string methodName = reader.ReadString();
int flags = reader.ReadInt32();
bool hasReturn = reader.ReadBoolean();
- Type returnType = hasReturn ? DeserializeType(reader) : typeof(void);
+ Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
int parametersCount = reader.ReadInt32();
@@ -347,7 +492,7 @@ namespace Godot
for (int i = 0; i < parametersCount; i++)
{
- Type parameterType = DeserializeType(reader);
+ Type? parameterType = DeserializeType(reader);
if (parameterType == null)
return false;
parameterTypes[i] = parameterType;
@@ -361,15 +506,23 @@ namespace Godot
return methodInfo != null && methodInfo.ReturnType == returnType;
}
- private static Type DeserializeType(BinaryReader reader)
+ private static Type? DeserializeType(BinaryReader reader)
{
int genericArgumentsCount = reader.ReadInt32();
if (genericArgumentsCount == -1)
return null;
- string assemblyQualifiedName = reader.ReadString();
- var type = Type.GetType(assemblyQualifiedName);
+ string assemblyName = reader.ReadString();
+
+ if (assemblyName.Length == 0)
+ {
+ GD.PushError($"Missing assembly name of type when attempting to deserialize delegate");
+ return null;
+ }
+
+ string typeFullName = reader.ReadString();
+ var type = ReflectionUtils.FindTypeInLoadedAssemblies(assemblyName, typeFullName);
if (type == null)
return null; // Type not found
@@ -380,7 +533,7 @@ namespace Godot
for (int i = 0; i < genericArgumentsCount; i++)
{
- Type genericArgumentType = DeserializeType(reader);
+ Type? genericArgumentType = DeserializeType(reader);
if (genericArgumentType == null)
return null;
genericArgumentTypes[i] = genericArgumentType;
@@ -391,5 +544,289 @@ namespace Godot
return type;
}
+
+ internal static class RuntimeTypeConversionHelper
+ {
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static godot_variant ConvertToVariant(object? obj)
+ {
+ if (obj == null)
+ return default;
+
+ switch (obj)
+ {
+ case bool @bool:
+ return VariantUtils.CreateFrom(@bool);
+ case char @char:
+ return VariantUtils.CreateFrom(@char);
+ case sbyte int8:
+ return VariantUtils.CreateFrom(int8);
+ case short int16:
+ return VariantUtils.CreateFrom(int16);
+ case int int32:
+ return VariantUtils.CreateFrom(int32);
+ case long int64:
+ return VariantUtils.CreateFrom(int64);
+ case byte uint8:
+ return VariantUtils.CreateFrom(uint8);
+ case ushort uint16:
+ return VariantUtils.CreateFrom(uint16);
+ case uint uint32:
+ return VariantUtils.CreateFrom(uint32);
+ case ulong uint64:
+ return VariantUtils.CreateFrom(uint64);
+ case float @float:
+ return VariantUtils.CreateFrom(@float);
+ case double @double:
+ return VariantUtils.CreateFrom(@double);
+ case Vector2 vector2:
+ return VariantUtils.CreateFrom(vector2);
+ case Vector2i vector2I:
+ return VariantUtils.CreateFrom(vector2I);
+ case Rect2 rect2:
+ return VariantUtils.CreateFrom(rect2);
+ case Rect2i rect2I:
+ return VariantUtils.CreateFrom(rect2I);
+ case Transform2D transform2D:
+ return VariantUtils.CreateFrom(transform2D);
+ case Vector3 vector3:
+ return VariantUtils.CreateFrom(vector3);
+ case Vector3i vector3I:
+ return VariantUtils.CreateFrom(vector3I);
+ case Vector4 vector4:
+ return VariantUtils.CreateFrom(vector4);
+ case Vector4i vector4I:
+ return VariantUtils.CreateFrom(vector4I);
+ case Basis basis:
+ return VariantUtils.CreateFrom(basis);
+ case Quaternion quaternion:
+ return VariantUtils.CreateFrom(quaternion);
+ case Transform3D transform3d:
+ return VariantUtils.CreateFrom(transform3d);
+ case Projection projection:
+ return VariantUtils.CreateFrom(projection);
+ case AABB aabb:
+ return VariantUtils.CreateFrom(aabb);
+ case Color color:
+ return VariantUtils.CreateFrom(color);
+ case Plane plane:
+ return VariantUtils.CreateFrom(plane);
+ case Callable callable:
+ return VariantUtils.CreateFrom(callable);
+ case Signal signal:
+ return VariantUtils.CreateFrom(signal);
+ case string @string:
+ return VariantUtils.CreateFrom(@string);
+ case byte[] byteArray:
+ return VariantUtils.CreateFrom(byteArray);
+ case int[] int32Array:
+ return VariantUtils.CreateFrom(int32Array);
+ case long[] int64Array:
+ return VariantUtils.CreateFrom(int64Array);
+ case float[] floatArray:
+ return VariantUtils.CreateFrom(floatArray);
+ case double[] doubleArray:
+ return VariantUtils.CreateFrom(doubleArray);
+ case string[] stringArray:
+ return VariantUtils.CreateFrom(stringArray);
+ case Vector2[] vector2Array:
+ return VariantUtils.CreateFrom(vector2Array);
+ case Vector3[] vector3Array:
+ return VariantUtils.CreateFrom(vector3Array);
+ case Color[] colorArray:
+ return VariantUtils.CreateFrom(colorArray);
+ case StringName[] stringNameArray:
+ return VariantUtils.CreateFrom(stringNameArray);
+ case NodePath[] nodePathArray:
+ return VariantUtils.CreateFrom(nodePathArray);
+ case RID[] ridArray:
+ return VariantUtils.CreateFrom(ridArray);
+ case Godot.Object[] godotObjectArray:
+ return VariantUtils.CreateFrom(godotObjectArray);
+ case StringName stringName:
+ return VariantUtils.CreateFrom(stringName);
+ case NodePath nodePath:
+ return VariantUtils.CreateFrom(nodePath);
+ case RID rid:
+ return VariantUtils.CreateFrom(rid);
+ case Collections.Dictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary);
+ case Collections.Array godotArray:
+ return VariantUtils.CreateFrom(godotArray);
+ case Variant variant:
+ return VariantUtils.CreateFrom(variant);
+ case Godot.Object godotObject:
+ return VariantUtils.CreateFrom(godotObject);
+ case Enum @enum:
+ return VariantUtils.CreateFrom(Convert.ToInt64(@enum));
+ case Collections.IGenericGodotDictionary godotDictionary:
+ return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary);
+ case Collections.IGenericGodotArray godotArray:
+ return VariantUtils.CreateFrom(godotArray.UnderlyingArray);
+ }
+
+ GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
+ obj.GetType().FullName + ".");
+ return new godot_variant();
+ }
+
+ private delegate object? ConvertToSystemObjectFunc(in godot_variant managed);
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ // ReSharper disable once RedundantNameQualifier
+ private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc>
+ ToSystemObjectFuncByType = new()
+ {
+ [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant),
+ [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant),
+ [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant),
+ [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant),
+ [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant),
+ [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant),
+ [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant),
+ [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant),
+ [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant),
+ [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant),
+ [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant),
+ [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant),
+ [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant),
+ [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant),
+ [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant),
+ [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant),
+ [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant),
+ [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant),
+ [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant),
+ [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant),
+ [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant),
+ [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant),
+ [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant),
+ [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant),
+ [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant),
+ [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant),
+ [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant),
+ [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant),
+ [typeof(Signal)] = (in godot_variant variant) => VariantUtils.ConvertTo<Signal>(variant),
+ [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant),
+ [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant),
+ [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant),
+ [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant),
+ [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant),
+ [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant),
+ [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant),
+ [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant),
+ [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant),
+ [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant),
+ [typeof(StringName[])] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant),
+ [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant),
+ [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant),
+ [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant),
+ [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant),
+ [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant),
+ [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) =>
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant),
+ [typeof(Godot.Collections.Array)] =
+ (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant),
+ [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant),
+ };
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static object? ConvertToObjectOfType(in godot_variant variant, Type type)
+ {
+ if (ToSystemObjectFuncByType.TryGetValue(type, out var func))
+ return func(variant);
+
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type);
+
+ if (typeof(Godot.Object[]).IsAssignableFrom(type))
+ {
+ static Godot.Object[] ConvertToSystemArrayOfGodotObject(in godot_array nativeArray, Type type)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(nativeArray));
+
+ int length = array.Count;
+ var ret = (Godot.Object[])Activator.CreateInstance(type, length)!;
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(variant);
+ return Convert.ChangeType(ConvertToSystemArrayOfGodotObject(godotArray, type), type);
+ }
+
+ if (type.IsEnum)
+ {
+ var enumUnderlyingType = type.GetEnumUnderlyingType();
+
+ switch (Type.GetTypeCode(enumUnderlyingType))
+ {
+ case TypeCode.SByte:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant));
+ case TypeCode.Int16:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant));
+ case TypeCode.Int32:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant));
+ case TypeCode.Int64:
+ return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant));
+ case TypeCode.Byte:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant));
+ case TypeCode.UInt16:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant));
+ case TypeCode.UInt32:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant));
+ case TypeCode.UInt64:
+ return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant));
+ default:
+ {
+ GD.PushError(
+ "Attempted to convert Variant to enum value of unsupported underlying type. Name: " +
+ type.FullName + " : " + enumUnderlyingType.FullName + ".");
+ return null;
+ }
+ }
+ }
+
+ if (type.IsGenericType)
+ {
+ var genericTypeDef = type.GetGenericTypeDefinition();
+
+ if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Dictionary) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Dictionary constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant)
+ });
+ }
+
+ if (genericTypeDef == typeof(Godot.Collections.Array<>))
+ {
+ var ctor = type.GetConstructor(BindingFlags.Default,
+ new[] { typeof(Godot.Collections.Array) });
+
+ if (ctor == null)
+ throw new InvalidOperationException("Array constructor not found");
+
+ return ctor.Invoke(new object?[]
+ {
+ VariantUtils.ConvertTo<Godot.Collections.Array>(variant)
+ });
+ }
+ }
+
+ GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}.");
+ return null;
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index e80b6af68f..b5a8742d3d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -1,79 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot.Collections
{
- internal class DictionarySafeHandle : SafeHandle
- {
- public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
- {
- this.handle = handle;
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- protected override bool ReleaseHandle()
- {
- Dictionary.godot_icall_Dictionary_Dtor(handle);
- return true;
- }
- }
-
/// <summary>
/// Wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine.
/// </summary>
- public class Dictionary : IDictionary, IDisposable
+ public sealed class Dictionary :
+ IDictionary<Variant, Variant>,
+ IReadOnlyDictionary<Variant, Variant>,
+ IDisposable
{
- private DictionarySafeHandle _safeHandle;
- private bool _disposed = false;
+ internal godot_dictionary.movable NativeValue;
+
+ private WeakReference<IDisposable> _weakReferenceToSelf;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
- _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ NativeValue = (godot_dictionary.movable)NativeFuncs.godotsharp_dictionary_new();
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- /// <summary>
- /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
- /// </summary>
- /// <param name="dictionary">The dictionary to construct from.</param>
- /// <returns>A new Godot Dictionary.</returns>
- public Dictionary(IDictionary dictionary) : this()
+ private Dictionary(godot_dictionary nativeValueToOwn)
{
- if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- foreach (DictionaryEntry entry in dictionary)
- Add(entry.Key, entry.Value);
+ NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
+ nativeValueToOwn :
+ NativeFuncs.godotsharp_dictionary_new());
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
- internal Dictionary(DictionarySafeHandle handle)
- {
- _safeHandle = handle;
- }
-
- internal Dictionary(IntPtr handle)
- {
- _safeHandle = new DictionarySafeHandle(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary CreateTakingOwnershipOfDisposableValue(godot_dictionary nativeValueToOwn)
+ => new Dictionary(nativeValueToOwn);
- internal IntPtr GetPtr()
+ ~Dictionary()
{
- if (_disposed)
- throw new ObjectDisposedException(GetType().FullName);
-
- return _safeHandle.DangerousGetHandle();
+ Dispose(false);
}
/// <summary>
@@ -81,16 +52,19 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
- if (_disposed)
- return;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void Dispose(bool disposing)
+ {
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
- if (_safeHandle != null)
+ if (_weakReferenceToSelf != null)
{
- _safeHandle.Dispose();
- _safeHandle = null;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
/// <summary>
@@ -100,7 +74,10 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
- return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
+ godot_dictionary newDictionary;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_duplicate(ref self, deep.ToGodotBool(), out newDictionary);
+ return CreateTakingOwnershipOfDisposableValue(newDictionary);
}
// IDictionary
@@ -108,176 +85,250 @@ namespace Godot.Collections
/// <summary>
/// Gets the collection of keys in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Keys
+ public ICollection<Variant> Keys
{
get
{
- IntPtr handle = godot_icall_Dictionary_Keys(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array keysArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(keysArray);
}
}
/// <summary>
/// Gets the collection of elements in this <see cref="Dictionary"/>.
/// </summary>
- public ICollection Values
+ public ICollection<Variant> Values
{
get
{
- IntPtr handle = godot_icall_Dictionary_Values(GetPtr());
- return new Array(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys;
+
+ IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values;
+
private (Array keys, Array values, int count) GetKeyValuePairs()
{
- int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
- Array keys = new Array(new ArraySafeHandle(keysHandle));
- Array values = new Array(new ArraySafeHandle(valuesHandle));
- return (keys, values, count);
- }
+ var self = (godot_dictionary)NativeValue;
+
+ godot_array keysArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keysArray);
+ var keys = Array.CreateTakingOwnershipOfDisposableValue(keysArray);
+
+ godot_array valuesArray;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out valuesArray);
+ var values = Array.CreateTakingOwnershipOfDisposableValue(valuesArray);
- bool IDictionary.IsFixedSize => false;
+ int count = NativeFuncs.godotsharp_dictionary_count(ref self);
- bool IDictionary.IsReadOnly => false;
+ return (keys, values, count);
+ }
/// <summary>
- /// Returns the object at the given <paramref name="key"/>.
+ /// Returns the value at the given <paramref name="key"/>.
/// </summary>
- /// <value>The object at the given <paramref name="key"/>.</value>
- public object this[object key]
+ /// <value>The value at the given <paramref name="key"/>.</value>
+ public Variant this[Variant key]
{
- get => godot_icall_Dictionary_GetValue(GetPtr(), key);
- set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant value).ToBool())
+ {
+ return Variant.CreateTakingOwnershipOfDisposableValue(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ (godot_variant)key.NativeVar, (godot_variant)value.NativeVar);
+ }
}
/// <summary>
- /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// Adds an value <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary"/>.
/// </summary>
- /// <param name="key">The key at which to add the object.</param>
- /// <param name="value">The object to add.</param>
- public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
+ /// <param name="key">The key at which to add the value.</param>
+ /// <param name="value">The value to add.</param>
+ public void Add(Variant key, Variant value)
+ {
+ var variantKey = (godot_variant)key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists.", nameof(key));
+
+ godot_variant variantValue = (godot_variant)value.NativeVar;
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
+ }
+
+ void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item)
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all items from this <see cref="Dictionary"/>.
/// </summary>
- public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
+ public void Clear()
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_clear(ref self);
+ }
/// <summary>
/// Checks if this <see cref="Dictionary"/> contains the given key.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
- public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ public bool ContainsKey(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- /// <summary>
- /// Gets an enumerator for this <see cref="Dictionary"/>.
- /// </summary>
- /// <returns>An enumerator.</returns>
- public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+ public bool Contains(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
+ }
/// <summary>
/// Removes an element from this <see cref="Dictionary"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
- public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ public bool Remove(Variant key)
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
+ }
- // ICollection
+ public bool Remove(KeyValuePair<Variant, Variant> item)
+ {
+ godot_variant variantKey = (godot_variant)item.Key.NativeVar;
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
- object ICollection.SyncRoot => this;
+ using (retValue)
+ {
+ if (!found)
+ return false;
- bool ICollection.IsSynchronized => false;
+ godot_variant variantValue = (godot_variant)item.Value.NativeVar;
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
+
+ return false;
+ }
+ }
/// <summary>
/// Returns the number of elements in this <see cref="Dictionary"/>.
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count => godot_icall_Dictionary_Count(GetPtr());
+ public int Count
+ {
+ get
+ {
+ var self = (godot_dictionary)NativeValue;
+ return NativeFuncs.godotsharp_dictionary_count(ref self);
+ }
+ }
+
+ bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false;
+
+ public bool TryGetValue(Variant key, out Variant value)
+ {
+ var self = (godot_dictionary)NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ (godot_variant)key.NativeVar, out godot_variant retValue).ToBool();
+
+ value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default;
+
+ return found;
+ }
/// <summary>
- /// Copies the elements of this <see cref="Dictionary"/> to the given
- /// untyped C# array, starting at the given index.
+ /// Copies the elements of this <see cref="Dictionary"/> to the given untyped
+ /// <see cref="KeyValuePair{TKey, TValue}"/> array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
- /// <param name="index">The index to start at.</param>
- public void CopyTo(System.Array array, int index)
+ /// <param name="arrayIndex">The index to start at.</param>
+ public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
- if (index < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
+ if (arrayIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
var (keys, values, count) = GetKeyValuePairs();
- if (array.Length < (index + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ if (array.Length < (arrayIndex + count))
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
- array.SetValue(new DictionaryEntry(keys[i], values[i]), index);
- index++;
+ array[arrayIndex] = new(keys[i], values[i]);
+ arrayIndex++;
}
}
// IEnumerable
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- private class DictionaryEnumerator : IDictionaryEnumerator
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
+ public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator()
{
- private readonly Dictionary _dictionary;
- private readonly int _count;
- private int _index = -1;
- private bool _dirty = true;
-
- private DictionaryEntry _entry;
-
- public DictionaryEnumerator(Dictionary dictionary)
- {
- _dictionary = dictionary;
- _count = dictionary.Count;
- }
-
- public object Current => Entry;
-
- public DictionaryEntry Entry
- {
- get
- {
- if (_dirty)
- {
- UpdateEntry();
- }
- return _entry;
- }
- }
-
- private void UpdateEntry()
+ for (int i = 0; i < Count; i++)
{
- _dirty = false;
- godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value);
- _entry = new DictionaryEntry(key, value);
+ yield return GetKeyValuePair(i);
}
+ }
- public object Key => Entry.Key;
-
- public object Value => Entry.Value;
-
- public bool MoveNext()
- {
- _index++;
- _dirty = true;
- return _index < _count;
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- public void Reset()
- {
- _index = -1;
- _dirty = true;
- }
+ private KeyValuePair<Variant, Variant> GetKeyValuePair(int index)
+ {
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key),
+ Variant.CreateTakingOwnershipOfDisposableValue(value));
}
/// <summary>
@@ -286,74 +337,16 @@ namespace Godot.Collections
/// <returns>A string representation of this dictionary.</returns>
public override string ToString()
{
- return godot_icall_Dictionary_ToString(GetPtr());
+ var self = (godot_dictionary)NativeValue;
+ NativeFuncs.godotsharp_dictionary_to_string(ref self, out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Ctor();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_Count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_KeyValuePairAt_Generic(IntPtr ptr, int index, out object key, out object value, int valueTypeEncoding, IntPtr valueTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr);
+ internal interface IGenericGodotDictionary
+ {
+ public Dictionary UnderlyingDictionary { get; }
}
/// <summary>
@@ -364,16 +357,31 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
- public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>
+ public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> :
+ IDictionary<TKey, TValue>,
+ IReadOnlyDictionary<TKey, TValue>,
+ IGenericGodotDictionary
{
- private readonly Dictionary _objectDict;
+ private static godot_variant ToVariantFunc(in Dictionary<TKey, TValue> godotDictionary) =>
+ VariantUtils.CreateFromDictionary(godotDictionary);
- internal static int valTypeEncoding;
- internal static IntPtr valTypeClass;
+ private static Dictionary<TKey, TValue> FromVariantFunc(in godot_variant variant) =>
+ VariantUtils.ConvertToDictionary<TKey, TValue>(variant);
- static Dictionary()
+ static unsafe Dictionary()
{
- Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
+ VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.ToVariantCb = &ToVariantFunc;
+ VariantUtils.GenericConversion<Dictionary<TKey, TValue>>.FromVariantCb = &FromVariantFunc;
+ }
+
+ private readonly Dictionary _underlyingDict;
+
+ Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict;
+
+ internal ref godot_dictionary.movable NativeValue
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref _underlyingDict.NativeValue;
}
/// <summary>
@@ -381,7 +389,7 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
- _objectDict = new Dictionary();
+ _underlyingDict = new Dictionary();
}
/// <summary>
@@ -391,19 +399,13 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
- _objectDict = new Dictionary();
-
if (dictionary == null)
- throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
-
- // TODO: Can be optimized
+ throw new ArgumentNullException(nameof(dictionary));
- IntPtr godotDictionaryPtr = GetPtr();
+ _underlyingDict = new Dictionary();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
- {
- Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value);
- }
+ Add(entry.Key, entry.Value);
}
/// <summary>
@@ -413,18 +415,13 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
- _objectDict = dictionary;
+ _underlyingDict = dictionary;
}
- internal Dictionary(IntPtr handle)
- {
- _objectDict = new Dictionary(handle);
- }
-
- internal Dictionary(DictionarySafeHandle handle)
- {
- _objectDict = new Dictionary(handle);
- }
+ // Explicit name to make it very clear
+ internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
+ godot_dictionary nativeValueToOwn)
+ => new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
@@ -432,12 +429,7 @@ namespace Godot.Collections
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
- return from._objectDict;
- }
-
- internal IntPtr GetPtr()
- {
- return _objectDict.GetPtr();
+ return from?._underlyingDict;
}
/// <summary>
@@ -447,7 +439,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
- return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep));
+ return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
@@ -458,8 +450,30 @@ namespace Godot.Collections
/// <value>The value at the given <paramref name="key"/>.</value>
public TValue this[TKey key]
{
- get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
- set { _objectDict[key] = value; }
+ get
+ {
+ using var variantKey = VariantUtils.CreateFrom(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant value).ToBool())
+ {
+ using (value)
+ return VariantUtils.ConvertTo<TValue>(value);
+ }
+ else
+ {
+ throw new KeyNotFoundException();
+ }
+ }
+ set
+ {
+ using var variantKey = VariantUtils.CreateFrom(key);
+ using var variantValue = VariantUtils.CreateFrom(value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_set_value(ref self,
+ variantKey, variantValue);
+ }
}
/// <summary>
@@ -469,8 +483,10 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr());
- return new Array<TKey>(new ArraySafeHandle(handle));
+ godot_array keyArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray);
+ return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
@@ -481,15 +497,30 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr());
- return new Array<TValue>(new ArraySafeHandle(handle));
+ godot_array valuesArray;
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
+ return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
+ IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
+
+ IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
+
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
- Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass);
- return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
+ out godot_variant key,
+ out godot_variant value);
+ using (key)
+ using (value)
+ {
+ return new KeyValuePair<TKey, TValue>(
+ VariantUtils.ConvertTo<TKey>(key),
+ VariantUtils.ConvertTo<TValue>(value));
+ }
}
/// <summary>
@@ -500,7 +531,14 @@ namespace Godot.Collections
/// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
- _objectDict.Add(key, value);
+ using var variantKey = VariantUtils.CreateFrom(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+
+ if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
+ throw new ArgumentException("An element with the same key already exists.", nameof(key));
+
+ using var variantValue = VariantUtils.CreateFrom(value);
+ NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
/// <summary>
@@ -510,7 +548,9 @@ namespace Godot.Collections
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(TKey key)
{
- return _objectDict.Contains(key);
+ using var variantKey = VariantUtils.CreateFrom(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
}
/// <summary>
@@ -519,7 +559,9 @@ namespace Godot.Collections
/// <param name="key">The key of the element to remove.</param>
public bool Remove(TKey key)
{
- return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
+ using var variantKey = VariantUtils.CreateFrom(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
/// <summary>
@@ -530,8 +572,14 @@ namespace Godot.Collections
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
- bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default;
+ using var variantKey = VariantUtils.CreateFrom(key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ value = found ? VariantUtils.ConvertTo<TValue>(retValue) : default;
+
return found;
}
@@ -542,29 +590,33 @@ namespace Godot.Collections
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
- public int Count
- {
- get { return _objectDict.Count; }
- }
+ public int Count => _underlyingDict.Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
- {
- _objectDict.Add(item.Key, item.Value);
- }
+ => Add(item.Key, item.Value);
/// <summary>
/// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
- public void Clear()
- {
- _objectDict.Clear();
- }
+ public void Clear() => _underlyingDict.Clear();
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
- return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ using var variantKey = VariantUtils.CreateFrom(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = VariantUtils.CreateFrom(item.Value);
+ return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
+ }
}
/// <summary>
@@ -579,12 +631,14 @@ namespace Godot.Collections
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex),
+ "Number was less than the array's lower bound in the first dimension.");
int count = Count;
if (array.Length < (arrayIndex + count))
- throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
+ throw new ArgumentException(
+ "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
@@ -595,8 +649,25 @@ namespace Godot.Collections
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
- return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
- ;
+ using var variantKey = VariantUtils.CreateFrom(item.Key);
+ var self = (godot_dictionary)_underlyingDict.NativeValue;
+ bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
+ variantKey, out godot_variant retValue).ToBool();
+
+ using (retValue)
+ {
+ if (!found)
+ return false;
+
+ using var variantValue = VariantUtils.CreateFrom(item.Value);
+ if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
+ {
+ return NativeFuncs.godotsharp_dictionary_remove_key(
+ ref self, variantKey).ToBool();
+ }
+
+ return false;
+ }
}
// IEnumerable<KeyValuePair<TKey, TValue>>
@@ -613,15 +684,19 @@ namespace Godot.Collections
}
}
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
- public override string ToString() => _objectDict.ToString();
+ public override string ToString() => _underlyingDict.ToString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.CreateFrom(from);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Dictionary<TKey, TValue>(Variant from) =>
+ from.AsGodotDictionary<TKey, TValue>();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 6475237002..e6cb4171a7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -1,20 +1,16 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
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();
+ internal static GodotTaskScheduler DefaultGodotTaskScheduler;
- /// <summary>
- /// Initializes the synchronization context as the context of the GodotTaskScheduler.
- /// </summary>
- public static GodotSynchronizationContext SynchronizationContext =>
- godot_icall_DefaultGodotTaskScheduler().Context;
+ internal static void InitializeDefaultGodotTaskScheduler()
+ => DefaultGodotTaskScheduler = new GodotTaskScheduler();
+
+ public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
new file mode 100644
index 0000000000..421b588560
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DisposablesTracker.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Concurrent;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
+
+#nullable enable
+
+namespace Godot
+{
+ internal static class DisposablesTracker
+ {
+ [UnmanagedCallersOnly]
+ internal static void OnGodotShuttingDown()
+ {
+ try
+ {
+ OnGodotShuttingDownImpl();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
+ }
+
+ private static void OnGodotShuttingDownImpl()
+ {
+ bool isStdoutVerbose;
+
+ try
+ {
+ isStdoutVerbose = OS.IsStdoutVerbose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // OS singleton already disposed. Maybe OnUnloading was called twice.
+ isStdoutVerbose = false;
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Disposing tracked instances...");
+
+ // Dispose Godot Objects first, and only then dispose other disposables
+ // like StringName, NodePath, Godot.Collections.Array/Dictionary, etc.
+ // The Godot Object Dispose() method may need any of the later instances.
+
+ foreach (WeakReference<Object> item in GodotObjectInstances.Keys)
+ {
+ if (item.TryGetTarget(out Object? self))
+ self.Dispose();
+ }
+
+ foreach (WeakReference<IDisposable> item in OtherInstances.Keys)
+ {
+ if (item.TryGetTarget(out IDisposable? self))
+ self.Dispose();
+ }
+
+ if (isStdoutVerbose)
+ GD.Print("Unloading: Finished disposing tracked instances.");
+ }
+
+ // ReSharper disable once RedundantNameQualifier
+ private static ConcurrentDictionary<WeakReference<Godot.Object>, byte> GodotObjectInstances { get; } =
+ new();
+
+ private static ConcurrentDictionary<WeakReference<IDisposable>, byte> OtherInstances { get; } =
+ new();
+
+ public static WeakReference<Object> RegisterGodotObject(Object godotObject)
+ {
+ var weakReferenceToSelf = new WeakReference<Object>(godotObject);
+ GodotObjectInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static WeakReference<IDisposable> RegisterDisposable(IDisposable disposable)
+ {
+ var weakReferenceToSelf = new WeakReference<IDisposable>(disposable);
+ OtherInstances.TryAdd(weakReferenceToSelf, 0);
+ return weakReferenceToSelf;
+ }
+
+ public static void UnregisterGodotObject(Object godotObject, WeakReference<Object> weakReferenceToSelf)
+ {
+ if (!GodotObjectInstances.TryRemove(weakReferenceToSelf, out _))
+ throw new ArgumentException("Godot Object not registered.", nameof(weakReferenceToSelf));
+ }
+
+ public static void UnregisterDisposable(WeakReference<IDisposable> weakReference)
+ {
+ if (!OtherInstances.TryRemove(weakReference, out _))
+ throw new ArgumentException("Disposable not registered.", nameof(weakReference));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
deleted file mode 100644
index 26d5f9c796..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Dynamic;
-using System.Linq.Expressions;
-using System.Runtime.CompilerServices;
-
-namespace Godot
-{
- /// <summary>
- /// Represents an <see cref="Object"/> whose members can be dynamically accessed at runtime through the Variant API.
- /// </summary>
- /// <remarks>
- /// <para>
- /// The <see cref="DynamicGodotObject"/> class enables access to the Variant
- /// members of a <see cref="Object"/> instance at runtime.
- /// </para>
- /// <para>
- /// This allows accessing the class members using their original names in the engine as well as the members from the
- /// script attached to the <see cref="Object"/>, regardless of the scripting language it was written in.
- /// </para>
- /// </remarks>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Object"/>.
- /// <code>
- /// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
- /// sprite.add_child(this);
- ///
- /// if ((sprite.hframes * sprite.vframes) &gt; 0)
- /// sprite.frame = 0;
- /// </code>
- /// </example>
- /// <example>
- /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Object"/>.
- /// <code>
- /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
- ///
- /// if (childNode.print_allowed)
- /// {
- /// childNode.message = "Hello from C#";
- /// childNode.print_message(3);
- /// }
- /// </code>
- /// The <c>ChildNode</c> node has the following GDScript script attached:
- /// <code>
- /// // # ChildNode.gd
- /// // var print_allowed = true
- /// // var message = ""
- /// //
- /// // func print_message(times):
- /// // for i in times:
- /// // print(message)
- /// </code>
- /// </example>
- public class DynamicGodotObject : DynamicObject
- {
- /// <summary>
- /// Gets the <see cref="Object"/> associated with this <see cref="DynamicGodotObject"/>.
- /// </summary>
- public Object Value { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DynamicGodotObject"/> class.
- /// </summary>
- /// <param name="godotObject">
- /// The <see cref="Object"/> that will be associated with this <see cref="DynamicGodotObject"/>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// Thrown when the <paramref name="godotObject"/> parameter is <see langword="null"/>.
- /// </exception>
- public DynamicGodotObject(Object godotObject)
- {
- if (godotObject == null)
- throw new ArgumentNullException(nameof(godotObject));
-
- Value = godotObject;
- }
-
- /// <inheritdoc/>
- public override IEnumerable<string> GetDynamicMemberNames()
- {
- return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
- }
-
- /// <inheritdoc/>
- public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
- {
- switch (binder.Operation)
- {
- case ExpressionType.Equal:
- case ExpressionType.NotEqual:
- if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool)))
- {
- if (arg == null)
- {
- bool boolResult = Object.IsInstanceValid(Value);
-
- if (binder.Operation == ExpressionType.Equal)
- boolResult = !boolResult;
-
- result = boolResult;
- return true;
- }
-
- if (arg is Object other)
- {
- bool boolResult = (Value == other);
-
- if (binder.Operation == ExpressionType.NotEqual)
- boolResult = !boolResult;
-
- result = boolResult;
- return true;
- }
- }
-
- break;
- default:
- // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual).
- // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly.
- break;
- }
-
- return base.TryBinaryOperation(binder, arg, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryConvert(ConvertBinder binder, out object result)
- {
- if (binder.Type == typeof(Object))
- {
- result = Value;
- return true;
- }
-
- if (typeof(Object).IsAssignableFrom(binder.Type))
- {
- // Throws InvalidCastException when the cast fails
- result = Convert.ChangeType(Value, binder.Type);
- return true;
- }
-
- return base.TryConvert(binder, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
- {
- if (indexes.Length == 1)
- {
- if (indexes[0] is string name)
- {
- return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result);
- }
- }
-
- return base.TryGetIndex(binder, indexes, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryGetMember(GetMemberBinder binder, out object result)
- {
- return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
- }
-
- /// <inheritdoc/>
- public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
- {
- return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
- }
-
- /// <inheritdoc/>
- public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
- {
- if (indexes.Length == 1)
- {
- if (indexes[0] is string name)
- {
- return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value);
- }
- }
-
- return base.TrySetIndex(binder, indexes, value);
- }
-
- /// <inheritdoc/>
- public override bool TrySetMember(SetMemberBinder binder, object value)
- {
- return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value);
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
-
- #region We don't override these methods
-
- // Looks like this is not usable from C#
- //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);
-
- // Object members cannot be deleted
- //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);
- //public override bool TryDeleteMember(DeleteMemberBinder binder);
-
- // Invocation on the object itself, e.g.: obj(param)
- //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result);
-
- // No unnary operations to handle
- //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
-
- #endregion
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
index 1dc21b6303..8325af034c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
@@ -36,7 +36,7 @@ namespace Godot
/// <seealso cref="GetNodeOrNull{T}(NodePath)"/>
/// <param name="path">The path to the node to fetch.</param>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// The fetched node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
@@ -48,13 +48,8 @@ namespace Godot
}
/// <summary>
- /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
- /// the current node) or an absolute path (in the scene tree) to a node. If the path
- /// does not exist, a <see langword="null"/> instance is returned and an error
- /// is logged. Attempts to access methods on the return value will result in an
- /// "Attempt to call &lt;method&gt; on a null instance." error.
- /// Note: Fetching absolute paths only works when the node is inside the scene tree
- /// (see <see cref="IsInsideTree"/>).
+ /// Similar to <see cref="GetNode"/>, but does not log an error if <paramref name="path"/>
+ /// does not point to a valid <see cref="Node"/>.
/// </summary>
/// <example>
/// Example: Assume your current node is Character and the following tree:
@@ -93,18 +88,22 @@ namespace Godot
/// Negative indices access the children from the last one.
/// To access a child node via its name, use <see cref="GetNode"/>.
/// </summary>
- /// <seealso cref="GetChildOrNull{T}(int)"/>
+ /// <seealso cref="GetChildOrNull{T}(int, bool)"/>
/// <param name="idx">Child index.</param>
+ /// <param name="includeInternal">
+ /// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
+ /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
+ /// </param>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// The fetched node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>.
/// </returns>
- public T GetChild<T>(int idx) where T : class
+ public T GetChild<T>(int idx, bool includeInternal = false) where T : class
{
- return (T)(object)GetChild(idx);
+ return (T)(object)GetChild(idx, includeInternal);
}
/// <summary>
@@ -113,15 +112,20 @@ namespace Godot
/// Negative indices access the children from the last one.
/// To access a child node via its name, use <see cref="GetNode"/>.
/// </summary>
- /// <seealso cref="GetChild{T}(int)"/>
+ /// <seealso cref="GetChild{T}(int, bool)"/>
/// <param name="idx">Child index.</param>
+ /// <param name="includeInternal">
+ /// If <see langword="false"/>, internal children are skipped (see <c>internal</c>
+ /// parameter in <see cref="AddChild(Node, bool, InternalMode)"/>).
+ /// </param>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
/// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found.
/// </returns>
- public T GetChildOrNull<T>(int idx) where T : class
+ public T GetChildOrNull<T>(int idx, bool includeInternal = false) where T : class
{
- return GetChild(idx) as T;
+ int count = GetChildCount(includeInternal);
+ return idx >= -count && idx < count ? GetChild(idx, includeInternal) as T : null;
}
/// <summary>
@@ -133,7 +137,7 @@ namespace Godot
/// </summary>
/// <seealso cref="GetOwnerOrNull{T}"/>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// The fetched node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
@@ -167,7 +171,7 @@ namespace Godot
/// </summary>
/// <seealso cref="GetParentOrNull{T}"/>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// The fetched node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
index 691fd85964..4094ceeb22 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -32,10 +32,17 @@ namespace Godot
/// </returns>
public static WeakRef WeakRef(Object obj)
{
- return godot_icall_Object_weakref(GetPtr(obj));
- }
+ if (!IsInstanceValid(obj))
+ return null;
+
+ NativeFuncs.godotsharp_weakref(GetPtr(obj), out godot_ref weakRef);
+ using (weakRef)
+ {
+ if (weakRef.IsNull)
+ return null;
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj);
+ return (WeakRef)InteropUtils.UnmanagedGetManaged(weakRef.Reference);
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
index 435b59d5f3..8463403096 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
@@ -11,7 +11,7 @@ namespace Godot
/// </summary>
/// <seealso cref="InstantiateOrNull{T}(GenEditState)"/>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the instantiated node can't be casted to the given type <typeparamref name="T"/>.
+ /// The instantiated node can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
/// <returns>The instantiated scene.</returns>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
index 25c11d5cf6..b246e56fa9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
@@ -19,7 +19,7 @@ namespace Godot
/// Returns an empty resource if no <see cref="ResourceFormatLoader"/> could handle the file.
/// </summary>
/// <exception cref="InvalidCastException">
- /// Thrown when the given the loaded resource can't be casted to the given type <typeparamref name="T"/>.
+ /// The loaded resource can't be casted to the given type <typeparamref name="T"/>.
/// </exception>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam>
public static T Load<T>(string path, string typeHint = null, CacheMode cacheMode = CacheMode.Reuse) where T : class
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
deleted file mode 100644
index df130a5c77..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using Godot.Collections;
-
-namespace Godot
-{
- public partial class SceneTree
- {
- /// <summary>
- /// Returns a list of all nodes assigned to the given <paramref name="group"/>.
- /// </summary>
- /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
- public Array<T> GetNodesInGroup<T>(StringName group) where T : class
- {
- return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T)));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index bb076a9633..e4b79e7ec4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,11 +1,6 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Collections.Generic;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -26,9 +21,11 @@ namespace Godot
/// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
/// <param name="allowObjects">If objects should be decoded.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
+ public static Variant BytesToVar(Span<byte> bytes, bool allowObjects = false)
{
- return godot_icall_GD_bytes2var(bytes, allowObjects);
+ using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
+ NativeFuncs.godotsharp_bytes_to_var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@@ -46,23 +43,24 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
- public static object Convert(object what, Variant.Type type)
+ public static Variant Convert(Variant what, Variant.Type type)
{
- return godot_icall_GD_convert(what, type);
+ NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
/// Converts from decibels to linear energy (audio).
/// </summary>
- /// <seealso cref="Linear2Db(real_t)"/>
+ /// <seealso cref="LinearToDb(real_t)"/>
/// <param name="db">Decibels to convert.</param>
/// <returns>Audio volume as linear energy.</returns>
- public static real_t Db2Linear(real_t db)
+ public static real_t DbToLinear(real_t db)
{
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
- private static object[] GetPrintParams(object[] parameters)
+ private static string[] GetPrintParams(object[] parameters)
{
if (parameters == null)
{
@@ -82,9 +80,9 @@ namespace Godot
/// </example>
/// <param name="var">Variable that will be hashed.</param>
/// <returns>Hash of the variable passed.</returns>
- public static int Hash(object var)
+ public static int Hash(Variant var)
{
- return godot_icall_GD_hash(var);
+ return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar);
}
/// <summary>
@@ -110,25 +108,25 @@ namespace Godot
/// <returns>The <see cref="Object"/> instance.</returns>
public static Object InstanceFromId(ulong instanceId)
{
- return godot_icall_GD_instance_from_id(instanceId);
+ return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId));
}
/// <summary>
/// Converts from linear energy to decibels (audio).
/// This can be used to implement volume sliders that behave as expected (since volume isn't linear).
/// </summary>
- /// <seealso cref="Db2Linear(real_t)"/>
+ /// <seealso cref="DbToLinear(real_t)"/>
/// <example>
/// <code>
/// // "slider" refers to a node that inherits Range such as HSlider or VSlider.
/// // Its range must be configured to go from 0 to 1.
/// // Change the bus name if you'd like to change the volume of a specific bus only.
- /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.Linear2Db(slider.value));
+ /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value));
/// </code>
/// </example>
/// <param name="linear">The linear energy to convert.</param>
/// <returns>Audio as decibels.</returns>
- public static real_t Linear2Db(real_t linear)
+ public static real_t LinearToDb(real_t linear)
{
return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
}
@@ -191,8 +189,6 @@ namespace Godot
/// Pushes an error message to Godot's built-in debugger and to the OS terminal.
///
/// Note: Errors printed this way will not pause project execution.
- /// To print an error message and pause project execution in debug builds,
- /// use [code]assert(false, "test error")[/code] instead.
/// </summary>
/// <example>
/// <code>
@@ -202,7 +198,8 @@ namespace Godot
/// <param name="message">Error message.</param>
public static void PushError(string message)
{
- godot_icall_GD_pusherror(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pusherror(godotStr);
}
/// <summary>
@@ -214,7 +211,8 @@ namespace Godot
/// <param name="message">Warning message.</param>
public static void PushWarning(string message)
{
- godot_icall_GD_pushwarning(message);
+ using var godotStr = Marshaling.ConvertStringToNative(message);
+ NativeFuncs.godotsharp_pushwarning(godotStr);
}
/// <summary>
@@ -235,15 +233,26 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void Print(params object[] what)
{
- godot_icall_GD_print(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print(godotStr);
}
/// <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.
+ /// 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 <c>red</c>, not 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[])"/>.
+ /// 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>
@@ -253,10 +262,11 @@ namespace Godot
/// </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));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_print_rich(godotStr);
}
/// <summary>
@@ -278,7 +288,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintErr(params object[] what)
{
- godot_icall_GD_printerr(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printerr(godotStr);
}
/// <summary>
@@ -298,7 +310,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintRaw(params object[] what)
{
- godot_icall_GD_printraw(GetPrintParams(what));
+ string str = string.Concat(GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printraw(godotStr);
}
/// <summary>
@@ -312,7 +326,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintS(params object[] what)
{
- godot_icall_GD_prints(GetPrintParams(what));
+ string str = string.Join(' ', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_prints(godotStr);
}
/// <summary>
@@ -326,7 +342,9 @@ namespace Godot
/// <param name="what">Arguments that will be printed.</param>
public static void PrintT(params object[] what)
{
- godot_icall_GD_printt(GetPrintParams(what));
+ string str = string.Join('\t', GetPrintParams(what));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_printt(godotStr);
}
/// <summary>
@@ -340,7 +358,7 @@ namespace Godot
/// <returns>A random <see langword="float"/> number.</returns>
public static float Randf()
{
- return godot_icall_GD_randf();
+ return NativeFuncs.godotsharp_randf();
}
/// <summary>
@@ -350,7 +368,7 @@ namespace Godot
/// <returns>A random normally-distributed <see langword="float"/> number.</returns>
public static double Randfn(double mean, double deviation)
{
- return godot_icall_GD_randfn(mean, deviation);
+ return NativeFuncs.godotsharp_randfn(mean, deviation);
}
/// <summary>
@@ -368,7 +386,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
public static uint Randi()
{
- return godot_icall_GD_randi();
+ return NativeFuncs.godotsharp_randi();
}
/// <summary>
@@ -381,7 +399,7 @@ namespace Godot
/// </summary>
public static void Randomize()
{
- godot_icall_GD_randomize();
+ NativeFuncs.godotsharp_randomize();
}
/// <summary>
@@ -396,7 +414,7 @@ namespace Godot
/// <returns>A random <see langword="double"/> number inside the given range.</returns>
public static double RandRange(double from, double to)
{
- return godot_icall_GD_randf_range(from, to);
+ return NativeFuncs.godotsharp_randf_range(from, to);
}
/// <summary>
@@ -413,7 +431,7 @@ namespace Godot
/// <returns>A random <see langword="int"/> number inside the given range.</returns>
public static int RandRange(int from, int to)
{
- return godot_icall_GD_randi_range(from, to);
+ return NativeFuncs.godotsharp_randi_range(from, to);
}
/// <summary>
@@ -426,7 +444,7 @@ namespace Godot
/// <returns>A random <see langword="uint"/> number.</returns>
public static uint RandFromSeed(ref ulong seed)
{
- return godot_icall_GD_rand_seed(seed, out seed);
+ return NativeFuncs.godotsharp_rand_from_seed(seed, out seed);
}
/// <summary>
@@ -483,7 +501,7 @@ namespace Godot
/// <param name="seed">Seed that will be used.</param>
public static void Seed(ulong seed)
{
- godot_icall_GD_seed(seed);
+ NativeFuncs.godotsharp_seed(seed);
}
/// <summary>
@@ -491,59 +509,57 @@ namespace Godot
/// </summary>
/// <param name="what">Arguments that will converted to string.</param>
/// <returns>The string formed by the given arguments.</returns>
- public static string Str(params object[] what)
+ public static string Str(params Variant[] what)
{
- return godot_icall_GD_str(what);
+ using var whatGodot = new Godot.Collections.Array(what);
+ NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
- /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value.
+ /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> to the original value.
/// </summary>
/// <example>
/// <code>
/// string a = "{\"a\": 1, \"b\": 2 }";
- /// var b = (Godot.Collections.Dictionary)GD.Str2Var(a);
+ /// var b = (Godot.Collections.Dictionary)GD.StrToVar(a);
/// GD.Print(b["a"]); // Prints 1
/// </code>
/// </example>
/// <param name="str">String that will be converted to Variant.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
- public static object Str2Var(string str)
+ public static Variant StrToVar(string str)
{
- return godot_icall_GD_str2var(str);
- }
-
- /// <summary>
- /// Returns whether the given class exists in <see cref="ClassDB"/>.
- /// </summary>
- /// <returns>If the class exists in <see cref="ClassDB"/>.</returns>
- public static bool TypeExists(StringName type)
- {
- return godot_icall_GD_type_exists(StringName.GetPtr(type));
+ using var godotStr = Marshaling.ConvertStringToNative(str);
+ NativeFuncs.godotsharp_str_to_var(godotStr, out godot_variant ret);
+ return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
/// Encodes a <c>Variant</c> value to a byte array.
/// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
/// (and can potentially include code).
- /// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>.
+ /// Deserialization can be done with <see cref="BytesToVar(Span{byte}, bool)"/>.
/// </summary>
/// <param name="var">Variant that will be encoded.</param>
/// <param name="fullObjects">If objects should be serialized.</param>
/// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
- public static byte[] Var2Bytes(object var, bool fullObjects = false)
+ public static byte[] VarToBytes(Variant var, bool fullObjects = false)
{
- return godot_icall_GD_var2bytes(var, fullObjects);
+ NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes);
+ using (varBytes)
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
/// <summary>
/// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that
- /// can later be parsed using <see cref="Str2Var(string)"/>.
+ /// can later be parsed using <see cref="StrToVar(string)"/>.
/// </summary>
/// <example>
/// <code>
/// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 };
- /// GD.Print(GD.Var2Str(a));
+ /// GD.Print(GD.VarToStr(a));
/// // Prints
/// // {
/// // "a": 1,
@@ -553,9 +569,11 @@ namespace Godot
/// </example>
/// <param name="var">Variant that will be converted to string.</param>
/// <returns>The <c>Variant</c> encoded as a string.</returns>
- public static string Var2Str(object var)
+ public static string VarToStr(Variant var)
{
- return godot_icall_GD_var2str(var);
+ NativeFuncs.godotsharp_var_to_str((godot_variant)var.NativeVar, out godot_string ret);
+ using (ret)
+ return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
@@ -564,85 +582,7 @@ namespace Godot
/// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns>
public static Variant.Type TypeToVariantType(Type type)
{
- return godot_icall_TypeToVariantType(type);
+ return Marshaling.ConvertManagedTypeToVariantType(type, out bool _);
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_convert(object what, Variant.Type type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_GD_hash(object var);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- 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)]
- internal static extern void godot_icall_GD_printraw(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_prints(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_printt(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_randomize();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern uint godot_icall_GD_randi();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern float godot_icall_GD_randf();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_GD_randi_range(int from, int to);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern double godot_icall_GD_randf_range(double from, double to);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern double godot_icall_GD_randfn(double mean, double deviation);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_seed(ulong seed);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_GD_str(object[] what);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object godot_icall_GD_str2var(string str);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool godot_icall_GD_type_exists(IntPtr type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_GD_var2str(object var);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_pusherror(string type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_GD_pushwarning(string type);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
index 9ccac1faaf..78a9d0fe9d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
@@ -17,10 +17,7 @@ namespace Godot
public override void Fail(string message, string detailMessage)
{
GD.PrintErr("Assertion failed: ", message);
- if (detailMessage != null)
- {
- GD.PrintErr(" Details: ", detailMessage);
- }
+ GD.PrintErr(" Details: ", detailMessage);
try
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
index 729529d093..a17741ddeb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
@@ -1,17 +1,33 @@
using System;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
public static partial class GD
{
- /// <summary>
- /// Fires when an unhandled exception occurs, regardless of project settings.
- /// </summary>
- public static event EventHandler<UnhandledExceptionArgs> UnhandledException;
-
- private static void OnUnhandledException(Exception e)
+ [UnmanagedCallersOnly]
+ internal static void OnCoreApiAssemblyLoaded(godot_bool isDebug)
{
- UnhandledException?.Invoke(null, new UnhandledExceptionArgs(e));
+ try
+ {
+ Dispatcher.InitializeDefaultGodotTaskScheduler();
+
+ if (isDebug.ToBool())
+ {
+ DebuggingUtils.InstallTraceListener();
+
+ AppDomain.CurrentDomain.UnhandledException += (_, e) =>
+ {
+ // Exception.ToString() includes the inner exception
+ ExceptionUtils.LogUnhandledException((Exception)e.ExceptionObject);
+ };
+ }
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
deleted file mode 100644
index 50ae2eb112..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Godot
-{
- internal static class MarshalUtils
- {
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericArray(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Array<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericList(Type type) =>
- type.GetGenericTypeDefinition() == typeof(List<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsSystemGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Dictionary<,>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
-
- /// <summary>
- /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
- /// </summary>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- 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">
- /// Thrown when the given <paramref name="type"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition)
- {
- genericTypeDefinition = type.GetGenericTypeDefinition();
- }
-
- /// <summary>
- /// Gets the element type for the given <paramref name="arrayType"/>.
- /// </summary>
- /// <param name="arrayType">Type for the generic array.</param>
- /// <param name="elementType">Element type for the generic array.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="arrayType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void ArrayGetElementType(Type arrayType, out Type elementType)
- {
- elementType = arrayType.GetGenericArguments()[0];
- }
-
- /// <summary>
- /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>.
- /// </summary>
- /// <param name="dictionaryType">The type for the generic dictionary.</param>
- /// <param name="keyType">Key type for the generic dictionary.</param>
- /// <param name="valueType">Value type for the generic dictionary.</param>
- /// <exception cref="InvalidOperationException">
- /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type.
- /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
- /// </exception>
- private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
- {
- var genericArgs = dictionaryType.GetGenericArguments();
- keyType = genericArgs[0];
- valueType = genericArgs[1];
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/>
- /// where the generic type for the elements is <paramref name="elemType"/>.
- /// </summary>
- /// <param name="elemType">Element type for the array.</param>
- /// <returns>The generic array type with the specified element type.</returns>
- private static Type MakeGenericArrayType(Type elemType)
- {
- return typeof(Collections.Array<>).MakeGenericType(elemType);
- }
-
- /// <summary>
- /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/>
- /// where the generic type for the keys is <paramref name="keyType"/> and
- /// for the values is <paramref name="valueType"/>.
- /// </summary>
- /// <param name="keyType">Key type for the dictionary.</param>
- /// <param name="valueType">Key type for the dictionary.</param>
- /// <returns>The generic dictionary type with the specified key and value types.</returns>
- private static Type MakeGenericDictionaryType(Type keyType, Type valueType)
- {
- return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 36b7d0f80f..b2cb0f5e6b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
@@ -40,9 +35,9 @@ namespace Godot
public const real_t NaN = real_t.NaN;
// 0.0174532924f and 0.0174532925199433
- private const real_t _deg2RadConst = (real_t)0.0174532925199432957692369077M;
+ private const real_t _degToRadConst = (real_t)0.0174532925199432957692369077M;
// 57.29578f and 57.2957795130823
- private const real_t _rad2DegConst = (real_t)57.295779513082320876798154814M;
+ private const real_t _radToDegConst = (real_t)57.295779513082320876798154814M;
/// <summary>
/// Returns the absolute value of <paramref name="s"/> (i.e. positive value).
@@ -180,7 +175,8 @@ namespace Godot
}
/// <summary>
- /// Cubic interpolates between two values by a normalized value with pre and post values.
+ /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/>
+ /// with pre and post values.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
@@ -198,8 +194,95 @@ namespace Godot
}
/// <summary>
+ /// Cubic interpolates between two rotation values with shortest path
+ /// by the factor defined in <paramref name="weight"/> with pre and post values.
+ /// See also <see cref="LerpAngle"/>.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" 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 interpolation.</returns>
+ public static real_t CubicInterpolateAngle(real_t from, real_t to, real_t pre, real_t post, real_t weight)
+ {
+ real_t fromRot = from % Mathf.Tau;
+
+ real_t preDiff = (pre - fromRot) % Mathf.Tau;
+ real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff;
+
+ real_t toDiff = (to - fromRot) % Mathf.Tau;
+ real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff;
+
+ real_t postDiff = (post - toRot) % Mathf.Tau;
+ real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff;
+
+ return CubicInterpolate(fromRot, toRot, preRot, postRot, weight);
+ }
+
+ /// <summary>
+ /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/>
+ /// with pre and post values.
+ /// It can perform smoother interpolation than <see cref="CubicInterpolate"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="toT"></param>
+ /// <param name="preT"></param>
+ /// <param name="postT"></param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static real_t CubicInterpolateInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, real_t toT, real_t preT, real_t postT)
+ {
+ /* Barry-Goldman method */
+ real_t t = Lerp(0.0f, toT, weight);
+ real_t a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT);
+ real_t a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT);
+ real_t a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT));
+ real_t b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT));
+ real_t b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT);
+ return Lerp(b1, b2, toT == 0 ? 0.5f : t / toT);
+ }
+
+ /// <summary>
+ /// Cubic interpolates between two rotation values with shortest path
+ /// by the factor defined in <paramref name="weight"/> with pre and post values.
+ /// See also <see cref="LerpAngle"/>.
+ /// It can perform smoother interpolation than <see cref="CubicInterpolateAngle"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="from">The start value for interpolation.</param>
+ /// <param name="to">The destination value for interpolation.</param>
+ /// <param name="pre">The value which before "from" value for interpolation.</param>
+ /// <param name="post">The value which after "to" value for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="toT"></param>
+ /// <param name="preT"></param>
+ /// <param name="postT"></param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static real_t CubicInterpolateAngleInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight,
+ real_t toT, real_t preT, real_t postT)
+ {
+ real_t fromRot = from % Mathf.Tau;
+
+ real_t preDiff = (pre - fromRot) % Mathf.Tau;
+ real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff;
+
+ real_t toDiff = (to - fromRot) % Mathf.Tau;
+ real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff;
+
+ real_t postDiff = (post - toRot) % Mathf.Tau;
+ real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff;
+
+ return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT);
+ }
+
+ /// <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.
+ /// 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>
@@ -220,13 +303,34 @@ namespace Godot
}
/// <summary>
+ /// Returns the derivative 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 BezierDerivative(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 t2 = t * t;
+
+ real_t d = (control1 - start) * 3 * omt2 + (control2 - control1) * 6 * omt * t + (end - control2) * 3 * t2;
+ return d;
+ }
+
+ /// <summary>
/// Converts an angle expressed in degrees to radians.
/// </summary>
/// <param name="deg">An angle expressed in degrees.</param>
/// <returns>The same angle expressed in radians.</returns>
- public static real_t Deg2Rad(real_t deg)
+ public static real_t DegToRad(real_t deg)
{
- return deg * _deg2RadConst;
+ return deg * _degToRadConst;
}
/// <summary>
@@ -336,6 +440,17 @@ namespace Godot
}
/// <summary>
+ /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not
+ /// <see cref="NaN"/>, positive infinite, or negative infinity.
+ /// </summary>
+ /// <param name="s">The value to check.</param>
+ /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns>
+ public static bool IsFinite(real_t s)
+ {
+ return real_t.IsFinite(s);
+ }
+
+ /// <summary>
/// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity).
/// </summary>
/// <param name="s">The value to check.</param>
@@ -356,10 +471,11 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero.
+ /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero.
/// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
///
- /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
+ /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with
+ /// one value as zero.
/// </summary>
/// <param name="s">The value to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns>
@@ -536,9 +652,9 @@ namespace Godot
/// </summary>
/// <param name="rad">An angle expressed in radians.</param>
/// <returns>The same angle expressed in degrees.</returns>
- public static real_t Rad2Deg(real_t rad)
+ public static real_t RadToDeg(real_t rad)
{
- return rad * _rad2DegConst;
+ return rad * _radToDegConst;
}
/// <summary>
@@ -551,7 +667,7 @@ namespace Godot
/// <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)
+ public static real_t Remap(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo)
{
return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value));
}
@@ -759,9 +875,10 @@ namespace Godot
}
/// <summary>
- /// Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code].
- /// If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave).
- /// If [code]length[/code] is less than zero, it becomes positive.
+ /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>.
+ /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side
+ /// or increased to the <paramref name="length"/> side (like a triangle wave).
+ /// If <paramref name="length"/> is less than zero, it becomes positive.
/// </summary>
/// <param name="value">The value to pingpong.</param>
/// <param name="length">The maximum value of the function.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index f15d01b34b..72a1868964 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
namespace Godot
@@ -88,6 +83,17 @@ namespace Godot
}
/// <summary>
+ /// Returns the sine and cosine of angle <paramref name="s"/> in radians.
+ /// </summary>
+ /// <param name="s">The angle in radians.</param>
+ /// <returns>The sine and cosine of that angle.</returns>
+ public static (real_t Sin, real_t Cos) SinCos(real_t s)
+ {
+ (double sin, double cos) = Math.SinCos(s);
+ return ((real_t)sin, (real_t)cos);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
/// equal to each other.
/// The comparison is done using the provided tolerance value.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
new file mode 100644
index 0000000000..afef20a7f2
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/CustomUnsafe.cs
@@ -0,0 +1,313 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop;
+
+// Ref structs are not allowed as generic type parameters, so we can't use Unsafe.AsPointer<T>/AsRef<T>.
+// As a workaround we create our own overloads for our structs with some tricks under the hood.
+
+public static class CustomUnsafe
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* AsPointer(ref godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_ref* ReadOnlyRefAsPointer(in godot_ref value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(godot_ref* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_ref AsRef(in godot_ref source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* AsPointer(ref godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant_call_error* ReadOnlyRefAsPointer(in godot_variant_call_error value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(godot_variant_call_error* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant_call_error AsRef(in godot_variant_call_error source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* AsPointer(ref godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_variant* ReadOnlyRefAsPointer(in godot_variant value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(godot_variant* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_variant AsRef(in godot_variant source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* AsPointer(ref godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string* ReadOnlyRefAsPointer(in godot_string value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(godot_string* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string AsRef(in godot_string source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* AsPointer(ref godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_string_name* ReadOnlyRefAsPointer(in godot_string_name value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(godot_string_name* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_string_name AsRef(in godot_string_name source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* AsPointer(ref godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_node_path* ReadOnlyRefAsPointer(in godot_node_path value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(godot_node_path* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_node_path AsRef(in godot_node_path source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* AsPointer(ref godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_signal* ReadOnlyRefAsPointer(in godot_signal value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(godot_signal* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_signal AsRef(in godot_signal source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* AsPointer(ref godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_callable* ReadOnlyRefAsPointer(in godot_callable value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(godot_callable* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_callable AsRef(in godot_callable source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* AsPointer(ref godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_array* ReadOnlyRefAsPointer(in godot_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(godot_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_array AsRef(in godot_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* AsPointer(ref godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_dictionary* ReadOnlyRefAsPointer(in godot_dictionary value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(godot_dictionary* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_dictionary AsRef(in godot_dictionary source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* AsPointer(ref godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_byte_array* ReadOnlyRefAsPointer(in godot_packed_byte_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(godot_packed_byte_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_byte_array AsRef(in godot_packed_byte_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* AsPointer(ref godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int32_array* ReadOnlyRefAsPointer(in godot_packed_int32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(godot_packed_int32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int32_array AsRef(in godot_packed_int32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* AsPointer(ref godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_int64_array* ReadOnlyRefAsPointer(in godot_packed_int64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(godot_packed_int64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_int64_array AsRef(in godot_packed_int64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* AsPointer(ref godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float32_array* ReadOnlyRefAsPointer(in godot_packed_float32_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(godot_packed_float32_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float32_array AsRef(in godot_packed_float32_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* AsPointer(ref godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_float64_array* ReadOnlyRefAsPointer(in godot_packed_float64_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(godot_packed_float64_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_float64_array AsRef(in godot_packed_float64_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* AsPointer(ref godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_string_array* ReadOnlyRefAsPointer(in godot_packed_string_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(godot_packed_string_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_string_array AsRef(in godot_packed_string_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* AsPointer(ref godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector2_array* ReadOnlyRefAsPointer(in godot_packed_vector2_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(godot_packed_vector2_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector2_array AsRef(in godot_packed_vector2_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* AsPointer(ref godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_vector3_array* ReadOnlyRefAsPointer(in godot_packed_vector3_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(godot_packed_vector3_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_vector3_array AsRef(in godot_packed_vector3_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* AsPointer(ref godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_packed_color_array* ReadOnlyRefAsPointer(in godot_packed_color_array value)
+ => value.GetUnsafeAddress();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(godot_packed_color_array* source)
+ => ref *source;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe ref godot_packed_color_array AsRef(in godot_packed_color_array source)
+ => ref *ReadOnlyRefAsPointer(in source);
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
new file mode 100644
index 0000000000..5a0ea2ba13
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ internal static class ExceptionUtils
+ {
+ public static void PushError(string message)
+ {
+ GD.PushError(message);
+ }
+
+ private static void OnExceptionLoggerException(Exception loggerException, Exception exceptionToLog)
+ {
+ try
+ {
+ // This better not throw
+ PushError(string.Concat("Exception thrown while trying to log another exception...",
+ "\n### Exception ###\n", exceptionToLog.ToString(),
+ "\n### Logger exception ###\n", loggerException.ToString()));
+ }
+ catch (Exception)
+ {
+ // Well, too bad...
+ }
+ }
+
+ private record struct StackInfoTuple(string? File, string Func, int Line);
+
+ private static void CollectExceptionInfo(Exception exception, List<StackInfoTuple> globalFrames,
+ StringBuilder excMsg)
+ {
+ if (excMsg.Length > 0)
+ excMsg.Append(" ---> ");
+ excMsg.Append(exception.GetType().FullName);
+ excMsg.Append(": ");
+ excMsg.Append(exception.Message);
+
+ var innerExc = exception.InnerException;
+
+ if (innerExc != null)
+ {
+ CollectExceptionInfo(innerExc, globalFrames, excMsg);
+ globalFrames.Add(new("", "--- End of inner exception stack trace ---", 0));
+ }
+
+ var stackTrace = new StackTrace(exception, fNeedFileInfo: true);
+
+ foreach (StackFrame frame in stackTrace.GetFrames())
+ {
+ DebuggingUtils.GetStackFrameMethodDecl(frame, out string methodDecl);
+ globalFrames.Add(new(frame.GetFileName(), methodDecl, frame.GetFileLineNumber()));
+ }
+ }
+
+ private static void SendToScriptDebugger(Exception e)
+ {
+ var globalFrames = new List<StackInfoTuple>();
+
+ var excMsg = new StringBuilder();
+
+ CollectExceptionInfo(e, globalFrames, excMsg);
+
+ string file = globalFrames.Count > 0 ? globalFrames[0].File ?? "" : "";
+ string func = globalFrames.Count > 0 ? globalFrames[0].Func : "";
+ int line = globalFrames.Count > 0 ? globalFrames[0].Line : 0;
+ string errorMsg = "Exception";
+
+ using godot_string nFile = Marshaling.ConvertStringToNative(file);
+ using godot_string nFunc = Marshaling.ConvertStringToNative(func);
+ using godot_string nErrorMsg = Marshaling.ConvertStringToNative(errorMsg);
+ using godot_string nExcMsg = Marshaling.ConvertStringToNative(excMsg.ToString());
+
+ using DebuggingUtils.godot_stack_info_vector stackInfoVector = default;
+
+ stackInfoVector.Resize(globalFrames.Count);
+
+ unsafe
+ {
+ for (int i = 0; i < globalFrames.Count; i++)
+ {
+ DebuggingUtils.godot_stack_info* stackInfo = &stackInfoVector.Elements[i];
+
+ var globalFrame = globalFrames[i];
+
+ // Assign directly to element in Vector. This way we don't need to worry
+ // about disposal if an exception is thrown. The Vector takes care of it.
+ stackInfo->File = Marshaling.ConvertStringToNative(globalFrame.File);
+ stackInfo->Func = Marshaling.ConvertStringToNative(globalFrame.Func);
+ stackInfo->Line = globalFrame.Line;
+ }
+
+ NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line,
+ nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector);
+ }
+ }
+
+ public static void LogException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+ else
+ {
+ GD.PushError(e.ToString());
+ }
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+
+ public static void LogUnhandledException(Exception e)
+ {
+ try
+ {
+ if (NativeFuncs.godotsharp_internal_script_debugger_is_active())
+ {
+ SendToScriptDebugger(e);
+ }
+
+ // In this case, print it as well in addition to sending it to the script debugger
+ GD.PushError("Unhandled exception\n" + e);
+ }
+ catch (Exception unexpected)
+ {
+ OnExceptionLoggerException(unexpected, e);
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
new file mode 100644
index 0000000000..5579992d2b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/GodotDllImportResolver.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public class GodotDllImportResolver
+ {
+ private IntPtr _internalHandle;
+
+ public GodotDllImportResolver(IntPtr internalHandle)
+ {
+ _internalHandle = internalHandle;
+ }
+
+ public IntPtr OnResolveDllImport(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName == "__Internal")
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Win32.GetModuleHandle(IntPtr.Zero);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return _internalHandle;
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return MacOS.dlopen(IntPtr.Zero, MacOS.RTLD_LAZY);
+ }
+ }
+
+ return IntPtr.Zero;
+ }
+
+ // ReSharper disable InconsistentNaming
+ private static class MacOS
+ {
+ private const string SystemLibrary = "/usr/lib/libSystem.dylib";
+
+ public const int RTLD_LAZY = 1;
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr dlopen(IntPtr path, int mode);
+ }
+
+ private static class Win32
+ {
+ private const string SystemLibrary = "Kernel32.dll";
+
+ [DllImport(SystemLibrary)]
+ public static extern IntPtr GetModuleHandle(IntPtr lpModuleName);
+ }
+ // ReSharper restore InconsistentNaming
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
new file mode 100644
index 0000000000..fa79c2efbc
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -0,0 +1,1097 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Godot.NativeInterop
+{
+ // NOTES:
+ // ref structs cannot implement interfaces, but they still work in `using` directives if they declare Dispose()
+
+ public static class GodotBoolExtensions
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe godot_bool ToGodotBool(this bool @bool)
+ {
+ return *(godot_bool*)&@bool;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe bool ToBool(this godot_bool godotBool)
+ {
+ return *(bool*)&godotBool;
+ }
+ }
+
+ // Apparently a struct with a byte is not blittable? It crashes when calling a UnmanagedCallersOnly function ptr.
+ // ReSharper disable once InconsistentNaming
+ public enum godot_bool : byte
+ {
+ True = 1,
+ False = 0
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_ref
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_ref* GetUnsafeAddress()
+ => (godot_ref*)Unsafe.AsPointer(ref Unsafe.AsRef(in _reference));
+
+ private IntPtr _reference;
+
+ public void Dispose()
+ {
+ if (_reference == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_ref_destroy(ref this);
+ _reference = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Reference
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference;
+ }
+
+ public readonly bool IsNull
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _reference == IntPtr.Zero;
+ }
+ }
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum godot_variant_call_error_error
+ {
+ GODOT_CALL_ERROR_CALL_OK = 0,
+ GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
+ GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT,
+ GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
+ GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
+ GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_variant_call_error
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant_call_error* GetUnsafeAddress()
+ => (godot_variant_call_error*)Unsafe.AsPointer(ref Unsafe.AsRef(in error));
+
+ private godot_variant_call_error_error error;
+ private int argument;
+ private int expected;
+
+ public godot_variant_call_error_error Error
+ {
+ readonly get => error;
+ set => error = value;
+ }
+
+ public int Argument
+ {
+ readonly get => argument;
+ set => argument = value;
+ }
+
+ public Godot.Variant.Type Expected
+ {
+ readonly get => (Godot.Variant.Type)expected;
+ set => expected = (int)value;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_variant
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_variant* GetUnsafeAddress()
+ => (godot_variant*)Unsafe.AsPointer(ref Unsafe.AsRef(in _typeField));
+
+ // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits.
+ [FieldOffset(0)] private int _typeField;
+
+ // There's padding here
+
+ [FieldOffset(8)] private godot_variant_data _data;
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ private unsafe ref struct godot_variant_data
+ {
+ [FieldOffset(0)] public godot_bool _bool;
+ [FieldOffset(0)] public long _int;
+ [FieldOffset(0)] public double _float;
+ [FieldOffset(0)] public Transform2D* _transform2D;
+ [FieldOffset(0)] public AABB* _aabb;
+ [FieldOffset(0)] public Basis* _basis;
+ [FieldOffset(0)] public Transform3D* _transform3D;
+ [FieldOffset(0)] public Projection* _projection;
+ [FieldOffset(0)] private godot_variant_data_mem _mem;
+
+ // The following fields are not in the C++ union, but this is how they're stored in _mem.
+ [FieldOffset(0)] public godot_string_name _m_string_name;
+ [FieldOffset(0)] public godot_string _m_string;
+ [FieldOffset(0)] public Vector4 _m_vector4;
+ [FieldOffset(0)] public Vector4i _m_vector4i;
+ [FieldOffset(0)] public Vector3 _m_vector3;
+ [FieldOffset(0)] public Vector3i _m_vector3i;
+ [FieldOffset(0)] public Vector2 _m_vector2;
+ [FieldOffset(0)] public Vector2i _m_vector2i;
+ [FieldOffset(0)] public Rect2 _m_rect2;
+ [FieldOffset(0)] public Rect2i _m_rect2i;
+ [FieldOffset(0)] public Plane _m_plane;
+ [FieldOffset(0)] public Quaternion _m_quaternion;
+ [FieldOffset(0)] public Color _m_color;
+ [FieldOffset(0)] public godot_node_path _m_node_path;
+ [FieldOffset(0)] public RID _m_rid;
+ [FieldOffset(0)] public godot_variant_obj_data _m_obj_data;
+ [FieldOffset(0)] public godot_callable _m_callable;
+ [FieldOffset(0)] public godot_signal _m_signal;
+ [FieldOffset(0)] public godot_dictionary _m_dictionary;
+ [FieldOffset(0)] public godot_array _m_array;
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public struct godot_variant_obj_data
+ {
+ public ulong id;
+ public IntPtr obj;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public struct godot_variant_data_mem
+ {
+#pragma warning disable 169
+ private real_t _mem0;
+ private real_t _mem1;
+ private real_t _mem2;
+ private real_t _mem3;
+#pragma warning restore 169
+ }
+ }
+
+ public Variant.Type Type
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => (Variant.Type)_typeField;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _typeField = (int)value;
+ }
+
+ public godot_bool Bool
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._bool;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._bool = value;
+ }
+
+ public long Int
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._int;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._int = value;
+ }
+
+ public double Float
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._float;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._float = value;
+ }
+
+ public readonly unsafe Transform2D* Transform2D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform2D;
+ }
+
+ public readonly unsafe AABB* AABB
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._aabb;
+ }
+
+ public readonly unsafe Basis* Basis
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._basis;
+ }
+
+ public readonly unsafe Transform3D* Transform3D
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._transform3D;
+ }
+
+ public readonly unsafe Projection* Projection
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._projection;
+ }
+
+ public godot_string_name StringName
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string_name;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string_name = value;
+ }
+
+ public godot_string String
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_string;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_string = value;
+ }
+
+ public Vector4 Vector4
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector4;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector4 = value;
+ }
+
+ public Vector4i Vector4i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector4i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector4i = value;
+ }
+
+ public Vector3 Vector3
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3 = value;
+ }
+
+ public Vector3i Vector3i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector3i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector3i = value;
+ }
+
+ public Vector2 Vector2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2 = value;
+ }
+
+ public Vector2i Vector2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_vector2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_vector2i = value;
+ }
+
+ public Rect2 Rect2
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2 = value;
+ }
+
+ public Rect2i Rect2i
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rect2i;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rect2i = value;
+ }
+
+ public Plane Plane
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_plane;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_plane = value;
+ }
+
+ public Quaternion Quaternion
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_quaternion;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_quaternion = value;
+ }
+
+ public Color Color
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_color;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_color = value;
+ }
+
+ public godot_node_path NodePath
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_node_path;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_node_path = value;
+ }
+
+ public RID RID
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_rid;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_rid = value;
+ }
+
+ public godot_callable Callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_callable;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_callable = value;
+ }
+
+ public godot_signal Signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_signal;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_signal = value;
+ }
+
+ public godot_dictionary Dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_dictionary;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_dictionary = value;
+ }
+
+ public godot_array Array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => _data._m_array;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => _data._m_array = value;
+ }
+
+ public readonly IntPtr Object
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data._m_obj_data.obj;
+ }
+
+ public void Dispose()
+ {
+ switch (Type)
+ {
+ case Variant.Type.Nil:
+ case Variant.Type.Bool:
+ case Variant.Type.Int:
+ case Variant.Type.Float:
+ case Variant.Type.Vector2:
+ case Variant.Type.Vector2i:
+ case Variant.Type.Rect2:
+ case Variant.Type.Rect2i:
+ case Variant.Type.Vector3:
+ case Variant.Type.Vector3i:
+ case Variant.Type.Vector4:
+ case Variant.Type.Vector4i:
+ case Variant.Type.Plane:
+ case Variant.Type.Quaternion:
+ case Variant.Type.Color:
+ case Variant.Type.Rid:
+ return;
+ }
+
+ NativeFuncs.godotsharp_variant_destroy(ref this);
+ Type = Variant.Type.Nil;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ // Variant.Type is generated as an enum of type long, so we can't use for the field as it must only take 32-bits.
+ [FieldOffset(0)] private int _typeField;
+
+ // There's padding here
+
+ [FieldOffset(8)] private godot_variant_data.godot_variant_data_mem _data;
+
+ public static unsafe explicit operator movable(in godot_variant value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_variant(movable value)
+ => *(godot_variant*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_variant DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_variant*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string* GetUnsafeAddress()
+ => (godot_string*)Unsafe.AsPointer(ref Unsafe.AsRef(in _ptr));
+
+ private IntPtr _ptr;
+
+ public void Dispose()
+ {
+ if (_ptr == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_destroy(ref this);
+ _ptr = IntPtr.Zero;
+ }
+
+ public readonly IntPtr Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ // Size including the null termination character
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_string_name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_string_name* GetUnsafeAddress()
+ => (godot_string_name*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_string_name_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++.
+ get => _data == IntPtr.Zero;
+ }
+
+ public static bool operator ==(godot_string_name left, godot_string_name right)
+ {
+ return left._data == right._data;
+ }
+
+ public static bool operator !=(godot_string_name left, godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public bool Equals(godot_string_name other)
+ {
+ return _data == other._data;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is StringName s && s.Equals(this);
+ }
+
+ public override int GetHashCode()
+ {
+ return _data.GetHashCode();
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_string_name value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_string_name(movable value)
+ => *(godot_string_name*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_string_name DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_string_name*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_node_path
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_node_path* GetUnsafeAddress()
+ => (godot_node_path*)Unsafe.AsPointer(ref Unsafe.AsRef(in _data));
+
+ private IntPtr _data;
+
+ public void Dispose()
+ {
+ if (_data == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_node_path_destroy(ref this);
+ _data = IntPtr.Zero;
+ }
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _data != IntPtr.Zero;
+ }
+
+ public readonly bool IsEmpty
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does.
+ get => _data == IntPtr.Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _data;
+
+ public static unsafe explicit operator movable(in godot_node_path value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_node_path(movable value)
+ => *(godot_node_path*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_node_path DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_node_path*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_signal
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_signal* GetUnsafeAddress()
+ => (godot_signal*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _name;
+
+ // There's padding here on 32-bit
+
+ [FieldOffset(8)] private ulong _objectId;
+
+ public godot_signal(godot_string_name name, ulong objectId) : this()
+ {
+ _name = name;
+ _objectId = objectId;
+ }
+
+ public godot_string_name Name
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _name;
+ }
+
+ public ulong ObjectId
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _objectId;
+ }
+
+ public void Dispose()
+ {
+ if (!_name.IsAllocated)
+ return;
+ NativeFuncs.godotsharp_signal_destroy(ref this);
+ _name = default;
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_callable
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_callable* GetUnsafeAddress()
+ => (godot_callable*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private godot_string_name _method;
+
+ // There's padding here on 32-bit
+
+ // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
+ [FieldOffset(8)] private ulong _objectId;
+ [FieldOffset(8)] private IntPtr _custom;
+
+ public godot_callable(godot_string_name method, ulong objectId) : this()
+ {
+ _method = method;
+ _objectId = objectId;
+ }
+
+ public void Dispose()
+ {
+ // _custom needs freeing as well
+ if (!_method.IsAllocated && _custom == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_callable_destroy(ref this);
+ _method = default;
+ _custom = IntPtr.Zero;
+ }
+ }
+
+ // A correctly constructed value needs to call the native default constructor to allocate `_p`.
+ // Don't pass a C# default constructed `godot_array` to native code, unless it's going to
+ // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
+ [StructLayout(LayoutKind.Explicit)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_array* GetUnsafeAddress()
+ => (godot_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _getUnsafeAddressHelper));
+
+ [FieldOffset(0)] private byte _getUnsafeAddressHelper;
+
+ [FieldOffset(0)] private unsafe ArrayPrivate* _p;
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct ArrayPrivate
+ {
+ private uint _safeRefCount;
+
+ public VariantVector _arrayVector;
+ // There are more fields here, but we don't care as we never store this in C#
+
+ public readonly int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _arrayVector.Size;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct VariantVector
+ {
+ private IntPtr _writeProxy;
+ public unsafe godot_variant* _ptr;
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ public readonly unsafe godot_variant* Elements
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p->_arrayVector._ptr;
+ }
+
+ public readonly unsafe bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != null ? _p->Size : 0;
+ }
+
+ public unsafe void Dispose()
+ {
+ if (_p == null)
+ return;
+ NativeFuncs.godotsharp_array_destroy(ref this);
+ _p = null;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private unsafe ArrayPrivate* _p;
+
+ public static unsafe explicit operator movable(in godot_array value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_array(movable value)
+ => *(godot_array*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_array DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_array*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ // IMPORTANT:
+ // A correctly constructed value needs to call the native default constructor to allocate `_p`.
+ // Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to
+ // be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_dictionary
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_dictionary* GetUnsafeAddress()
+ => (godot_dictionary*)Unsafe.AsPointer(ref Unsafe.AsRef(in _p));
+
+ private IntPtr _p;
+
+ public readonly bool IsAllocated
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _p != IntPtr.Zero;
+ }
+
+ public void Dispose()
+ {
+ if (_p == IntPtr.Zero)
+ return;
+ NativeFuncs.godotsharp_dictionary_destroy(ref this);
+ _p = IntPtr.Zero;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ internal struct movable
+ {
+ private IntPtr _p;
+
+ public static unsafe explicit operator movable(in godot_dictionary value)
+ => *(movable*)CustomUnsafe.AsPointer(ref CustomUnsafe.AsRef(value));
+
+ public static unsafe explicit operator godot_dictionary(movable value)
+ => *(godot_dictionary*)Unsafe.AsPointer(ref value);
+
+ public unsafe ref godot_dictionary DangerousSelfRef =>
+ ref CustomUnsafe.AsRef((godot_dictionary*)Unsafe.AsPointer(ref this));
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_byte_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_byte_array* GetUnsafeAddress()
+ => (godot_packed_byte_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe byte* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe byte* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int32_array* GetUnsafeAddress()
+ => (godot_packed_int32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe int* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe int* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *(_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_int64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_int64_array* GetUnsafeAddress()
+ => (godot_packed_int64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe long* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe long* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float32_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float32_array* GetUnsafeAddress()
+ => (godot_packed_float32_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe float* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe float* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_float64_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_float64_array* GetUnsafeAddress()
+ => (godot_packed_float64_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe double* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe double* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_string_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_string_array* GetUnsafeAddress()
+ => (godot_packed_string_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe godot_string* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe godot_string* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector2_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector2_array* GetUnsafeAddress()
+ => (godot_packed_vector2_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector2* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector2* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_vector3_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_vector3_array* GetUnsafeAddress()
+ => (godot_packed_vector3_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Vector3* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Vector3* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ // ReSharper disable once InconsistentNaming
+ public ref struct godot_packed_color_array
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly unsafe godot_packed_color_array* GetUnsafeAddress()
+ => (godot_packed_color_array*)Unsafe.AsPointer(ref Unsafe.AsRef(in _writeProxy));
+
+ private IntPtr _writeProxy;
+ private unsafe Color* _ptr;
+
+ public unsafe void Dispose()
+ {
+ if (_ptr == null)
+ return;
+ NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
+ _ptr = null;
+ }
+
+ public readonly unsafe Color* Buffer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr;
+ }
+
+ public readonly unsafe int Size
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _ptr != null ? *((int*)_ptr - 1) : 0;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
new file mode 100644
index 0000000000..82f1c04d40
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropUtils.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ internal static class InteropUtils
+ {
+ public static Object UnmanagedGetManaged(IntPtr unmanaged)
+ {
+ // The native pointer may be null
+ if (unmanaged == IntPtr.Zero)
+ return null;
+
+ IntPtr gcHandlePtr;
+ godot_bool hasCsScriptInstance;
+
+ // First try to get the tied managed instance from a CSharpInstance script instance
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_script_instance_managed(
+ unmanaged, out hasCsScriptInstance);
+
+ if (gcHandlePtr != IntPtr.Zero)
+ return (Object)GCHandle.FromIntPtr(gcHandlePtr).Target;
+
+ // Otherwise, if the object has a CSharpInstance script instance, return null
+
+ if (hasCsScriptInstance.ToBool())
+ return null;
+
+ // If it doesn't have a CSharpInstance script instance, try with native instance bindings
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_get_instance_binding_managed(unmanaged);
+
+ object target = gcHandlePtr != IntPtr.Zero ? GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+
+ if (target != null)
+ return (Object)target;
+
+ // If the native instance binding GC handle target was collected, create a new one
+
+ gcHandlePtr = NativeFuncs.godotsharp_internal_unmanaged_instance_binding_create_managed(
+ unmanaged, gcHandlePtr);
+
+ return gcHandlePtr != IntPtr.Zero ? (Object)GCHandle.FromIntPtr(gcHandlePtr).Target : null;
+ }
+
+ public static void TieManagedToUnmanaged(Object managed, IntPtr unmanaged,
+ StringName nativeName, bool refCounted, Type type, Type nativeType)
+ {
+ var gcHandle = refCounted ?
+ CustomGCHandle.AllocWeak(managed) :
+ CustomGCHandle.AllocStrong(managed, type);
+
+ if (type == nativeType)
+ {
+ var nativeNameSelf = (godot_string_name)nativeName.NativeValue;
+ NativeFuncs.godotsharp_internal_tie_native_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, nativeNameSelf, refCounted.ToGodotBool());
+ }
+ else
+ {
+ unsafe
+ {
+ // We don't dispose `script` ourselves here.
+ // `tie_user_managed_to_unmanaged` does it for us to avoid another P/Invoke call.
+ godot_ref script;
+ ScriptManagerBridge.GetOrLoadOrCreateScriptForType(type, &script);
+
+ // IMPORTANT: This must be called after GetOrCreateScriptBridgeForType
+ NativeFuncs.godotsharp_internal_tie_user_managed_to_unmanaged(
+ GCHandle.ToIntPtr(gcHandle), unmanaged, &script, refCounted.ToGodotBool());
+ }
+ }
+ }
+
+ public static void TieManagedToUnmanagedWithPreSetup(Object managed, IntPtr unmanaged,
+ Type type, Type nativeType)
+ {
+ if (type == nativeType)
+ return;
+
+ var strongGCHandle = CustomGCHandle.AllocStrong(managed);
+ NativeFuncs.godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ GCHandle.ToIntPtr(strongGCHandle), unmanaged);
+ }
+
+ public static Object EngineGetSingleton(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ return UnmanagedGetManaged(NativeFuncs.godotsharp_engine_get_singleton(src));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
new file mode 100644
index 0000000000..0d9a698af0
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs
@@ -0,0 +1,598 @@
+using System;
+using System.Runtime.InteropServices;
+using Godot.Collections;
+using Array = System.Array;
+
+// ReSharper disable InconsistentNaming
+
+// We want to use full name qualifiers here even if redundant for clarity
+// ReSharper disable RedundantNameQualifier
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static class Marshaling
+ {
+ internal static Variant.Type ConvertManagedTypeToVariantType(Type type, out bool r_nil_is_variant)
+ {
+ r_nil_is_variant = false;
+
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Boolean:
+ return Variant.Type.Bool;
+ case TypeCode.Char:
+ return Variant.Type.Int;
+ case TypeCode.SByte:
+ return Variant.Type.Int;
+ case TypeCode.Int16:
+ return Variant.Type.Int;
+ case TypeCode.Int32:
+ return Variant.Type.Int;
+ case TypeCode.Int64:
+ return Variant.Type.Int;
+ case TypeCode.Byte:
+ return Variant.Type.Int;
+ case TypeCode.UInt16:
+ return Variant.Type.Int;
+ case TypeCode.UInt32:
+ return Variant.Type.Int;
+ case TypeCode.UInt64:
+ return Variant.Type.Int;
+ case TypeCode.Single:
+ return Variant.Type.Float;
+ case TypeCode.Double:
+ return Variant.Type.Float;
+ case TypeCode.String:
+ return Variant.Type.String;
+ default:
+ {
+ if (type == typeof(Vector2))
+ return Variant.Type.Vector2;
+
+ if (type == typeof(Vector2i))
+ return Variant.Type.Vector2i;
+
+ if (type == typeof(Rect2))
+ return Variant.Type.Rect2;
+
+ if (type == typeof(Rect2i))
+ return Variant.Type.Rect2i;
+
+ if (type == typeof(Transform2D))
+ return Variant.Type.Transform2d;
+
+ if (type == typeof(Vector3))
+ return Variant.Type.Vector3;
+
+ if (type == typeof(Vector3i))
+ return Variant.Type.Vector3i;
+
+ if (type == typeof(Vector4))
+ return Variant.Type.Vector4;
+
+ if (type == typeof(Vector4i))
+ return Variant.Type.Vector4i;
+
+ if (type == typeof(Basis))
+ return Variant.Type.Basis;
+
+ if (type == typeof(Quaternion))
+ return Variant.Type.Quaternion;
+
+ if (type == typeof(Transform3D))
+ return Variant.Type.Transform3d;
+
+ if (type == typeof(Projection))
+ return Variant.Type.Projection;
+
+ if (type == typeof(AABB))
+ return Variant.Type.Aabb;
+
+ if (type == typeof(Color))
+ return Variant.Type.Color;
+
+ if (type == typeof(Plane))
+ return Variant.Type.Plane;
+
+ if (type == typeof(Callable))
+ return Variant.Type.Callable;
+
+ if (type == typeof(Signal))
+ return Variant.Type.Signal;
+
+ if (type.IsEnum)
+ return Variant.Type.Int;
+
+ if (type.IsArray || type.IsSZArray)
+ {
+ if (type == typeof(byte[]))
+ return Variant.Type.PackedByteArray;
+
+ if (type == typeof(int[]))
+ return Variant.Type.PackedInt32Array;
+
+ if (type == typeof(long[]))
+ return Variant.Type.PackedInt64Array;
+
+ if (type == typeof(float[]))
+ return Variant.Type.PackedFloat32Array;
+
+ if (type == typeof(double[]))
+ return Variant.Type.PackedFloat64Array;
+
+ if (type == typeof(string[]))
+ return Variant.Type.PackedStringArray;
+
+ if (type == typeof(Vector2[]))
+ return Variant.Type.PackedVector2Array;
+
+ if (type == typeof(Vector3[]))
+ return Variant.Type.PackedVector3Array;
+
+ if (type == typeof(Color[]))
+ return Variant.Type.PackedColorArray;
+
+ if (type == typeof(StringName[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(NodePath[]))
+ return Variant.Type.Array;
+
+ if (type == typeof(RID[]))
+ return Variant.Type.Array;
+
+ if (typeof(Godot.Object[]).IsAssignableFrom(type))
+ return Variant.Type.Array;
+ }
+ else if (type.IsGenericType)
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Variant.Type.Object;
+
+ // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections
+ // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode.
+
+ if (typeof(IGenericGodotDictionary).IsAssignableFrom(type))
+ return Variant.Type.Dictionary;
+
+ if (typeof(IGenericGodotArray).IsAssignableFrom(type))
+ return Variant.Type.Array;
+ }
+ else if (type == typeof(Variant))
+ {
+ r_nil_is_variant = true;
+ return Variant.Type.Nil;
+ }
+ else
+ {
+ if (typeof(Godot.Object).IsAssignableFrom(type))
+ return Variant.Type.Object;
+
+ if (typeof(StringName) == type)
+ return Variant.Type.StringName;
+
+ if (typeof(NodePath) == type)
+ return Variant.Type.NodePath;
+
+ if (typeof(RID) == type)
+ return Variant.Type.Rid;
+
+ if (typeof(Collections.Dictionary) == type)
+ return Variant.Type.Dictionary;
+
+ if (typeof(Collections.Array) == type)
+ return Variant.Type.Array;
+ }
+
+ break;
+ }
+ }
+
+ // Unknown
+ return Variant.Type.Nil;
+ }
+
+ // String
+
+ public static unsafe godot_string ConvertStringToNative(string? p_mono_string)
+ {
+ if (p_mono_string == null)
+ return new godot_string();
+
+ fixed (char* methodChars = p_mono_string)
+ {
+ NativeFuncs.godotsharp_string_new_with_utf16_chars(out godot_string dest, methodChars);
+ return dest;
+ }
+ }
+
+ public static unsafe string ConvertStringToManaged(in godot_string p_string)
+ {
+ if (p_string.Buffer == IntPtr.Zero)
+ return string.Empty;
+
+ const int sizeOfChar32 = 4;
+ byte* bytes = (byte*)p_string.Buffer;
+ int size = p_string.Size;
+ if (size == 0)
+ return string.Empty;
+ size -= 1; // zero at the end
+ int sizeInBytes = size * sizeOfChar32;
+ return System.Text.Encoding.UTF32.GetString(bytes, sizeInBytes);
+ }
+
+ // Callable
+
+ public static godot_callable ConvertCallableToNative(in Callable p_managed_callable)
+ {
+ if (p_managed_callable.Delegate != null)
+ {
+ var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
+
+ IntPtr objectPtr = p_managed_callable.Target != null ?
+ Object.GetPtr(p_managed_callable.Target) :
+ IntPtr.Zero;
+
+ unsafe
+ {
+ NativeFuncs.godotsharp_callable_new_with_delegate(
+ GCHandle.ToIntPtr(gcHandle), (IntPtr)p_managed_callable.Trampoline,
+ objectPtr, out godot_callable callable);
+
+ return callable;
+ }
+ }
+ else
+ {
+ godot_string_name method;
+
+ if (p_managed_callable.Method != null && !p_managed_callable.Method.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_callable.Method.NativeValue;
+ method = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ method = default;
+ }
+
+ return new godot_callable(method /* Takes ownership of disposable */,
+ p_managed_callable.Target.GetInstanceId());
+ }
+ }
+
+ public static Callable ConvertCallableToManaged(in godot_callable p_callable)
+ {
+ if (NativeFuncs.godotsharp_callable_get_data_for_marshalling(p_callable,
+ out IntPtr delegateGCHandle, out IntPtr trampoline,
+ out IntPtr godotObject, out godot_string_name name).ToBool())
+ {
+ if (delegateGCHandle != IntPtr.Zero)
+ {
+ unsafe
+ {
+ return Callable.CreateWithUnsafeTrampoline(
+ (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target,
+ (delegate* managed<object, NativeVariantPtrArgs, out godot_variant, void>)trampoline);
+ }
+ }
+
+ return new Callable(
+ InteropUtils.UnmanagedGetManaged(godotObject),
+ StringName.CreateTakingOwnershipOfDisposableValue(name));
+ }
+
+ // Some other unsupported callable
+ return new Callable();
+ }
+
+ // Signal
+
+ public static godot_signal ConvertSignalToNative(in Signal p_managed_signal)
+ {
+ ulong ownerId = p_managed_signal.Owner.GetInstanceId();
+ godot_string_name name;
+
+ if (p_managed_signal.Name != null && !p_managed_signal.Name.IsEmpty)
+ {
+ var src = (godot_string_name)p_managed_signal.Name.NativeValue;
+ name = NativeFuncs.godotsharp_string_name_new_copy(src);
+ }
+ else
+ {
+ name = default;
+ }
+
+ return new godot_signal(name, ownerId);
+ }
+
+ public static Signal ConvertSignalToManaged(in godot_signal p_signal)
+ {
+ var owner = GD.InstanceFromId(p_signal.ObjectId);
+ var name = StringName.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name));
+ return new Signal(owner, name);
+ }
+
+ // Array
+
+ internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array)
+ where T : Godot.Object
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new T[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = (T)array[i].AsGodotObject();
+
+ return ret;
+ }
+
+ internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new StringName[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsStringName();
+
+ return ret;
+ }
+
+ internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new NodePath[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsNodePath();
+
+ return ret;
+ }
+
+ internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array)
+ {
+ var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
+ NativeFuncs.godotsharp_array_new_copy(p_array));
+
+ int length = array.Count;
+ var ret = new RID[length];
+
+ for (int i = 0; i < length; i++)
+ ret[i] = array[i].AsRID();
+
+ return ret;
+ }
+
+ // PackedByteArray
+
+ public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array)
+ {
+ byte* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<byte>();
+ var array = new byte[size];
+ fixed (byte* dest = array)
+ Buffer.MemoryCopy(buffer, dest, size, size);
+ return array;
+ }
+
+ public static unsafe godot_packed_byte_array ConvertSystemArrayToNativePackedByteArray(Span<byte> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_byte_array();
+ fixed (byte* src = p_array)
+ return NativeFuncs.godotsharp_packed_byte_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt32Array
+
+ public static unsafe int[] ConvertNativePackedInt32ArrayToSystemArray(godot_packed_int32_array p_array)
+ {
+ int* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<int>();
+ int sizeInBytes = size * sizeof(int);
+ var array = new int[size];
+ fixed (int* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_int32_array ConvertSystemArrayToNativePackedInt32Array(Span<int> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int32_array();
+ fixed (int* src = p_array)
+ return NativeFuncs.godotsharp_packed_int32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedInt64Array
+
+ public static unsafe long[] ConvertNativePackedInt64ArrayToSystemArray(godot_packed_int64_array p_array)
+ {
+ long* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<long>();
+ int sizeInBytes = size * sizeof(long);
+ var array = new long[size];
+ fixed (long* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_int64_array ConvertSystemArrayToNativePackedInt64Array(Span<long> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_int64_array();
+ fixed (long* src = p_array)
+ return NativeFuncs.godotsharp_packed_int64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat32Array
+
+ public static unsafe float[] ConvertNativePackedFloat32ArrayToSystemArray(godot_packed_float32_array p_array)
+ {
+ float* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<float>();
+ int sizeInBytes = size * sizeof(float);
+ var array = new float[size];
+ fixed (float* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_float32_array ConvertSystemArrayToNativePackedFloat32Array(
+ Span<float> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float32_array();
+ fixed (float* src = p_array)
+ return NativeFuncs.godotsharp_packed_float32_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedFloat64Array
+
+ public static unsafe double[] ConvertNativePackedFloat64ArrayToSystemArray(godot_packed_float64_array p_array)
+ {
+ double* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<double>();
+ int sizeInBytes = size * sizeof(double);
+ var array = new double[size];
+ fixed (double* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_float64_array ConvertSystemArrayToNativePackedFloat64Array(
+ Span<double> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_float64_array();
+ fixed (double* src = p_array)
+ return NativeFuncs.godotsharp_packed_float64_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedStringArray
+
+ public static unsafe string[] ConvertNativePackedStringArrayToSystemArray(godot_packed_string_array p_array)
+ {
+ godot_string* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<string>();
+ var array = new string[size];
+ for (int i = 0; i < size; i++)
+ array[i] = ConvertStringToManaged(buffer[i]);
+ return array;
+ }
+
+ public static godot_packed_string_array ConvertSystemArrayToNativePackedStringArray(Span<string> p_array)
+ {
+ godot_packed_string_array dest = new godot_packed_string_array();
+
+ if (p_array.IsEmpty)
+ return dest;
+
+ /* TODO: Replace godotsharp_packed_string_array_add with a single internal call to
+ get the write address. We can't use `dest._ptr` directly for writing due to COW. */
+
+ for (int i = 0; i < p_array.Length; i++)
+ {
+ using godot_string godotStrElem = ConvertStringToNative(p_array[i]);
+ NativeFuncs.godotsharp_packed_string_array_add(ref dest, godotStrElem);
+ }
+
+ return dest;
+ }
+
+ // PackedVector2Array
+
+ public static unsafe Vector2[] ConvertNativePackedVector2ArrayToSystemArray(godot_packed_vector2_array p_array)
+ {
+ Vector2* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector2>();
+ int sizeInBytes = size * sizeof(Vector2);
+ var array = new Vector2[size];
+ fixed (Vector2* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_vector2_array ConvertSystemArrayToNativePackedVector2Array(
+ Span<Vector2> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector2_array();
+ fixed (Vector2* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector2_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedVector3Array
+
+ public static unsafe Vector3[] ConvertNativePackedVector3ArrayToSystemArray(godot_packed_vector3_array p_array)
+ {
+ Vector3* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Vector3>();
+ int sizeInBytes = size * sizeof(Vector3);
+ var array = new Vector3[size];
+ fixed (Vector3* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_vector3_array ConvertSystemArrayToNativePackedVector3Array(
+ Span<Vector3> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_vector3_array();
+ fixed (Vector3* src = p_array)
+ return NativeFuncs.godotsharp_packed_vector3_array_new_mem_copy(src, p_array.Length);
+ }
+
+ // PackedColorArray
+
+ public static unsafe Color[] ConvertNativePackedColorArrayToSystemArray(godot_packed_color_array p_array)
+ {
+ Color* buffer = p_array.Buffer;
+ int size = p_array.Size;
+ if (size == 0)
+ return Array.Empty<Color>();
+ int sizeInBytes = size * sizeof(Color);
+ var array = new Color[size];
+ fixed (Color* dest = array)
+ Buffer.MemoryCopy(buffer, dest, sizeInBytes, sizeInBytes);
+ return array;
+ }
+
+ public static unsafe godot_packed_color_array ConvertSystemArrayToNativePackedColorArray(Span<Color> p_array)
+ {
+ if (p_array.IsEmpty)
+ return new godot_packed_color_array();
+ fixed (Color* src = p_array)
+ return NativeFuncs.godotsharp_packed_color_array_new_mem_copy(src, p_array.Length);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
new file mode 100644
index 0000000000..57488bd586
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs
@@ -0,0 +1,514 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.SourceGenerators.Internal;
+
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'glue/runtime_interop.cpp'.
+ */
+
+ [GenerateUnmanagedCallbacks(typeof(UnmanagedCallbacks))]
+ public static unsafe partial class NativeFuncs
+ {
+ private static bool initialized = false;
+
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ public static void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized.");
+ initialized = true;
+
+ if (unmanagedCallbacksSize != sizeof(UnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch.", nameof(unmanagedCallbacksSize));
+
+ _unmanagedCallbacks = Unsafe.AsRef<UnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct UnmanagedCallbacks
+ {
+ }
+
+ // Custom functions
+
+ public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
+ in godot_string_name p_methodname);
+
+ public static partial delegate* unmanaged<IntPtr> godotsharp_get_class_constructor(
+ in godot_string_name p_classname);
+
+ public static partial IntPtr godotsharp_engine_get_singleton(in godot_string p_name);
+
+
+ internal static partial Error godotsharp_stack_info_vector_resize(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector, int p_size);
+
+ internal static partial void godotsharp_stack_info_vector_destroy(
+ ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
+ in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
+ godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
+
+ internal static partial bool godotsharp_internal_script_debugger_is_active();
+
+ internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr);
+
+ internal static partial void godotsharp_internal_object_disposed(IntPtr ptr, IntPtr gcHandleToFree);
+
+ internal static partial void godotsharp_internal_refcounted_disposed(IntPtr ptr, IntPtr gcHandleToFree,
+ godot_bool isFinalizer);
+
+ internal static partial Error godotsharp_internal_signal_awaiter_connect(IntPtr source,
+ in godot_string_name signal,
+ IntPtr target, IntPtr awaiterHandlePtr);
+
+ internal static partial void godotsharp_internal_tie_native_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, in godot_string_name nativeName, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_user_managed_to_unmanaged(IntPtr gcHandleIntPtr,
+ IntPtr unmanaged, godot_ref* scriptPtr, godot_bool refCounted);
+
+ internal static partial void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(
+ IntPtr gcHandleIntPtr, IntPtr unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_script_instance_managed(IntPtr p_unmanaged,
+ out godot_bool r_has_cs_script_instance);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(IntPtr p_unmanaged);
+
+ internal static partial IntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(IntPtr p_unmanaged,
+ IntPtr oldGCHandlePtr);
+
+ internal static partial void godotsharp_internal_new_csharp_script(godot_ref* r_dest);
+
+ internal static partial godot_bool godotsharp_internal_script_load(in godot_string p_path, godot_ref* r_dest);
+
+ internal static partial void godotsharp_internal_reload_registered_script(IntPtr scriptPtr);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_native(in godot_string_name p_native_name,
+ in godot_array p_input, out godot_array r_output);
+
+ internal static partial void godotsharp_array_filter_godot_objects_by_non_native(in godot_array p_input,
+ out godot_array r_output);
+
+ public static partial void godotsharp_ref_new_from_ref_counted_ptr(out godot_ref r_dest,
+ IntPtr p_ref_counted_ptr);
+
+ public static partial void godotsharp_ref_destroy(ref godot_ref p_instance);
+
+ public static partial void godotsharp_string_name_new_from_string(out godot_string_name r_dest,
+ in godot_string p_name);
+
+ public static partial void godotsharp_node_path_new_from_string(out godot_node_path r_dest,
+ in godot_string p_name);
+
+ public static partial void
+ godotsharp_string_name_as_string(out godot_string r_dest, in godot_string_name p_name);
+
+ public static partial void godotsharp_node_path_as_string(out godot_string r_dest, in godot_node_path p_np);
+
+ public static partial godot_packed_byte_array godotsharp_packed_byte_array_new_mem_copy(byte* p_src,
+ int p_length);
+
+ public static partial godot_packed_int32_array godotsharp_packed_int32_array_new_mem_copy(int* p_src,
+ int p_length);
+
+ public static partial godot_packed_int64_array godotsharp_packed_int64_array_new_mem_copy(long* p_src,
+ int p_length);
+
+ public static partial godot_packed_float32_array godotsharp_packed_float32_array_new_mem_copy(float* p_src,
+ int p_length);
+
+ public static partial godot_packed_float64_array godotsharp_packed_float64_array_new_mem_copy(double* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector2_array godotsharp_packed_vector2_array_new_mem_copy(Vector2* p_src,
+ int p_length);
+
+ public static partial godot_packed_vector3_array godotsharp_packed_vector3_array_new_mem_copy(Vector3* p_src,
+ int p_length);
+
+ public static partial godot_packed_color_array godotsharp_packed_color_array_new_mem_copy(Color* p_src,
+ int p_length);
+
+ public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
+ in godot_string p_element);
+
+ public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_trampoline,
+ IntPtr p_object, out godot_callable r_callable);
+
+ internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,
+ out IntPtr r_delegate_handle, out IntPtr r_trampoline, out IntPtr r_object, out godot_string_name r_name);
+
+ internal static partial godot_variant godotsharp_callable_call(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ internal static partial void godotsharp_callable_call_deferred(in godot_callable p_callable,
+ godot_variant** p_args, int p_arg_count);
+
+ internal static partial Color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha);
+
+ // GDNative functions
+
+ // gdnative.h
+
+ public static partial void godotsharp_method_bind_ptrcall(IntPtr p_method_bind, IntPtr p_instance, void** p_args,
+ void* p_ret);
+
+ public static partial godot_variant godotsharp_method_bind_call(IntPtr p_method_bind, IntPtr p_instance,
+ godot_variant** p_args, int p_arg_count, out godot_variant_call_error p_call_error);
+
+ // variant.h
+
+ public static partial void
+ godotsharp_variant_new_string_name(out godot_variant r_dest, in godot_string_name p_s);
+
+ public static partial void godotsharp_variant_new_copy(out godot_variant r_dest, in godot_variant p_src);
+
+ public static partial void godotsharp_variant_new_node_path(out godot_variant r_dest, in godot_node_path p_np);
+
+ public static partial void godotsharp_variant_new_object(out godot_variant r_dest, IntPtr p_obj);
+
+ public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d);
+
+ public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis);
+
+ public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans);
+
+ public static partial void godotsharp_variant_new_projection(out godot_variant r_dest, in Projection p_proj);
+
+ public static partial void godotsharp_variant_new_aabb(out godot_variant r_dest, in AABB p_aabb);
+
+ public static partial void godotsharp_variant_new_dictionary(out godot_variant r_dest,
+ in godot_dictionary p_dict);
+
+ public static partial void godotsharp_variant_new_array(out godot_variant r_dest, in godot_array p_arr);
+
+ public static partial void godotsharp_variant_new_packed_byte_array(out godot_variant r_dest,
+ in godot_packed_byte_array p_pba);
+
+ public static partial void godotsharp_variant_new_packed_int32_array(out godot_variant r_dest,
+ in godot_packed_int32_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_int64_array(out godot_variant r_dest,
+ in godot_packed_int64_array p_pia);
+
+ public static partial void godotsharp_variant_new_packed_float32_array(out godot_variant r_dest,
+ in godot_packed_float32_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_float64_array(out godot_variant r_dest,
+ in godot_packed_float64_array p_pra);
+
+ public static partial void godotsharp_variant_new_packed_string_array(out godot_variant r_dest,
+ in godot_packed_string_array p_psa);
+
+ public static partial void godotsharp_variant_new_packed_vector2_array(out godot_variant r_dest,
+ in godot_packed_vector2_array p_pv2a);
+
+ public static partial void godotsharp_variant_new_packed_vector3_array(out godot_variant r_dest,
+ in godot_packed_vector3_array p_pv3a);
+
+ public static partial void godotsharp_variant_new_packed_color_array(out godot_variant r_dest,
+ in godot_packed_color_array p_pca);
+
+ public static partial godot_bool godotsharp_variant_as_bool(in godot_variant p_self);
+
+ public static partial Int64 godotsharp_variant_as_int(in godot_variant p_self);
+
+ public static partial double godotsharp_variant_as_float(in godot_variant p_self);
+
+ public static partial godot_string godotsharp_variant_as_string(in godot_variant p_self);
+
+ public static partial Vector2 godotsharp_variant_as_vector2(in godot_variant p_self);
+
+ public static partial Vector2i godotsharp_variant_as_vector2i(in godot_variant p_self);
+
+ public static partial Rect2 godotsharp_variant_as_rect2(in godot_variant p_self);
+
+ public static partial Rect2i godotsharp_variant_as_rect2i(in godot_variant p_self);
+
+ public static partial Vector3 godotsharp_variant_as_vector3(in godot_variant p_self);
+
+ public static partial Vector3i godotsharp_variant_as_vector3i(in godot_variant p_self);
+
+ public static partial Transform2D godotsharp_variant_as_transform2d(in godot_variant p_self);
+
+ public static partial Vector4 godotsharp_variant_as_vector4(in godot_variant p_self);
+
+ public static partial Vector4i godotsharp_variant_as_vector4i(in godot_variant p_self);
+
+ public static partial Plane godotsharp_variant_as_plane(in godot_variant p_self);
+
+ public static partial Quaternion godotsharp_variant_as_quaternion(in godot_variant p_self);
+
+ public static partial AABB godotsharp_variant_as_aabb(in godot_variant p_self);
+
+ public static partial Basis godotsharp_variant_as_basis(in godot_variant p_self);
+
+ public static partial Transform3D godotsharp_variant_as_transform3d(in godot_variant p_self);
+
+ public static partial Projection godotsharp_variant_as_projection(in godot_variant p_self);
+
+ public static partial Color godotsharp_variant_as_color(in godot_variant p_self);
+
+ public static partial godot_string_name godotsharp_variant_as_string_name(in godot_variant p_self);
+
+ public static partial godot_node_path godotsharp_variant_as_node_path(in godot_variant p_self);
+
+ public static partial RID godotsharp_variant_as_rid(in godot_variant p_self);
+
+ public static partial godot_callable godotsharp_variant_as_callable(in godot_variant p_self);
+
+ public static partial godot_signal godotsharp_variant_as_signal(in godot_variant p_self);
+
+ public static partial godot_dictionary godotsharp_variant_as_dictionary(in godot_variant p_self);
+
+ public static partial godot_array godotsharp_variant_as_array(in godot_variant p_self);
+
+ public static partial godot_packed_byte_array godotsharp_variant_as_packed_byte_array(in godot_variant p_self);
+
+ public static partial godot_packed_int32_array godotsharp_variant_as_packed_int32_array(in godot_variant p_self);
+
+ public static partial godot_packed_int64_array godotsharp_variant_as_packed_int64_array(in godot_variant p_self);
+
+ public static partial godot_packed_float32_array godotsharp_variant_as_packed_float32_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_float64_array godotsharp_variant_as_packed_float64_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_string_array godotsharp_variant_as_packed_string_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector2_array godotsharp_variant_as_packed_vector2_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_vector3_array godotsharp_variant_as_packed_vector3_array(
+ in godot_variant p_self);
+
+ public static partial godot_packed_color_array godotsharp_variant_as_packed_color_array(in godot_variant p_self);
+
+ public static partial godot_bool godotsharp_variant_equals(in godot_variant p_a, in godot_variant p_b);
+
+ // string.h
+
+ public static partial void godotsharp_string_new_with_utf16_chars(out godot_string r_dest, char* p_contents);
+
+ // string_name.h
+
+ public static partial void godotsharp_string_name_new_copy(out godot_string_name r_dest,
+ in godot_string_name p_src);
+
+ // node_path.h
+
+ public static partial void godotsharp_node_path_new_copy(out godot_node_path r_dest, in godot_node_path p_src);
+
+ // array.h
+
+ public static partial void godotsharp_array_new(out godot_array r_dest);
+
+ public static partial void godotsharp_array_new_copy(out godot_array r_dest, in godot_array p_src);
+
+ public static partial godot_variant* godotsharp_array_ptrw(ref godot_array p_self);
+
+ // dictionary.h
+
+ public static partial void godotsharp_dictionary_new(out godot_dictionary r_dest);
+
+ public static partial void godotsharp_dictionary_new_copy(out godot_dictionary r_dest,
+ in godot_dictionary p_src);
+
+ // destroy functions
+
+ public static partial void godotsharp_packed_byte_array_destroy(ref godot_packed_byte_array p_self);
+
+ public static partial void godotsharp_packed_int32_array_destroy(ref godot_packed_int32_array p_self);
+
+ public static partial void godotsharp_packed_int64_array_destroy(ref godot_packed_int64_array p_self);
+
+ public static partial void godotsharp_packed_float32_array_destroy(ref godot_packed_float32_array p_self);
+
+ public static partial void godotsharp_packed_float64_array_destroy(ref godot_packed_float64_array p_self);
+
+ public static partial void godotsharp_packed_string_array_destroy(ref godot_packed_string_array p_self);
+
+ public static partial void godotsharp_packed_vector2_array_destroy(ref godot_packed_vector2_array p_self);
+
+ public static partial void godotsharp_packed_vector3_array_destroy(ref godot_packed_vector3_array p_self);
+
+ public static partial void godotsharp_packed_color_array_destroy(ref godot_packed_color_array p_self);
+
+ public static partial void godotsharp_variant_destroy(ref godot_variant p_self);
+
+ public static partial void godotsharp_string_destroy(ref godot_string p_self);
+
+ public static partial void godotsharp_string_name_destroy(ref godot_string_name p_self);
+
+ public static partial void godotsharp_node_path_destroy(ref godot_node_path p_self);
+
+ public static partial void godotsharp_signal_destroy(ref godot_signal p_self);
+
+ public static partial void godotsharp_callable_destroy(ref godot_callable p_self);
+
+ public static partial void godotsharp_array_destroy(ref godot_array p_self);
+
+ public static partial void godotsharp_dictionary_destroy(ref godot_dictionary p_self);
+
+ // Array
+
+ public static partial int godotsharp_array_add(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void
+ godotsharp_array_duplicate(ref godot_array p_self, godot_bool p_deep, out godot_array r_dest);
+
+ public static partial int godotsharp_array_index_of(ref godot_array p_self, in godot_variant p_item);
+
+ public static partial void godotsharp_array_insert(ref godot_array p_self, int p_index, in godot_variant p_item);
+
+ public static partial void godotsharp_array_remove_at(ref godot_array p_self, int p_index);
+
+ public static partial Error godotsharp_array_resize(ref godot_array p_self, int p_new_size);
+
+ public static partial void godotsharp_array_shuffle(ref godot_array p_self);
+
+ public static partial void godotsharp_array_to_string(ref godot_array p_self, out godot_string r_str);
+
+ // Dictionary
+
+ public static partial godot_bool godotsharp_dictionary_try_get_value(ref godot_dictionary p_self,
+ in godot_variant p_key,
+ out godot_variant r_value);
+
+ public static partial void godotsharp_dictionary_set_value(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_keys(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial void godotsharp_dictionary_values(ref godot_dictionary p_self, out godot_array r_dest);
+
+ public static partial int godotsharp_dictionary_count(ref godot_dictionary p_self);
+
+ public static partial void godotsharp_dictionary_key_value_pair_at(ref godot_dictionary p_self, int p_index,
+ out godot_variant r_key, out godot_variant r_value);
+
+ public static partial void godotsharp_dictionary_add(ref godot_dictionary p_self, in godot_variant p_key,
+ in godot_variant p_value);
+
+ public static partial void godotsharp_dictionary_clear(ref godot_dictionary p_self);
+
+ public static partial godot_bool godotsharp_dictionary_contains_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep,
+ out godot_dictionary r_dest);
+
+ public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self,
+ in godot_variant p_key);
+
+ public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str);
+
+ // StringExtensions
+
+ public static partial void godotsharp_string_simplify_path(in godot_string p_self,
+ out godot_string r_simplified_path);
+
+ public static partial void godotsharp_string_to_camel_case(in godot_string p_self,
+ out godot_string r_camel_case);
+
+ public static partial void godotsharp_string_to_pascal_case(in godot_string p_self,
+ out godot_string r_pascal_case);
+
+ public static partial void godotsharp_string_to_snake_case(in godot_string p_self,
+ out godot_string r_snake_case);
+
+ // NodePath
+
+ public static partial void godotsharp_node_path_get_as_property_path(in godot_node_path p_self,
+ ref godot_node_path r_dest);
+
+ public static partial void godotsharp_node_path_get_concatenated_names(in godot_node_path p_self,
+ out godot_string r_names);
+
+ public static partial void godotsharp_node_path_get_concatenated_subnames(in godot_node_path p_self,
+ out godot_string r_subnames);
+
+ public static partial void godotsharp_node_path_get_name(in godot_node_path p_self, int p_idx,
+ out godot_string r_name);
+
+ public static partial int godotsharp_node_path_get_name_count(in godot_node_path p_self);
+
+ public static partial void godotsharp_node_path_get_subname(in godot_node_path p_self, int p_idx,
+ out godot_string r_subname);
+
+ public static partial int godotsharp_node_path_get_subname_count(in godot_node_path p_self);
+
+ public static partial godot_bool godotsharp_node_path_is_absolute(in godot_node_path p_self);
+
+ // GD, etc
+
+ internal static partial void godotsharp_bytes_to_var(in godot_packed_byte_array p_bytes,
+ godot_bool p_allow_objects,
+ out godot_variant r_ret);
+
+ internal static partial void godotsharp_convert(in godot_variant p_what, int p_type,
+ out godot_variant r_ret);
+
+ internal static partial int godotsharp_hash(in godot_variant p_var);
+
+ internal static partial IntPtr godotsharp_instance_from_id(ulong p_instance_id);
+
+ internal static partial void godotsharp_print(in godot_string p_what);
+
+ public static partial void godotsharp_print_rich(in godot_string p_what);
+
+ internal static partial void godotsharp_printerr(in godot_string p_what);
+
+ internal static partial void godotsharp_printraw(in godot_string p_what);
+
+ internal static partial void godotsharp_prints(in godot_string p_what);
+
+ internal static partial void godotsharp_printt(in godot_string p_what);
+
+ internal static partial float godotsharp_randf();
+
+ internal static partial uint godotsharp_randi();
+
+ internal static partial void godotsharp_randomize();
+
+ internal static partial double godotsharp_randf_range(double from, double to);
+
+ internal static partial double godotsharp_randfn(double mean, double deviation);
+
+ internal static partial int godotsharp_randi_range(int from, int to);
+
+ internal static partial uint godotsharp_rand_from_seed(ulong seed, out ulong newSeed);
+
+ internal static partial void godotsharp_seed(ulong seed);
+
+ internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref);
+
+ internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret);
+
+ internal static partial void godotsharp_str_to_var(in godot_string p_str, out godot_variant r_ret);
+
+ internal static partial void godotsharp_var_to_bytes(in godot_variant p_what, godot_bool p_full_objects,
+ out godot_packed_byte_array r_bytes);
+
+ internal static partial void godotsharp_var_to_str(in godot_variant p_var, out godot_string r_ret);
+
+ internal static partial void godotsharp_pusherror(in godot_string p_str);
+
+ internal static partial void godotsharp_pushwarning(in godot_string p_str);
+
+ // Object
+
+ public static partial void godotsharp_object_to_string(IntPtr ptr, out godot_string r_str);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
new file mode 100644
index 0000000000..9f0b55431b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs
@@ -0,0 +1,103 @@
+// ReSharper disable InconsistentNaming
+
+namespace Godot.NativeInterop
+{
+ public static partial class NativeFuncs
+ {
+ public static godot_variant godotsharp_variant_new_copy(in godot_variant src)
+ {
+ switch (src.Type)
+ {
+ case Variant.Type.Nil:
+ return default;
+ case Variant.Type.Bool:
+ return new godot_variant() { Bool = src.Bool, Type = Variant.Type.Bool };
+ case Variant.Type.Int:
+ return new godot_variant() { Int = src.Int, Type = Variant.Type.Int };
+ case Variant.Type.Float:
+ return new godot_variant() { Float = src.Float, Type = Variant.Type.Float };
+ case Variant.Type.Vector2:
+ return new godot_variant() { Vector2 = src.Vector2, Type = Variant.Type.Vector2 };
+ case Variant.Type.Vector2i:
+ return new godot_variant() { Vector2i = src.Vector2i, Type = Variant.Type.Vector2i };
+ case Variant.Type.Rect2:
+ return new godot_variant() { Rect2 = src.Rect2, Type = Variant.Type.Rect2 };
+ case Variant.Type.Rect2i:
+ return new godot_variant() { Rect2i = src.Rect2i, Type = Variant.Type.Rect2i };
+ case Variant.Type.Vector3:
+ return new godot_variant() { Vector3 = src.Vector3, Type = Variant.Type.Vector3 };
+ case Variant.Type.Vector3i:
+ return new godot_variant() { Vector3i = src.Vector3i, Type = Variant.Type.Vector3i };
+ case Variant.Type.Vector4:
+ return new godot_variant() { Vector4 = src.Vector4, Type = Variant.Type.Vector4 };
+ case Variant.Type.Vector4i:
+ return new godot_variant() { Vector4i = src.Vector4i, Type = Variant.Type.Vector4i };
+ case Variant.Type.Plane:
+ return new godot_variant() { Plane = src.Plane, Type = Variant.Type.Plane };
+ case Variant.Type.Quaternion:
+ return new godot_variant() { Quaternion = src.Quaternion, Type = Variant.Type.Quaternion };
+ case Variant.Type.Color:
+ return new godot_variant() { Color = src.Color, Type = Variant.Type.Color };
+ case Variant.Type.Rid:
+ return new godot_variant() { RID = src.RID, Type = Variant.Type.Rid };
+ }
+
+ godotsharp_variant_new_copy(out godot_variant ret, src);
+ return ret;
+ }
+
+ public static godot_string_name godotsharp_string_name_new_copy(in godot_string_name src)
+ {
+ if (src.IsEmpty)
+ return default;
+ godotsharp_string_name_new_copy(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_copy(in godot_node_path src)
+ {
+ if (src.IsEmpty)
+ return default;
+ godotsharp_node_path_new_copy(out godot_node_path ret, src);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new()
+ {
+ godotsharp_array_new(out godot_array ret);
+ return ret;
+ }
+
+ public static godot_array godotsharp_array_new_copy(in godot_array src)
+ {
+ godotsharp_array_new_copy(out godot_array ret, src);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new()
+ {
+ godotsharp_dictionary_new(out godot_dictionary ret);
+ return ret;
+ }
+
+ public static godot_dictionary godotsharp_dictionary_new_copy(in godot_dictionary src)
+ {
+ godotsharp_dictionary_new_copy(out godot_dictionary ret, src);
+ return ret;
+ }
+
+ public static godot_string_name godotsharp_string_name_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_string_name_new_from_string(out godot_string_name ret, src);
+ return ret;
+ }
+
+ public static godot_node_path godotsharp_node_path_new_from_string(string name)
+ {
+ using godot_string src = Marshaling.ConvertStringToNative(name);
+ godotsharp_node_path_new_from_string(out godot_node_path ret, src);
+ return ret;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
new file mode 100644
index 0000000000..d8c5d99cb8
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeVariantPtrArgs.cs
@@ -0,0 +1,34 @@
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop
+{
+ // Our source generators will add trampolines methods that access variant arguments.
+ // This struct makes that possible without having to enable `AllowUnsafeBlocks` in game projects.
+
+ public unsafe ref struct NativeVariantPtrArgs
+ {
+ private godot_variant** _args;
+ private int _argc;
+
+ internal NativeVariantPtrArgs(godot_variant** args, int argc)
+ {
+ _args = args;
+ _argc = argc;
+ }
+
+ /// <summary>
+ /// Returns the number of arguments.
+ /// </summary>
+ public int Count
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _argc;
+ }
+
+ public ref godot_variant this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref *_args[index];
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
new file mode 100644
index 0000000000..9c9258dd9e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -0,0 +1,625 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot.Collections;
+
+// ReSharper disable InconsistentNaming
+
+#nullable enable
+
+namespace Godot.NativeInterop
+{
+ public static partial class VariantUtils
+ {
+ public static godot_variant CreateFromRID(RID from)
+ => new() { Type = Variant.Type.Rid, RID = from };
+
+ public static godot_variant CreateFromBool(bool from)
+ => new() { Type = Variant.Type.Bool, Bool = from.ToGodotBool() };
+
+ public static godot_variant CreateFromInt(long from)
+ => new() { Type = Variant.Type.Int, Int = from };
+
+ public static godot_variant CreateFromInt(ulong from)
+ => new() { Type = Variant.Type.Int, Int = (long)from };
+
+ public static godot_variant CreateFromFloat(double from)
+ => new() { Type = Variant.Type.Float, Float = from };
+
+ public static godot_variant CreateFromVector2(Vector2 from)
+ => new() { Type = Variant.Type.Vector2, Vector2 = from };
+
+ public static godot_variant CreateFromVector2i(Vector2i from)
+ => new() { Type = Variant.Type.Vector2i, Vector2i = from };
+
+ public static godot_variant CreateFromVector3(Vector3 from)
+ => new() { Type = Variant.Type.Vector3, Vector3 = from };
+
+ public static godot_variant CreateFromVector3i(Vector3i from)
+ => new() { Type = Variant.Type.Vector3i, Vector3i = from };
+
+ public static godot_variant CreateFromVector4(Vector4 from)
+ => new() { Type = Variant.Type.Vector4, Vector4 = from };
+
+ public static godot_variant CreateFromVector4i(Vector4i from)
+ => new() { Type = Variant.Type.Vector4i, Vector4i = from };
+
+ public static godot_variant CreateFromRect2(Rect2 from)
+ => new() { Type = Variant.Type.Rect2, Rect2 = from };
+
+ public static godot_variant CreateFromRect2i(Rect2i from)
+ => new() { Type = Variant.Type.Rect2i, Rect2i = from };
+
+ public static godot_variant CreateFromQuaternion(Quaternion from)
+ => new() { Type = Variant.Type.Quaternion, Quaternion = from };
+
+ public static godot_variant CreateFromColor(Color from)
+ => new() { Type = Variant.Type.Color, Color = from };
+
+ public static godot_variant CreateFromPlane(Plane from)
+ => new() { Type = Variant.Type.Plane, Plane = from };
+
+ public static godot_variant CreateFromTransform2D(Transform2D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform2d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromBasis(Basis from)
+ {
+ NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromTransform3D(Transform3D from)
+ {
+ NativeFuncs.godotsharp_variant_new_transform3d(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromProjection(Projection from)
+ {
+ NativeFuncs.godotsharp_variant_new_projection(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromAABB(AABB from)
+ {
+ NativeFuncs.godotsharp_variant_new_aabb(out godot_variant ret, from);
+ return ret;
+ }
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
+ => new() { Type = Variant.Type.Callable, Callable = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromCallable(Callable from)
+ => CreateFromCallableTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertCallableToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
+ => new() { Type = Variant.Type.Signal, Signal = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromSignal(Signal from)
+ => CreateFromSignalTakingOwnershipOfDisposableValue(
+ Marshaling.ConvertSignalToNative(from));
+
+ // Explicit name to make it very clear
+ public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
+ => new() { Type = Variant.Type.String, String = from };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromString(string? from)
+ => CreateFromStringTakingOwnershipOfDisposableValue(Marshaling.ConvertStringToNative(from));
+
+ public static godot_variant CreateFromPackedByteArray(in godot_packed_byte_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt32Array(in godot_packed_int32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedInt64Array(in godot_packed_int64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_int64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat32Array(in godot_packed_float32_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float32_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedFloat64Array(in godot_packed_float64_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_float64_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedStringArray(in godot_packed_string_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_string_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector2Array(in godot_packed_vector2_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector2_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedVector3Array(in godot_packed_vector3_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_vector3_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ public static godot_variant CreateFromPackedColorArray(in godot_packed_color_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_packed_color_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedByteArray(Span<byte> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedByteArray(from);
+ return CreateFromPackedByteArray(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt32Array(Span<int> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedInt32Array(from);
+ return CreateFromPackedInt32Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedInt64Array(Span<long> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedInt64Array(from);
+ return CreateFromPackedInt64Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat32Array(Span<float> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from);
+ return CreateFromPackedFloat32Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedFloat64Array(Span<double> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from);
+ return CreateFromPackedFloat64Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedStringArray(Span<string> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedStringArray(from);
+ return CreateFromPackedStringArray(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector2Array(Span<Vector2> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector2Array(from);
+ return CreateFromPackedVector2Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedVector3Array(Span<Vector3> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedVector3Array(from);
+ return CreateFromPackedVector3Array(nativePackedArray);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromPackedColorArray(Span<Color> from)
+ {
+ using var nativePackedArray = Marshaling.ConvertSystemArrayToNativePackedColorArray(from);
+ return CreateFromPackedColorArray(nativePackedArray);
+ }
+
+ public static godot_variant CreateFromSystemArrayOfStringName(Span<StringName> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from)
+ => CreateFromArray(new Collections.Array(from));
+
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from)
+ {
+ if (from == null)
+ return default; // Nil
+ using var fromGodot = new Collections.Array(from);
+ return CreateFromArray((godot_array)fromGodot.NativeValue);
+ }
+
+ public static godot_variant CreateFromArray(godot_array from)
+ {
+ NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray(Collections.Array? from)
+ => from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromArray<T>(Array<T>? from)
+ => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
+
+ public static godot_variant CreateFromDictionary(godot_dictionary from)
+ {
+ NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary(Dictionary? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from)
+ => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
+
+ public static godot_variant CreateFromStringName(godot_string_name from)
+ {
+ NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromStringName(StringName? from)
+ => from != null ? CreateFromStringName((godot_string_name)from.NativeValue) : default;
+
+ public static godot_variant CreateFromNodePath(godot_node_path from)
+ {
+ NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_variant CreateFromNodePath(NodePath? from)
+ => from != null ? CreateFromNodePath((godot_node_path)from.NativeValue) : default;
+
+ public static godot_variant CreateFromGodotObjectPtr(IntPtr from)
+ {
+ if (from == IntPtr.Zero)
+ return new godot_variant();
+ NativeFuncs.godotsharp_variant_new_object(out godot_variant ret, from);
+ return ret;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static godot_variant CreateFromGodotObject(Godot.Object? from)
+ => from != null ? CreateFromGodotObjectPtr(Object.GetPtr(from)) : default;
+
+ // We avoid the internal call if the stored type is the same we want.
+
+ public static bool ConvertToBool(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Bool ?
+ p_var.Bool.ToBool() :
+ NativeFuncs.godotsharp_variant_as_bool(p_var).ToBool();
+
+ public static char ConvertToChar(in godot_variant p_var)
+ => (char)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static sbyte ConvertToInt8(in godot_variant p_var)
+ => (sbyte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static short ConvertToInt16(in godot_variant p_var)
+ => (short)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static int ConvertToInt32(in godot_variant p_var)
+ => (int)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static long ConvertToInt64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var);
+
+ public static byte ConvertToUInt8(in godot_variant p_var)
+ => (byte)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ushort ConvertToUInt16(in godot_variant p_var)
+ => (ushort)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static uint ConvertToUInt32(in godot_variant p_var)
+ => (uint)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static ulong ConvertToUInt64(in godot_variant p_var)
+ => (ulong)(p_var.Type == Variant.Type.Int ?
+ p_var.Int :
+ NativeFuncs.godotsharp_variant_as_int(p_var));
+
+ public static float ConvertToFloat32(in godot_variant p_var)
+ => (float)(p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var));
+
+ public static double ConvertToFloat64(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Float ?
+ p_var.Float :
+ NativeFuncs.godotsharp_variant_as_float(p_var);
+
+ public static Vector2 ConvertToVector2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2 ?
+ p_var.Vector2 :
+ NativeFuncs.godotsharp_variant_as_vector2(p_var);
+
+ public static Vector2i ConvertToVector2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector2i ?
+ p_var.Vector2i :
+ NativeFuncs.godotsharp_variant_as_vector2i(p_var);
+
+ public static Rect2 ConvertToRect2(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2 ?
+ p_var.Rect2 :
+ NativeFuncs.godotsharp_variant_as_rect2(p_var);
+
+ public static Rect2i ConvertToRect2i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rect2i ?
+ p_var.Rect2i :
+ NativeFuncs.godotsharp_variant_as_rect2i(p_var);
+
+ public static unsafe Transform2D ConvertToTransform2D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform2d ?
+ *p_var.Transform2D :
+ NativeFuncs.godotsharp_variant_as_transform2d(p_var);
+
+ public static Vector3 ConvertToVector3(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3 ?
+ p_var.Vector3 :
+ NativeFuncs.godotsharp_variant_as_vector3(p_var);
+
+ public static Vector3i ConvertToVector3i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector3i ?
+ p_var.Vector3i :
+ NativeFuncs.godotsharp_variant_as_vector3i(p_var);
+
+ public static unsafe Vector4 ConvertToVector4(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4 ?
+ p_var.Vector4 :
+ NativeFuncs.godotsharp_variant_as_vector4(p_var);
+
+ public static unsafe Vector4i ConvertToVector4i(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Vector4i ?
+ p_var.Vector4i :
+ NativeFuncs.godotsharp_variant_as_vector4i(p_var);
+
+ public static unsafe Basis ConvertToBasis(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Basis ?
+ *p_var.Basis :
+ NativeFuncs.godotsharp_variant_as_basis(p_var);
+
+ public static Quaternion ConvertToQuaternion(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Quaternion ?
+ p_var.Quaternion :
+ NativeFuncs.godotsharp_variant_as_quaternion(p_var);
+
+ public static unsafe Transform3D ConvertToTransform3D(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Transform3d ?
+ *p_var.Transform3D :
+ NativeFuncs.godotsharp_variant_as_transform3d(p_var);
+
+ public static unsafe Projection ConvertToProjection(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Projection ?
+ *p_var.Projection :
+ NativeFuncs.godotsharp_variant_as_projection(p_var);
+
+ public static unsafe AABB ConvertToAABB(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Aabb ?
+ *p_var.AABB :
+ NativeFuncs.godotsharp_variant_as_aabb(p_var);
+
+ public static Color ConvertToColor(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Color ?
+ p_var.Color :
+ NativeFuncs.godotsharp_variant_as_color(p_var);
+
+ public static Plane ConvertToPlane(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Plane ?
+ p_var.Plane :
+ NativeFuncs.godotsharp_variant_as_plane(p_var);
+
+ public static RID ConvertToRID(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Rid ?
+ p_var.RID :
+ NativeFuncs.godotsharp_variant_as_rid(p_var);
+
+ public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // ReSharper disable once RedundantNameQualifier
+ public static Godot.Object ConvertToGodotObject(in godot_variant p_var)
+ => InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var));
+
+ public static string ConvertToString(in godot_variant p_var)
+ {
+ switch (p_var.Type)
+ {
+ case Variant.Type.Nil:
+ return ""; // Otherwise, Variant -> String would return the string "Null"
+ case Variant.Type.String:
+ {
+ // We avoid the internal call if the stored type is the same we want.
+ return Marshaling.ConvertStringToManaged(p_var.String);
+ }
+ default:
+ {
+ using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
+ return Marshaling.ConvertStringToManaged(godotString);
+ }
+ }
+ }
+
+ public static godot_string_name ConvertToNativeStringName(in godot_variant p_var)
+ => p_var.Type == Variant.Type.StringName ?
+ NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName) :
+ NativeFuncs.godotsharp_variant_as_string_name(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static StringName ConvertToStringName(in godot_variant p_var)
+ => StringName.CreateTakingOwnershipOfDisposableValue(ConvertToNativeStringName(p_var));
+
+ public static godot_node_path ConvertToNativeNodePath(in godot_variant p_var)
+ => p_var.Type == Variant.Type.NodePath ?
+ NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath) :
+ NativeFuncs.godotsharp_variant_as_node_path(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static NodePath ConvertToNodePath(in godot_variant p_var)
+ => NodePath.CreateTakingOwnershipOfDisposableValue(ConvertToNativeNodePath(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_callable ConvertToNativeCallable(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_callable(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Callable ConvertToCallable(in godot_variant p_var)
+ => Marshaling.ConvertCallableToManaged(ConvertToNativeCallable(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static godot_signal ConvertToNativeSignal(in godot_variant p_var)
+ => NativeFuncs.godotsharp_variant_as_signal(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Signal ConvertToSignal(in godot_variant p_var)
+ => Marshaling.ConvertSignalToManaged(ConvertToNativeSignal(p_var));
+
+ public static godot_array ConvertToNativeArray(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Array ?
+ NativeFuncs.godotsharp_array_new_copy(p_var.Array) :
+ NativeFuncs.godotsharp_variant_as_array(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Collections.Array ConvertToArray(in godot_variant p_var)
+ => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Array<T> ConvertToArray<T>(in godot_variant p_var)
+ => Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var));
+
+ public static godot_dictionary ConvertToNativeDictionary(in godot_variant p_var)
+ => p_var.Type == Variant.Type.Dictionary ?
+ NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
+ NativeFuncs.godotsharp_variant_as_dictionary(p_var);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary ConvertToDictionary(in godot_variant p_var)
+ => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(in godot_variant p_var)
+ => Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var));
+
+ public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
+ return Marshaling.ConvertNativePackedByteArrayToSystemArray(packedArray);
+ }
+
+ public static int[] ConvertAsPackedInt32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
+ return Marshaling.ConvertNativePackedInt32ArrayToSystemArray(packedArray);
+ }
+
+ public static long[] ConvertAsPackedInt64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
+ return Marshaling.ConvertNativePackedInt64ArrayToSystemArray(packedArray);
+ }
+
+ public static float[] ConvertAsPackedFloat32ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
+ return Marshaling.ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
+ }
+
+ public static double[] ConvertAsPackedFloat64ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
+ return Marshaling.ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
+ }
+
+ public static string[] ConvertAsPackedStringArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(packedArray);
+ }
+
+ public static Vector2[] ConvertAsPackedVector2ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
+ return Marshaling.ConvertNativePackedVector2ArrayToSystemArray(packedArray);
+ }
+
+ public static Vector3[] ConvertAsPackedVector3ArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
+ return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
+ }
+
+ public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
+ {
+ using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
+ return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
+ }
+
+ public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray);
+ }
+
+ public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray);
+ }
+
+ public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var)
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray);
+ }
+
+ public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
+ // ReSharper disable once RedundantNameQualifier
+ where T : Godot.Object
+ {
+ using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
+ return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray);
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
new file mode 100644
index 0000000000..3d64533269
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs
@@ -0,0 +1,414 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace Godot.NativeInterop;
+
+#nullable enable
+
+public partial class VariantUtils
+{
+ private static Exception UnsupportedType<T>() => new InvalidOperationException(
+ $"The type is not supported for conversion to/from Variant: '{typeof(T).FullName}'");
+
+ internal static class GenericConversion<T>
+ {
+ public static unsafe godot_variant ToVariant(in T from) =>
+ ToVariantCb != null ? ToVariantCb(from) : throw UnsupportedType<T>();
+
+ public static unsafe T FromVariant(in godot_variant variant) =>
+ FromVariantCb != null ? FromVariantCb(variant) : throw UnsupportedType<T>();
+
+ // ReSharper disable once StaticMemberInGenericType
+ internal static unsafe delegate*<in T, godot_variant> ToVariantCb;
+
+ // ReSharper disable once StaticMemberInGenericType
+ internal static unsafe delegate*<in godot_variant, T> FromVariantCb;
+
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ static GenericConversion()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static godot_variant CreateFrom<[MustBeVariant] T>(in T from)
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static TTo UnsafeAs<TTo>(in T f) => Unsafe.As<T, TTo>(ref Unsafe.AsRef(f));
+
+ // `typeof(T) == typeof(X)` is optimized away. We cannot cache `typeof(T)` in a local variable, as it's not optimized when done like that.
+
+ if (typeof(T) == typeof(bool))
+ return CreateFromBool(UnsafeAs<bool>(from));
+
+ if (typeof(T) == typeof(char))
+ return CreateFromInt(UnsafeAs<char>(from));
+
+ if (typeof(T) == typeof(sbyte))
+ return CreateFromInt(UnsafeAs<sbyte>(from));
+
+ if (typeof(T) == typeof(short))
+ return CreateFromInt(UnsafeAs<short>(from));
+
+ if (typeof(T) == typeof(int))
+ return CreateFromInt(UnsafeAs<int>(from));
+
+ if (typeof(T) == typeof(long))
+ return CreateFromInt(UnsafeAs<long>(from));
+
+ if (typeof(T) == typeof(byte))
+ return CreateFromInt(UnsafeAs<byte>(from));
+
+ if (typeof(T) == typeof(ushort))
+ return CreateFromInt(UnsafeAs<ushort>(from));
+
+ if (typeof(T) == typeof(uint))
+ return CreateFromInt(UnsafeAs<uint>(from));
+
+ if (typeof(T) == typeof(ulong))
+ return CreateFromInt(UnsafeAs<ulong>(from));
+
+ if (typeof(T) == typeof(float))
+ return CreateFromFloat(UnsafeAs<float>(from));
+
+ if (typeof(T) == typeof(double))
+ return CreateFromFloat(UnsafeAs<double>(from));
+
+ if (typeof(T) == typeof(Vector2))
+ return CreateFromVector2(UnsafeAs<Vector2>(from));
+
+ if (typeof(T) == typeof(Vector2i))
+ return CreateFromVector2i(UnsafeAs<Vector2i>(from));
+
+ if (typeof(T) == typeof(Rect2))
+ return CreateFromRect2(UnsafeAs<Rect2>(from));
+
+ if (typeof(T) == typeof(Rect2i))
+ return CreateFromRect2i(UnsafeAs<Rect2i>(from));
+
+ if (typeof(T) == typeof(Transform2D))
+ return CreateFromTransform2D(UnsafeAs<Transform2D>(from));
+
+ if (typeof(T) == typeof(Projection))
+ return CreateFromProjection(UnsafeAs<Projection>(from));
+
+ if (typeof(T) == typeof(Vector3))
+ return CreateFromVector3(UnsafeAs<Vector3>(from));
+
+ if (typeof(T) == typeof(Vector3i))
+ return CreateFromVector3i(UnsafeAs<Vector3i>(from));
+
+ if (typeof(T) == typeof(Basis))
+ return CreateFromBasis(UnsafeAs<Basis>(from));
+
+ if (typeof(T) == typeof(Quaternion))
+ return CreateFromQuaternion(UnsafeAs<Quaternion>(from));
+
+ if (typeof(T) == typeof(Transform3D))
+ return CreateFromTransform3D(UnsafeAs<Transform3D>(from));
+
+ if (typeof(T) == typeof(Vector4))
+ return CreateFromVector4(UnsafeAs<Vector4>(from));
+
+ if (typeof(T) == typeof(Vector4i))
+ return CreateFromVector4i(UnsafeAs<Vector4i>(from));
+
+ if (typeof(T) == typeof(AABB))
+ return CreateFromAABB(UnsafeAs<AABB>(from));
+
+ if (typeof(T) == typeof(Color))
+ return CreateFromColor(UnsafeAs<Color>(from));
+
+ if (typeof(T) == typeof(Plane))
+ return CreateFromPlane(UnsafeAs<Plane>(from));
+
+ if (typeof(T) == typeof(Callable))
+ return CreateFromCallable(UnsafeAs<Callable>(from));
+
+ if (typeof(T) == typeof(Signal))
+ return CreateFromSignal(UnsafeAs<Signal>(from));
+
+ if (typeof(T) == typeof(string))
+ return CreateFromString(UnsafeAs<string>(from));
+
+ if (typeof(T) == typeof(byte[]))
+ return CreateFromPackedByteArray(UnsafeAs<byte[]>(from));
+
+ if (typeof(T) == typeof(int[]))
+ return CreateFromPackedInt32Array(UnsafeAs<int[]>(from));
+
+ if (typeof(T) == typeof(long[]))
+ return CreateFromPackedInt64Array(UnsafeAs<long[]>(from));
+
+ if (typeof(T) == typeof(float[]))
+ return CreateFromPackedFloat32Array(UnsafeAs<float[]>(from));
+
+ if (typeof(T) == typeof(double[]))
+ return CreateFromPackedFloat64Array(UnsafeAs<double[]>(from));
+
+ if (typeof(T) == typeof(string[]))
+ return CreateFromPackedStringArray(UnsafeAs<string[]>(from));
+
+ if (typeof(T) == typeof(Vector2[]))
+ return CreateFromPackedVector2Array(UnsafeAs<Vector2[]>(from));
+
+ if (typeof(T) == typeof(Vector3[]))
+ return CreateFromPackedVector3Array(UnsafeAs<Vector3[]>(from));
+
+ if (typeof(T) == typeof(Color[]))
+ return CreateFromPackedColorArray(UnsafeAs<Color[]>(from));
+
+ if (typeof(T) == typeof(StringName[]))
+ return CreateFromSystemArrayOfStringName(UnsafeAs<StringName[]>(from));
+
+ if (typeof(T) == typeof(NodePath[]))
+ return CreateFromSystemArrayOfNodePath(UnsafeAs<NodePath[]>(from));
+
+ if (typeof(T) == typeof(RID[]))
+ return CreateFromSystemArrayOfRID(UnsafeAs<RID[]>(from));
+
+ if (typeof(T) == typeof(StringName))
+ return CreateFromStringName(UnsafeAs<StringName>(from));
+
+ if (typeof(T) == typeof(NodePath))
+ return CreateFromNodePath(UnsafeAs<NodePath>(from));
+
+ if (typeof(T) == typeof(RID))
+ return CreateFromRID(UnsafeAs<RID>(from));
+
+ if (typeof(T) == typeof(Godot.Collections.Dictionary))
+ return CreateFromDictionary(UnsafeAs<Godot.Collections.Dictionary>(from));
+
+ if (typeof(T) == typeof(Godot.Collections.Array))
+ return CreateFromArray(UnsafeAs<Godot.Collections.Array>(from));
+
+ if (typeof(T) == typeof(Variant))
+ return NativeFuncs.godotsharp_variant_new_copy((godot_variant)UnsafeAs<Variant>(from).NativeVar);
+
+ // More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
+
+ // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
+ return CreateFromGodotObject(UnsafeAs<Godot.Object>(from));
+
+ // `typeof(T).IsValueType` is optimized away
+ // `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
+ // Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
+
+ if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
+ {
+ // `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
+ // Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
+ // We don't need to know whether it's signed or unsigned.
+
+ if (Unsafe.SizeOf<T>() == 1)
+ return CreateFromInt(UnsafeAs<sbyte>(from));
+
+ if (Unsafe.SizeOf<T>() == 2)
+ return CreateFromInt(UnsafeAs<short>(from));
+
+ if (Unsafe.SizeOf<T>() == 4)
+ return CreateFromInt(UnsafeAs<int>(from));
+
+ if (Unsafe.SizeOf<T>() == 8)
+ return CreateFromInt(UnsafeAs<long>(from));
+
+ throw UnsupportedType<T>();
+ }
+
+ return GenericConversion<T>.ToVariant(from);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ [SuppressMessage("ReSharper", "RedundantNameQualifier")]
+ public static T ConvertTo<[MustBeVariant] T>(in godot_variant variant)
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static T UnsafeAsT<TFrom>(TFrom f) => Unsafe.As<TFrom, T>(ref Unsafe.AsRef(f));
+
+ if (typeof(T) == typeof(bool))
+ return UnsafeAsT(ConvertToBool(variant));
+
+ if (typeof(T) == typeof(char))
+ return UnsafeAsT(ConvertToChar(variant));
+
+ if (typeof(T) == typeof(sbyte))
+ return UnsafeAsT(ConvertToInt8(variant));
+
+ if (typeof(T) == typeof(short))
+ return UnsafeAsT(ConvertToInt16(variant));
+
+ if (typeof(T) == typeof(int))
+ return UnsafeAsT(ConvertToInt32(variant));
+
+ if (typeof(T) == typeof(long))
+ return UnsafeAsT(ConvertToInt64(variant));
+
+ if (typeof(T) == typeof(byte))
+ return UnsafeAsT(ConvertToUInt8(variant));
+
+ if (typeof(T) == typeof(ushort))
+ return UnsafeAsT(ConvertToUInt16(variant));
+
+ if (typeof(T) == typeof(uint))
+ return UnsafeAsT(ConvertToUInt32(variant));
+
+ if (typeof(T) == typeof(ulong))
+ return UnsafeAsT(ConvertToUInt64(variant));
+
+ if (typeof(T) == typeof(float))
+ return UnsafeAsT(ConvertToFloat32(variant));
+
+ if (typeof(T) == typeof(double))
+ return UnsafeAsT(ConvertToFloat64(variant));
+
+ if (typeof(T) == typeof(Vector2))
+ return UnsafeAsT(ConvertToVector2(variant));
+
+ if (typeof(T) == typeof(Vector2i))
+ return UnsafeAsT(ConvertToVector2i(variant));
+
+ if (typeof(T) == typeof(Rect2))
+ return UnsafeAsT(ConvertToRect2(variant));
+
+ if (typeof(T) == typeof(Rect2i))
+ return UnsafeAsT(ConvertToRect2i(variant));
+
+ if (typeof(T) == typeof(Transform2D))
+ return UnsafeAsT(ConvertToTransform2D(variant));
+
+ if (typeof(T) == typeof(Vector3))
+ return UnsafeAsT(ConvertToVector3(variant));
+
+ if (typeof(T) == typeof(Vector3i))
+ return UnsafeAsT(ConvertToVector3i(variant));
+
+ if (typeof(T) == typeof(Basis))
+ return UnsafeAsT(ConvertToBasis(variant));
+
+ if (typeof(T) == typeof(Quaternion))
+ return UnsafeAsT(ConvertToQuaternion(variant));
+
+ if (typeof(T) == typeof(Transform3D))
+ return UnsafeAsT(ConvertToTransform3D(variant));
+
+ if (typeof(T) == typeof(Projection))
+ return UnsafeAsT(ConvertToProjection(variant));
+
+ if (typeof(T) == typeof(Vector4))
+ return UnsafeAsT(ConvertToVector4(variant));
+
+ if (typeof(T) == typeof(Vector4i))
+ return UnsafeAsT(ConvertToVector4i(variant));
+
+ if (typeof(T) == typeof(AABB))
+ return UnsafeAsT(ConvertToAABB(variant));
+
+ if (typeof(T) == typeof(Color))
+ return UnsafeAsT(ConvertToColor(variant));
+
+ if (typeof(T) == typeof(Plane))
+ return UnsafeAsT(ConvertToPlane(variant));
+
+ if (typeof(T) == typeof(Callable))
+ return UnsafeAsT(ConvertToCallable(variant));
+
+ if (typeof(T) == typeof(Signal))
+ return UnsafeAsT(ConvertToSignal(variant));
+
+ if (typeof(T) == typeof(string))
+ return UnsafeAsT(ConvertToString(variant));
+
+ if (typeof(T) == typeof(byte[]))
+ return UnsafeAsT(ConvertAsPackedByteArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(int[]))
+ return UnsafeAsT(ConvertAsPackedInt32ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(long[]))
+ return UnsafeAsT(ConvertAsPackedInt64ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(float[]))
+ return UnsafeAsT(ConvertAsPackedFloat32ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(double[]))
+ return UnsafeAsT(ConvertAsPackedFloat64ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(string[]))
+ return UnsafeAsT(ConvertAsPackedStringArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(Vector2[]))
+ return UnsafeAsT(ConvertAsPackedVector2ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(Vector3[]))
+ return UnsafeAsT(ConvertAsPackedVector3ArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(Color[]))
+ return UnsafeAsT(ConvertAsPackedColorArrayToSystemArray(variant));
+
+ if (typeof(T) == typeof(StringName[]))
+ return UnsafeAsT(ConvertToSystemArrayOfStringName(variant));
+
+ if (typeof(T) == typeof(NodePath[]))
+ return UnsafeAsT(ConvertToSystemArrayOfNodePath(variant));
+
+ if (typeof(T) == typeof(RID[]))
+ return UnsafeAsT(ConvertToSystemArrayOfRID(variant));
+
+ if (typeof(T) == typeof(StringName))
+ return UnsafeAsT(ConvertToStringName(variant));
+
+ if (typeof(T) == typeof(NodePath))
+ return UnsafeAsT(ConvertToNodePath(variant));
+
+ if (typeof(T) == typeof(RID))
+ return UnsafeAsT(ConvertToRID(variant));
+
+ if (typeof(T) == typeof(Godot.Collections.Dictionary))
+ return UnsafeAsT(ConvertToDictionary(variant));
+
+ if (typeof(T) == typeof(Godot.Collections.Array))
+ return UnsafeAsT(ConvertToArray(variant));
+
+ if (typeof(T) == typeof(Variant))
+ return UnsafeAsT(Variant.CreateCopyingBorrowed(variant));
+
+ // More complex checks here at the end, to avoid screwing the simple ones in case they're not optimized away.
+
+ // `typeof(X).IsAssignableFrom(typeof(T))` is optimized away
+
+ if (typeof(Godot.Object).IsAssignableFrom(typeof(T)))
+ return (T)(object)ConvertToGodotObject(variant);
+
+ // `typeof(T).IsValueType` is optimized away
+ // `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
+ // Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
+
+ if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
+ {
+ // `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
+ // Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
+ // We don't need to know whether it's signed or unsigned.
+
+ if (Unsafe.SizeOf<T>() == 1)
+ return UnsafeAsT(ConvertToInt8(variant));
+
+ if (Unsafe.SizeOf<T>() == 2)
+ return UnsafeAsT(ConvertToInt16(variant));
+
+ if (Unsafe.SizeOf<T>() == 4)
+ return UnsafeAsT(ConvertToInt32(variant));
+
+ if (Unsafe.SizeOf<T>() == 8)
+ return UnsafeAsT(ConvertToInt64(variant));
+
+ throw UnsupportedType<T>();
+ }
+
+ return GenericConversion<T>.FromVariant(variant);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 9ae01016cb..b02bd167a1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -39,22 +39,11 @@ namespace Godot
/// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
/// </code>
/// </example>
- public sealed partial class NodePath : IDisposable
+ public sealed class NodePath : IDisposable
{
- private bool _disposed = false;
+ internal godot_node_path.movable NativeValue;
- private IntPtr ptr;
-
- internal static IntPtr GetPtr(NodePath instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null.");
-
- if (instance._disposed)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~NodePath()
{
@@ -70,29 +59,33 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (_disposed)
- return;
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
- if (ptr != IntPtr.Zero)
+ if (_weakReferenceToSelf != null)
{
- godot_icall_NodePath_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
-
- _disposed = true;
}
- internal NodePath(IntPtr ptr)
+ private NodePath(godot_node_path nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_node_path.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static NodePath CreateTakingOwnershipOfDisposableValue(godot_node_path nativeValueToOwn)
+ => new NodePath(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="NodePath"/>.
/// </summary>
- public NodePath() : this(string.Empty) { }
+ public NodePath()
+ {
+ }
/// <summary>
/// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>,
@@ -125,7 +118,11 @@ namespace Godot
/// <param name="path">A string that represents a path in a scene tree.</param>
public NodePath(string path)
{
- ptr = godot_icall_NodePath_Ctor(path);
+ if (!string.IsNullOrEmpty(path))
+ {
+ NativeValue = (godot_node_path.movable)NativeFuncs.godotsharp_node_path_new_from_string(path);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -138,7 +135,7 @@ namespace Godot
/// Converts this <see cref="NodePath"/> to a string.
/// </summary>
/// <param name="from">The <see cref="NodePath"/> to convert.</param>
- public static implicit operator string(NodePath from) => from.ToString();
+ public static implicit operator string(NodePath from) => from?.ToString();
/// <summary>
/// Converts this <see cref="NodePath"/> to a string.
@@ -146,7 +143,13 @@ namespace Godot
/// <returns>A string representation of this <see cref="NodePath"/>.</returns>
public override string ToString()
{
- return godot_icall_NodePath_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
@@ -166,7 +169,10 @@ namespace Godot
/// <returns>The <see cref="NodePath"/> as a pure property path.</returns>
public NodePath GetAsPropertyPath()
{
- return new NodePath(godot_icall_NodePath_get_as_property_path(GetPtr(this)));
+ godot_node_path propertyPath = default;
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_as_property_path(self, ref propertyPath);
+ return CreateTakingOwnershipOfDisposableValue(propertyPath);
}
/// <summary>
@@ -181,7 +187,10 @@ namespace Godot
/// <returns>The names concatenated with <c>/</c>.</returns>
public string GetConcatenatedNames()
{
- return godot_icall_NodePath_get_concatenated_names(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_names(self, out godot_string names);
+ using (names)
+ return Marshaling.ConvertStringToManaged(names);
}
/// <summary>
@@ -195,9 +204,12 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The subnames concatenated with <c>:</c>.</returns>
- public string GetConcatenatedSubnames()
+ public string GetConcatenatedSubNames()
{
- return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_concatenated_subnames(self, out godot_string subNames);
+ using (subNames)
+ return Marshaling.ConvertStringToManaged(subNames);
}
/// <summary>
@@ -215,28 +227,35 @@ namespace Godot
/// <returns>The name at the given index <paramref name="idx"/>.</returns>
public string GetName(int idx)
{
- return godot_icall_NodePath_get_name(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_name(self, idx, out godot_string name);
+ using (name)
+ return Marshaling.ConvertStringToManaged(name);
}
/// <summary>
/// Gets the number of node names which make up the path.
- /// Subnames (see <see cref="GetSubnameCount"/>) are not included.
+ /// Subnames (see <see cref="GetSubNameCount"/>) are not included.
/// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names.
/// </summary>
/// <returns>The number of node names which make up the path.</returns>
public int GetNameCount()
{
- return godot_icall_NodePath_get_name_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_name_count(self);
}
/// <summary>
- /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>).
+ /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubNameCount"/>).
/// </summary>
/// <param name="idx">The subname index.</param>
/// <returns>The subname at the given index <paramref name="idx"/>.</returns>
- public string GetSubname(int idx)
+ public string GetSubName(int idx)
{
- return godot_icall_NodePath_get_subname(GetPtr(this), idx);
+ var self = (godot_node_path)NativeValue;
+ NativeFuncs.godotsharp_node_path_get_subname(self, idx, out godot_string subName);
+ using (subName)
+ return Marshaling.ConvertStringToManaged(subName);
}
/// <summary>
@@ -245,9 +264,10 @@ namespace Godot
/// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames.
/// </summary>
/// <returns>The number of subnames in the path.</returns>
- public int GetSubnameCount()
+ public int GetSubNameCount()
{
- return godot_icall_NodePath_get_subname_count(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_get_subname_count(self);
}
/// <summary>
@@ -259,52 +279,14 @@ namespace Godot
/// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
public bool IsAbsolute()
{
- return godot_icall_NodePath_is_absolute(GetPtr(this));
+ var self = (godot_node_path)NativeValue;
+ return NativeFuncs.godotsharp_node_path_is_absolute(self).ToBool();
}
/// <summary>
/// Returns <see langword="true"/> if the node path is empty.
/// </summary>
/// <returns>If the <see cref="NodePath"/> is empty.</returns>
- public bool IsEmpty()
- {
- return godot_icall_NodePath_is_empty(GetPtr(this));
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_NodePath_Ctor(string path);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_NodePath_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_operator_String(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- 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)]
- private static extern string godot_icall_NodePath_get_name(IntPtr ptr, int arg1);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int godot_icall_NodePath_get_name_count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int godot_icall_NodePath_get_subname_count(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_NodePath_is_absolute(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_NodePath_is_empty(IntPtr ptr);
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index 746612477d..60ee6eb6f4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -1,54 +1,78 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.Bridge;
+using Godot.NativeInterop;
namespace Godot
{
public partial class Object : IDisposable
{
private bool _disposed = false;
+ private static readonly Type CachedType = typeof(Object);
- private static StringName nativeName = "Object";
+ internal IntPtr NativePtr;
+ private bool _memoryOwn;
- internal IntPtr ptr;
- internal bool memoryOwn;
+ private WeakReference<Object> _weakReferenceToSelf;
/// <summary>
/// Constructs a new <see cref="Object"/>.
/// </summary>
public Object() : this(false)
{
- if (ptr == IntPtr.Zero)
- ptr = godot_icall_Object_Ctor(this);
- _InitializeGodotScriptInstanceInternals();
+ unsafe
+ {
+ _ConstructAndInitialize(NativeCtor, NativeName, CachedType, refCounted: false);
+ }
}
- internal void _InitializeGodotScriptInstanceInternals()
+ internal unsafe void _ConstructAndInitialize(
+ delegate* unmanaged<IntPtr> nativeCtor,
+ StringName nativeName,
+ Type cachedType,
+ bool refCounted
+ )
{
- godot_icall_Object_ConnectEventSignals(ptr);
+ if (NativePtr == IntPtr.Zero)
+ {
+ NativePtr = nativeCtor();
+
+ InteropUtils.TieManagedToUnmanaged(this, NativePtr,
+ nativeName, refCounted, GetType(), cachedType);
+ }
+ else
+ {
+ InteropUtils.TieManagedToUnmanagedWithPreSetup(this, NativePtr,
+ GetType(), cachedType);
+ }
+
+ _weakReferenceToSelf = DisposablesTracker.RegisterGodotObject(this);
}
internal Object(bool memoryOwn)
{
- this.memoryOwn = memoryOwn;
+ _memoryOwn = memoryOwn;
}
/// <summary>
/// The pointer to the native instance of this <see cref="Object"/>.
/// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
+ public IntPtr NativeInstance => NativePtr;
internal static IntPtr GetPtr(Object instance)
{
if (instance == null)
return IntPtr.Zero;
- if (instance._disposed)
+ // We check if NativePtr is null because this may be called by the debugger.
+ // If the debugger puts a breakpoint in one of the base constructors, before
+ // NativePtr is assigned, that would result in UB or crashes when calling
+ // native functions that receive the pointer, which can happen because the
+ // debugger calls ToString() and tries to get the value of properties.
+ if (instance._disposed || instance.NativePtr == IntPtr.Zero)
throw new ObjectDisposedException(instance.GetType().FullName);
- return instance.ptr;
+ return instance.NativePtr;
}
~Object()
@@ -73,22 +97,35 @@ namespace Godot
if (_disposed)
return;
- if (ptr != IntPtr.Zero)
+ _disposed = true;
+
+ if (NativePtr != IntPtr.Zero)
{
- if (memoryOwn)
+ IntPtr gcHandleToFree = NativeFuncs.godotsharp_internal_object_get_associated_gchandle(NativePtr);
+
+ if (gcHandleToFree != IntPtr.Zero)
+ {
+ object target = GCHandle.FromIntPtr(gcHandleToFree).Target;
+ // The GC handle may have been replaced in another thread. Release it only if
+ // it's associated to this managed instance, or if the target is no longer alive.
+ if (target != this && target != null)
+ gcHandleToFree = IntPtr.Zero;
+ }
+
+ if (_memoryOwn)
{
- memoryOwn = false;
- godot_icall_RefCounted_Disposed(this, ptr, !disposing);
+ NativeFuncs.godotsharp_internal_refcounted_disposed(NativePtr, gcHandleToFree,
+ (!disposing).ToGodotBool());
}
else
{
- godot_icall_Object_Disposed(this, ptr);
+ NativeFuncs.godotsharp_internal_object_disposed(NativePtr, gcHandleToFree);
}
- ptr = IntPtr.Zero;
+ NativePtr = IntPtr.Zero;
}
- _disposed = true;
+ DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
}
/// <summary>
@@ -97,7 +134,9 @@ namespace Godot
/// <returns>A string representation of this object.</returns>
public override string ToString()
{
- return godot_icall_Object_ToString(GetPtr(this));
+ NativeFuncs.godotsharp_object_to_string(GetPtr(this), out godot_string str);
+ using (str)
+ return Marshaling.ConvertStringToManaged(str);
}
/// <summary>
@@ -132,33 +171,72 @@ namespace Godot
return new SignalAwaiter(source, signal, this);
}
- /// <summary>
- /// Gets a new <see cref="DynamicGodotObject"/> associated with this instance.
- /// </summary>
- public dynamic DynamicObject => new DynamicGodotObject(this);
+ internal static Type InternalGetClassNativeBase(Type t)
+ {
+ do
+ {
+ var assemblyName = t.Assembly.GetName();
- internal static IntPtr __ClassDB_get_method(StringName type, string method)
+ if (assemblyName.Name == "GodotSharp")
+ return t;
+
+ if (assemblyName.Name == "GodotSharpEditor")
+ return t;
+ } while ((t = t.BaseType) != null);
+
+ return null;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
+ {
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
+ {
+ value = default;
+ return false;
+ }
+
+ // ReSharper disable once VirtualMemberNeverOverridden.Global
+ protected internal virtual void RaiseGodotClassSignalCallbacks(in godot_string_name signal,
+ NativeVariantPtrArgs args)
{
- return godot_icall_Object_ClassDB_get_method(StringName.GetPtr(type), method);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_Ctor(Object obj);
+ internal static IntPtr ClassDB_get_method(StringName type, StringName method)
+ {
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var methodSelf = (godot_string_name)method.NativeValue;
+ IntPtr methodBind = NativeFuncs.godotsharp_method_bind_get_method(typeSelf, methodSelf);
+
+ if (methodBind == IntPtr.Zero)
+ throw new NativeMethodBindNotFoundException(type + "." + method);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
+ return methodBind;
+ }
+
+ internal static unsafe delegate* unmanaged<IntPtr> ClassDB_get_constructor(StringName type)
+ {
+ // for some reason the '??' operator doesn't support 'delegate*'
+ var typeSelf = (godot_string_name)type.NativeValue;
+ var nativeConstructor = NativeFuncs.godotsharp_get_class_constructor(typeSelf);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ if (nativeConstructor == null)
+ throw new NativeConstructorNotFoundException(type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
+ return nativeConstructor;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_Object_ToString(IntPtr ptr);
+ protected internal virtual void SaveGodotObjectData(GodotSerializationInfo info)
+ {
+ }
- // Used by the generated API
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_Object_ClassDB_get_method(IntPtr type, string method);
+ // TODO: Should this be a constructor overload?
+ protected internal virtual void RestoreGodotObjectData(GodotSerializationInfo info)
+ {
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
new file mode 100644
index 0000000000..0fcc4ee01b
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.exceptions.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Text;
+
+#nullable enable
+
+namespace Godot
+{
+ public partial class Object
+ {
+ public class NativeMemberNotFoundException : Exception
+ {
+ public NativeMemberNotFoundException()
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message) : base(message)
+ {
+ }
+
+ public NativeMemberNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+
+ public class NativeConstructorNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeClassName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeConstructorNotFoundException = "Unable to find the native constructor.";
+
+ public NativeConstructorNotFoundException()
+ : base(Arg_NativeConstructorNotFoundException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? nativeClassName)
+ : this(Arg_NativeConstructorNotFoundException, nativeClassName)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName)
+ : base(message)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public NativeConstructorNotFoundException(string? message, string? nativeClassName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeClassName = nativeClassName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ StringBuilder sb;
+ if (string.IsNullOrEmpty(base.Message))
+ {
+ sb = new(Arg_NativeConstructorNotFoundException);
+ }
+ else
+ {
+ sb = new(base.Message);
+ }
+
+ if (!string.IsNullOrEmpty(_nativeClassName))
+ {
+ sb.Append($" (Method '{_nativeClassName}')");
+ }
+
+ return sb.ToString();
+ }
+ }
+ }
+
+ public class NativeMethodBindNotFoundException : NativeMemberNotFoundException
+ {
+ private readonly string? _nativeMethodName;
+
+ // ReSharper disable once InconsistentNaming
+ private const string Arg_NativeMethodBindNotFoundException = "Unable to find the native method bind.";
+
+ public NativeMethodBindNotFoundException()
+ : base(Arg_NativeMethodBindNotFoundException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? nativeMethodName)
+ : this(Arg_NativeMethodBindNotFoundException, nativeMethodName)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName)
+ : base(message)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public NativeMethodBindNotFoundException(string? message, string? nativeMethodName, Exception? innerException)
+ : base(message, innerException)
+ {
+ _nativeMethodName = nativeMethodName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ StringBuilder sb;
+ if (string.IsNullOrEmpty(base.Message))
+ {
+ sb = new(Arg_NativeMethodBindNotFoundException);
+ }
+ else
+ {
+ sb = new(base.Message);
+ }
+
+ if (!string.IsNullOrEmpty(_nativeMethodName))
+ {
+ sb.Append($" (Method '{_nativeMethodName}')");
+ }
+
+ return sb.ToString();
+ }
+ }
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index fd97a71e47..8a125e3c73 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -20,14 +15,14 @@ namespace Godot
private Vector3 _normal;
/// <summary>
- /// The normal of the plane, which must be normalized.
+ /// The normal of the plane, which must be a unit vector.
/// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is
/// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property.
/// </summary>
/// <value>Equivalent to <see cref="x"/>, <see cref="y"/>, and <see cref="z"/>.</value>
public Vector3 Normal
{
- get { return _normal; }
+ readonly get { return _normal; }
set { _normal = value; }
}
@@ -37,7 +32,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Normal"/>'s X value.</value>
public real_t x
{
- get
+ readonly get
{
return _normal.x;
}
@@ -53,7 +48,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Normal"/>'s Y value.</value>
public real_t y
{
- get
+ readonly get
{
return _normal.y;
}
@@ -69,7 +64,7 @@ namespace Godot
/// <value>Equivalent to <see cref="Normal"/>'s Z value.</value>
public real_t z
{
- get
+ readonly get
{
return _normal.z;
}
@@ -90,30 +85,23 @@ namespace Godot
public real_t D { get; set; }
/// <summary>
- /// The center of the plane, the point where the normal line intersects the plane.
+ /// Returns the shortest distance from this plane to the position <paramref name="point"/>.
/// </summary>
- /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
- public Vector3 Center
+ /// <param name="point">The position to use for the calculation.</param>
+ /// <returns>The shortest distance.</returns>
+ public readonly real_t DistanceTo(Vector3 point)
{
- get
- {
- return _normal * D;
- }
- set
- {
- _normal = value.Normalized();
- D = value.Length();
- }
+ return _normal.Dot(point) - D;
}
/// <summary>
- /// Returns the shortest distance from this plane to the position <paramref name="point"/>.
+ /// Returns the center of the plane, the point on the plane closest to the origin.
+ /// The point where the normal line going through the origin intersects the plane.
/// </summary>
- /// <param name="point">The position to use for the calculation.</param>
- /// <returns>The shortest distance.</returns>
- public real_t DistanceTo(Vector3 point)
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
+ public readonly Vector3 GetCenter()
{
- return _normal.Dot(point) - D;
+ return _normal * D;
}
/// <summary>
@@ -123,7 +111,7 @@ namespace Godot
/// <param name="point">The point to check.</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 tolerance = Mathf.Epsilon)
+ public readonly bool HasPoint(Vector3 point, real_t tolerance = Mathf.Epsilon)
{
real_t dist = _normal.Dot(point) - D;
return Mathf.Abs(dist) <= tolerance;
@@ -136,7 +124,7 @@ namespace Godot
/// <param name="b">One of the three planes to use in the calculation.</param>
/// <param name="c">One of the three planes to use in the calculation.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public Vector3? Intersect3(Plane b, Plane c)
+ public readonly Vector3? Intersect3(Plane b, Plane c)
{
real_t denom = _normal.Cross(b._normal).Dot(c._normal);
@@ -160,7 +148,7 @@ namespace Godot
/// <param name="from">The start of the ray.</param>
/// <param name="dir">The direction of the ray, normalized.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public Vector3? IntersectRay(Vector3 from, Vector3 dir)
+ public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
@@ -188,7 +176,7 @@ namespace Godot
/// <param name="begin">The start of the line segment.</param>
/// <param name="end">The end of the line segment.</param>
/// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
- public Vector3? IntersectSegment(Vector3 begin, Vector3 end)
+ public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
real_t den = _normal.Dot(segment);
@@ -210,11 +198,21 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this plane is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return _normal.IsFinite() && Mathf.IsFinite(D);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane.
/// </summary>
/// <param name="point">The point to check.</param>
/// <returns>A <see langword="bool"/> for whether or not the point is above the plane.</returns>
- public bool IsPointOver(Vector3 point)
+ public readonly bool IsPointOver(Vector3 point)
{
return _normal.Dot(point) > D;
}
@@ -223,7 +221,7 @@ namespace Godot
/// Returns the plane scaled to unit length.
/// </summary>
/// <returns>A normalized version of the plane.</returns>
- public Plane Normalized()
+ public readonly Plane Normalized()
{
real_t len = _normal.Length();
@@ -240,7 +238,7 @@ namespace Godot
/// </summary>
/// <param name="point">The point to project.</param>
/// <returns>The projected point.</returns>
- public Vector3 Project(Vector3 point)
+ public readonly Vector3 Project(Vector3 point)
{
return point - (_normal * DistanceTo(point));
}
@@ -285,10 +283,21 @@ namespace Godot
}
/// <summary>
+ /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector.
+ /// The plane will intersect the origin.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
+ public Plane(Vector3 normal)
+ {
+ _normal = normal;
+ D = 0;
+ }
+
+ /// <summary>
/// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
/// the plane's distance to the origin <paramref name="d"/>.
/// </summary>
- /// <param name="normal">The normal of the plane, must be normalized.</param>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(Vector3 normal, real_t d)
{
@@ -297,6 +306,18 @@ namespace Godot
}
/// <summary>
+ /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
+ /// a <paramref name="point"/> on the plane.
+ /// </summary>
+ /// <param name="normal">The normal of the plane, must be a unit vector.</param>
+ /// <param name="point">The point on the plane.</param>
+ public Plane(Vector3 normal, Vector3 point)
+ {
+ _normal = normal;
+ D = _normal.Dot(point);
+ }
+
+ /// <summary>
/// Constructs a <see cref="Plane"/> from the three points, given in clockwise order.
/// </summary>
/// <param name="v1">The first point.</param>
@@ -356,14 +377,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The other object to compare.</param>
/// <returns>Whether or not the plane and the other object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Plane)
- {
- return Equals((Plane)obj);
- }
-
- return false;
+ return obj is Plane other && Equals(other);
}
/// <summary>
@@ -371,7 +387,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other plane to compare.</param>
/// <returns>Whether or not the planes are exactly equal.</returns>
- public bool Equals(Plane other)
+ public readonly bool Equals(Plane other)
{
return _normal == other._normal && D == other.D;
}
@@ -382,7 +398,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other plane to compare.</param>
/// <returns>Whether or not the planes are approximately equal.</returns>
- public bool IsEqualApprox(Plane other)
+ public readonly bool IsEqualApprox(Plane other)
{
return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D);
}
@@ -391,7 +407,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Plane"/>.
/// </summary>
/// <returns>A hash code for this plane.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return _normal.GetHashCode() ^ D.GetHashCode();
}
@@ -400,7 +416,7 @@ namespace Godot
/// Converts this <see cref="Plane"/> to a string.
/// </summary>
/// <returns>A string representation of this plane.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"{_normal}, {D}";
}
@@ -409,7 +425,7 @@ namespace Godot
/// Converts this <see cref="Plane"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this plane.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"{_normal.ToString(format)}, {D.ToString(format)}";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
new file mode 100644
index 0000000000..f11b3c553a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs
@@ -0,0 +1,1028 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ /// <summary>
+ /// A 4x4 matrix used for 3D projective transformations. It can represent transformations such as
+ /// translation, rotation, scaling, shearing, and perspective division. It consists of four
+ /// <see cref="Vector4"/> columns.
+ /// For purely linear transformations (translation, rotation, and scale), it is recommended to use
+ /// <see cref="Transform3D"/>, as it is more performant and has a lower memory footprint.
+ /// Used internally as <see cref="Camera3D"/>'s projection matrix.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Projection : IEquatable<Projection>
+ {
+ /// <summary>
+ /// Enumerated index values for the planes.
+ /// </summary>
+ public enum Planes
+ {
+ /// <summary>
+ /// The projection's near plane.
+ /// </summary>
+ Near,
+ /// <summary>
+ /// The projection's far plane.
+ /// </summary>
+ Far,
+ /// <summary>
+ /// The projection's left plane.
+ /// </summary>
+ Left,
+ /// <summary>
+ /// The projection's top plane.
+ /// </summary>
+ Top,
+ /// <summary>
+ /// The projection's right plane.
+ /// </summary>
+ Right,
+ /// <summary>
+ /// The projection's bottom plane.
+ /// </summary>
+ Bottom,
+ }
+
+ /// <summary>
+ /// The projection's X column. Also accessible by using the index position <c>[0]</c>.
+ /// </summary>
+ public Vector4 x;
+
+ /// <summary>
+ /// The projection's Y column. Also accessible by using the index position <c>[1]</c>.
+ /// </summary>
+ public Vector4 y;
+
+ /// <summary>
+ /// The projection's Z column. Also accessible by using the index position <c>[2]</c>.
+ /// </summary>
+ public Vector4 z;
+
+ /// <summary>
+ /// The projection's W column. Also accessible by using the index position <c>[3]</c>.
+ /// </summary>
+ public Vector4 w;
+
+ /// <summary>
+ /// Access whole columns in the form of <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="column">Which column vector.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> is not 0, 1, 2 or 3.
+ /// </exception>
+ public Vector4 this[int column]
+ {
+ readonly get
+ {
+ switch (column)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ set
+ {
+ switch (column)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Access single values.
+ /// </summary>
+ /// <param name="column">Which column vector.</param>
+ /// <param name="row">Which row of the column.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> or <paramref name="row"/> are not 0, 1, 2 or 3.
+ /// </exception>
+ public real_t this[int column, int row]
+ {
+ readonly get
+ {
+ switch (column)
+ {
+ case 0:
+ return x[row];
+ case 1:
+ return y[row];
+ case 2:
+ return z[row];
+ case 3:
+ return w[row];
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ set
+ {
+ switch (column)
+ {
+ case 0:
+ x[row] = value;
+ return;
+ case 1:
+ y[row] = value;
+ return;
+ case 2:
+ z[row] = value;
+ return;
+ case 3:
+ w[row] = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(column));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions from a depth range of
+ /// <c>-1</c> to <c>1</c> to one that ranges from <c>0</c> to <c>1</c>, and flips the projected
+ /// positions vertically, according to <paramref name="flipY"/>.
+ /// </summary>
+ /// <param name="flipY">If the projection should be flipped vertically.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateDepthCorrection(bool flipY)
+ {
+ return new Projection(
+ new Vector4(1, 0, 0, 0),
+ new Vector4(0, flipY ? -1 : 1, 0, 0),
+ new Vector4(0, 0, (real_t)0.5, 0),
+ new Vector4(0, 0, (real_t)0.5, 1)
+ );
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that scales a given projection to fit around
+ /// a given <see cref="AABB"/> in projection space.
+ /// </summary>
+ /// <param name="aabb">The AABB to fit the projection around.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateFitAabb(AABB aabb)
+ {
+ Vector3 min = aabb.Position;
+ Vector3 max = aabb.Position + aabb.Size;
+
+ return new Projection(
+ new Vector4(2 / (max.x - min.x), 0, 0, 0),
+ new Vector4(0, 2 / (max.y - min.y), 0, 0),
+ new Vector4(0, 0, 2 / (max.z - min.z), 0),
+ new Vector4(-(max.x + min.x) / (max.x - min.x), -(max.y + min.y) / (max.y - min.y), -(max.z + min.z) / (max.z - min.z), 1)
+ );
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> for projecting positions onto a head-mounted display with
+ /// the given X:Y aspect ratio, distance between eyes, display width, distance to lens, oversampling factor,
+ /// and depth clipping planes.
+ /// <paramref name="eye"/> creates the projection for the left eye when set to 1,
+ /// or the right eye when set to 2.
+ /// </summary>
+ /// <param name="eye">
+ /// The eye to create the projection for.
+ /// The left eye when set to 1, the right eye when set to 2.
+ /// </param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="intraocularDist">The distance between the eyes.</param>
+ /// <param name="displayWidth">The display width.</param>
+ /// <param name="displayToLens">The distance to the lens.</param>
+ /// <param name="oversample">The oversampling factor.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateForHmd(int eye, real_t aspect, real_t intraocularDist, real_t displayWidth, real_t displayToLens, real_t oversample, real_t zNear, real_t zFar)
+ {
+ real_t f1 = (intraocularDist * (real_t)0.5) / displayToLens;
+ real_t f2 = ((displayWidth - intraocularDist) * (real_t)0.5) / displayToLens;
+ real_t f3 = (displayWidth / (real_t)4.0) / displayToLens;
+
+ real_t add = ((f1 + f2) * (oversample - (real_t)1.0)) / (real_t)2.0;
+ f1 += add;
+ f2 += add;
+ f3 *= oversample;
+
+ f3 /= aspect;
+
+ switch (eye)
+ {
+ case 1:
+ return CreateFrustum(-f2 * zNear, f1 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar);
+ case 2:
+ return CreateFrustum(-f1 * zNear, f2 * zNear, -f3 * zNear, f3 * zNear, zNear, zFar);
+ default:
+ return Zero;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions in a frustum with
+ /// the given clipping planes.
+ /// </summary>
+ /// <param name="left">The left clipping distance.</param>
+ /// <param name="right">The right clipping distance.</param>
+ /// <param name="bottom">The bottom clipping distance.</param>
+ /// <param name="top">The top clipping distance.</param>
+ /// <param name="near">The near clipping distance.</param>
+ /// <param name="far">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateFrustum(real_t left, real_t right, real_t bottom, real_t top, real_t near, real_t far)
+ {
+ if (right <= left)
+ {
+ throw new ArgumentException("right is less or equal to left.");
+ }
+ if (top <= bottom)
+ {
+ throw new ArgumentException("top is less or equal to bottom.");
+ }
+ if (far <= near)
+ {
+ throw new ArgumentException("far is less or equal to near.");
+ }
+
+ real_t x = 2 * near / (right - left);
+ real_t y = 2 * near / (top - bottom);
+
+ real_t a = (right + left) / (right - left);
+ real_t b = (top + bottom) / (top - bottom);
+ real_t c = -(far + near) / (far - near);
+ real_t d = -2 * far * near / (far - near);
+
+ return new Projection(
+ new Vector4(x, 0, 0, 0),
+ new Vector4(0, y, 0, 0),
+ new Vector4(a, b, c, -1),
+ new Vector4(0, 0, d, 0)
+ );
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions in a frustum with
+ /// the given size, X:Y aspect ratio, offset, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="size">The frustum size.</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="offset">The offset to apply.</param>
+ /// <param name="near">The near clipping distance.</param>
+ /// <param name="far">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateFrustumAspect(real_t size, real_t aspect, Vector2 offset, real_t near, real_t far, bool flipFov)
+ {
+ if (!flipFov)
+ {
+ size *= aspect;
+ }
+ return CreateFrustum(-size / 2 + offset.x, +size / 2 + offset.x, -size / aspect / 2 + offset.y, +size / aspect / 2 + offset.y, near, far);
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions into the given <see cref="Rect2"/>.
+ /// </summary>
+ /// <param name="rect">The Rect2 to project positions into.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateLightAtlasRect(Rect2 rect)
+ {
+ return new Projection(
+ new Vector4(rect.Size.x, 0, 0, 0),
+ new Vector4(0, rect.Size.y, 0, 0),
+ new Vector4(0, 0, 1, 0),
+ new Vector4(rect.Position.x, rect.Position.y, 0, 1)
+ );
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using an orthogonal projection with
+ /// the given clipping planes.
+ /// </summary>
+ /// <param name="left">The left clipping distance.</param>
+ /// <param name="right">The right clipping distance.</param>
+ /// <param name="bottom">The bottom clipping distance.</param>
+ /// <param name="top">The top clipping distance.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateOrthogonal(real_t left, real_t right, real_t bottom, real_t top, real_t zNear, real_t zFar)
+ {
+ Projection proj = Projection.Identity;
+ proj.x.x = (real_t)2.0 / (right - left);
+ proj.w.x = -((right + left) / (right - left));
+ proj.y.y = (real_t)2.0 / (top - bottom);
+ proj.w.y = -((top + bottom) / (top - bottom));
+ proj.z.z = (real_t)(-2.0) / (zFar - zNear);
+ proj.w.z = -((zFar + zNear) / (zFar - zNear));
+ proj.w.w = (real_t)1.0;
+ return proj;
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using an orthogonal projection with
+ /// the given size, X:Y aspect ratio, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="size">The frustum size.</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreateOrthogonalAspect(real_t size, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
+ {
+ if (!flipFov)
+ {
+ size *= aspect;
+ }
+ return CreateOrthogonal(-size / 2, +size / 2, -size / aspect / 2, +size / aspect / 2, zNear, zFar);
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using a perspective projection with
+ /// the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping planes.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="fovyDegrees">The vertical field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreatePerspective(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov)
+ {
+ if (flipFov)
+ {
+ fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect);
+ }
+ real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0);
+ real_t deltaZ = zFar - zNear;
+ (real_t sin, real_t cos) = Mathf.SinCos(radians);
+
+ if ((deltaZ == 0) || (sin == 0) || (aspect == 0))
+ {
+ return Zero;
+ }
+
+ real_t cotangent = cos / sin;
+
+ Projection proj = Projection.Identity;
+
+ proj.x.x = cotangent / aspect;
+ proj.y.y = cotangent;
+ proj.z.z = -(zFar + zNear) / deltaZ;
+ proj.z.w = -1;
+ proj.w.z = -2 * zNear * zFar / deltaZ;
+ proj.w.w = 0;
+
+ return proj;
+ }
+
+ /// <summary>
+ /// Creates a new <see cref="Projection"/> that projects positions using a perspective projection with
+ /// the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping distances.
+ /// The projection is adjusted for a head-mounted display with the given distance between eyes and distance
+ /// to a point that can be focused on.
+ /// <paramref name="eye"/> creates the projection for the left eye when set to 1,
+ /// or the right eye when set to 2.
+ /// <paramref name="flipFov"/> determines whether the projection's field of view is flipped over its diagonal.
+ /// </summary>
+ /// <param name="fovyDegrees">The vertical field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <param name="zNear">The near clipping distance.</param>
+ /// <param name="zFar">The far clipping distance.</param>
+ /// <param name="flipFov">If the field of view is flipped over the projection's diagonal.</param>
+ /// <param name="eye">
+ /// The eye to create the projection for.
+ /// The left eye when set to 1, the right eye when set to 2.
+ /// </param>
+ /// <param name="intraocularDist">The distance between the eyes.</param>
+ /// <param name="convergenceDist">The distance to a point of convergence that can be focused on.</param>
+ /// <returns>The created projection.</returns>
+ public static Projection CreatePerspectiveHmd(real_t fovyDegrees, real_t aspect, real_t zNear, real_t zFar, bool flipFov, int eye, real_t intraocularDist, real_t convergenceDist)
+ {
+ if (flipFov)
+ {
+ fovyDegrees = GetFovy(fovyDegrees, (real_t)1.0 / aspect);
+ }
+
+ real_t ymax = zNear * Mathf.Tan(Mathf.DegToRad(fovyDegrees / (real_t)2.0));
+ real_t xmax = ymax * aspect;
+ real_t frustumshift = (intraocularDist / (real_t)2.0) * zNear / convergenceDist;
+ real_t left;
+ real_t right;
+ real_t modeltranslation;
+ switch (eye)
+ {
+ case 1:
+ left = -xmax + frustumshift;
+ right = xmax + frustumshift;
+ modeltranslation = intraocularDist / (real_t)2.0;
+ break;
+ case 2:
+ left = -xmax - frustumshift;
+ right = xmax - frustumshift;
+ modeltranslation = -intraocularDist / (real_t)2.0;
+ break;
+ default:
+ left = -xmax;
+ right = xmax;
+ modeltranslation = (real_t)0.0;
+ break;
+ }
+ Projection proj = CreateFrustum(left, right, -ymax, ymax, zNear, zFar);
+ Projection cm = Projection.Identity;
+ cm.w.x = modeltranslation;
+ return proj * cm;
+ }
+
+ /// <summary>
+ /// Returns a scalar value that is the signed factor by which areas are scaled by this matrix.
+ /// If the sign is negative, the matrix flips the orientation of the area.
+ /// The determinant can be used to calculate the invertibility of a matrix or solve linear systems
+ /// of equations involving the matrix, among other applications.
+ /// </summary>
+ /// <returns>The determinant calculated from this projection.</returns>
+ public readonly real_t Determinant()
+ {
+ return x.w * y.z * z.y * w.x - x.z * y.w * z.y * w.x -
+ x.w * y.y * z.z * w.x + x.y * y.w * z.z * w.x +
+ x.z * y.y * z.w * w.x - x.y * y.z * z.w * w.x -
+ x.w * y.z * z.x * w.y + x.z * y.w * z.x * w.y +
+ x.w * y.x * z.z * w.y - x.x * y.w * z.z * w.y -
+ x.z * y.x * z.w * w.y + x.x * y.z * z.w * w.y +
+ x.w * y.y * z.x * w.z - x.y * y.w * z.x * w.z -
+ x.w * y.x * z.y * w.z + x.x * y.w * z.y * w.z +
+ x.y * y.x * z.w * w.z - x.x * y.y * z.w * w.z -
+ x.z * y.y * z.x * w.w + x.y * y.z * z.x * w.w +
+ x.z * y.x * z.y * w.w - x.x * y.z * z.y * w.w -
+ x.y * y.x * z.z * w.w + x.x * y.y * z.z * w.w;
+ }
+
+ /// <summary>
+ /// Returns the X:Y aspect ratio of this <see cref="Projection"/>'s viewport.
+ /// </summary>
+ /// <returns>The aspect ratio from this projection's viewport.</returns>
+ public readonly real_t GetAspect()
+ {
+ Vector2 vpHe = GetViewportHalfExtents();
+ return vpHe.x / vpHe.y;
+ }
+
+ /// <summary>
+ /// Returns the horizontal field of view of the projection (in degrees).
+ /// </summary>
+ /// <returns>The horizontal field of view of this projection.</returns>
+ public readonly real_t GetFov()
+ {
+ Plane rightPlane = new Plane(x.w - x.x, y.w - y.x, z.w - z.x, -w.w + w.x).Normalized();
+ if (z.x == 0 && z.y == 0)
+ {
+ return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x))) * (real_t)2.0;
+ }
+ else
+ {
+ Plane leftPlane = new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x).Normalized();
+ return Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(leftPlane.Normal.x))) + Mathf.RadToDeg(Mathf.Acos(Mathf.Abs(rightPlane.Normal.x)));
+ }
+ }
+
+ /// <summary>
+ /// Returns the vertical field of view of the projection (in degrees) associated with
+ /// the given horizontal field of view (in degrees) and aspect ratio.
+ /// </summary>
+ /// <param name="fovx">The horizontal field of view (in degrees).</param>
+ /// <param name="aspect">The aspect ratio.</param>
+ /// <returns>The vertical field of view of this projection.</returns>
+ public static real_t GetFovy(real_t fovx, real_t aspect)
+ {
+ return Mathf.RadToDeg(Mathf.Atan(aspect * Mathf.Tan(Mathf.DegToRad(fovx) * (real_t)0.5)) * (real_t)2.0);
+ }
+
+ /// <summary>
+ /// Returns the factor by which the visible level of detail is scaled by this <see cref="Projection"/>.
+ /// </summary>
+ /// <returns>The level of detail factor for this projection.</returns>
+ public readonly real_t GetLodMultiplier()
+ {
+ if (IsOrthogonal())
+ {
+ return GetViewportHalfExtents().x;
+ }
+ else
+ {
+ real_t zn = GetZNear();
+ real_t width = GetViewportHalfExtents().x * (real_t)2.0;
+ return (real_t)1.0 / (zn / width);
+ }
+ }
+
+ /// <summary>
+ /// Returns the number of pixels with the given pixel width displayed per meter, after
+ /// this <see cref="Projection"/> is applied.
+ /// </summary>
+ /// <param name="forPixelWidth">The width for each pixel (in meters).</param>
+ /// <returns>The number of pixels per meter.</returns>
+ public readonly int GetPixelsPerMeter(int forPixelWidth)
+ {
+ Vector3 result = this * new Vector3(1, 0, -1);
+
+ return (int)((result.x * (real_t)0.5 + (real_t)0.5) * forPixelWidth);
+ }
+
+ /// <summary>
+ /// Returns the clipping plane of this <see cref="Projection"/> whose index is given
+ /// by <paramref name="plane"/>.
+ /// <paramref name="plane"/> should be equal to one of <see cref="Planes.Near"/>,
+ /// <see cref="Planes.Far"/>, <see cref="Planes.Left"/>, <see cref="Planes.Top"/>,
+ /// <see cref="Planes.Right"/>, or <see cref="Planes.Bottom"/>.
+ /// </summary>
+ /// <param name="plane">The kind of clipping plane to get from the projection.</param>
+ /// <returns>The clipping plane of this projection.</returns>
+ public readonly Plane GetProjectionPlane(Planes plane)
+ {
+ Plane newPlane = plane switch
+ {
+ Planes.Near => new Plane(x.w + x.z, y.w + y.z, z.w + z.z, w.w + w.z),
+ Planes.Far => new Plane(x.w - x.z, y.w - y.z, z.w - z.z, w.w - w.z),
+ Planes.Left => new Plane(x.w + x.x, y.w + y.x, z.w + z.x, w.w + w.x),
+ Planes.Top => new Plane(x.w - x.y, y.w - y.y, z.w - z.y, w.w - w.y),
+ Planes.Right => new Plane(x.w - x.x, y.w - y.x, z.w - z.x, w.w - w.x),
+ Planes.Bottom => new Plane(x.w + x.y, y.w + y.y, z.w + z.y, w.w + w.y),
+ _ => new Plane(),
+ };
+ newPlane.Normal = -newPlane.Normal;
+ return newPlane.Normalized();
+ }
+
+ /// <summary>
+ /// Returns the dimensions of the far clipping plane of the projection, divided by two.
+ /// </summary>
+ /// <returns>The half extents for this projection's far plane.</returns>
+ public readonly Vector2 GetFarPlaneHalfExtents()
+ {
+ var res = GetProjectionPlane(Planes.Far).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
+ return new Vector2(res.Value.x, res.Value.y);
+ }
+
+ /// <summary>
+ /// Returns the dimensions of the viewport plane that this <see cref="Projection"/>
+ /// projects positions onto, divided by two.
+ /// </summary>
+ /// <returns>The half extents for this projection's viewport plane.</returns>
+ public readonly Vector2 GetViewportHalfExtents()
+ {
+ var res = GetProjectionPlane(Planes.Near).Intersect3(GetProjectionPlane(Planes.Right), GetProjectionPlane(Planes.Top));
+ return new Vector2(res.Value.x, res.Value.y);
+ }
+
+ /// <summary>
+ /// Returns the distance for this <see cref="Projection"/> beyond which positions are clipped.
+ /// </summary>
+ /// <returns>The distance beyond which positions are clipped.</returns>
+ public readonly real_t GetZFar()
+ {
+ return GetProjectionPlane(Planes.Far).D;
+ }
+
+ /// <summary>
+ /// Returns the distance for this <see cref="Projection"/> before which positions are clipped.
+ /// </summary>
+ /// <returns>The distance before which positions are clipped.</returns>
+ public readonly real_t GetZNear()
+ {
+ return -GetProjectionPlane(Planes.Near).D;
+ }
+
+ /// <summary>
+ /// Returns a copy of this <see cref="Projection"/> with the signs of the values of the Y column flipped.
+ /// </summary>
+ /// <returns>The flipped projection.</returns>
+ public readonly Projection FlippedY()
+ {
+ Projection proj = this;
+ proj.y = -proj.y;
+ return proj;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="Projection"/> with the near clipping distance adjusted to be
+ /// <paramref name="newZNear"/>.
+ /// Note: The original <see cref="Projection"/> must be a perspective projection.
+ /// </summary>
+ /// <param name="newZNear">The near clipping distance to adjust the projection to.</param>
+ /// <returns>The adjusted projection.</returns>
+ public readonly Projection PerspectiveZNearAdjusted(real_t newZNear)
+ {
+ Projection proj = this;
+ real_t zFar = GetZFar();
+ real_t zNear = newZNear;
+ real_t deltaZ = zFar - zNear;
+ proj.z.z = -(zFar + zNear) / deltaZ;
+ proj.w.z = -2 * zNear * zFar / deltaZ;
+ return proj;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="Projection"/> with the X and Y values from the given <see cref="Vector2"/>
+ /// added to the first and second values of the final column respectively.
+ /// </summary>
+ /// <param name="offset">The offset to apply to the projection.</param>
+ /// <returns>The offsetted projection.</returns>
+ public readonly Projection JitterOffseted(Vector2 offset)
+ {
+ Projection proj = this;
+ proj.w.x += offset.x;
+ proj.w.y += offset.y;
+ return proj;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="Projection"/> that performs the inverse of this <see cref="Projection"/>'s
+ /// projective transformation.
+ /// </summary>
+ /// <returns>The inverted projection.</returns>
+ public readonly Projection Inverse()
+ {
+ Projection proj = this;
+ int i, j, k;
+ int[] pvt_i = new int[4];
+ int[] pvt_j = new int[4]; /* Locations of pivot matrix */
+ real_t pvt_val; /* Value of current pivot element */
+ real_t hold; /* Temporary storage */
+ real_t determinant = 1.0f;
+ for (k = 0; k < 4; k++)
+ {
+ /* Locate k'th pivot element */
+ pvt_val = proj[k][k]; /* Initialize for search */
+ pvt_i[k] = k;
+ pvt_j[k] = k;
+ for (i = k; i < 4; i++)
+ {
+ for (j = k; j < 4; j++)
+ {
+ if (Mathf.Abs(proj[i][j]) > Mathf.Abs(pvt_val))
+ {
+ pvt_i[k] = i;
+ pvt_j[k] = j;
+ pvt_val = proj[i][j];
+ }
+ }
+ }
+
+ /* Product of pivots, gives determinant when finished */
+ determinant *= pvt_val;
+ if (Mathf.IsZeroApprox(determinant))
+ {
+ return Zero;
+ }
+
+ /* "Interchange" rows (with sign change stuff) */
+ i = pvt_i[k];
+ if (i != k)
+ { /* If rows are different */
+ for (j = 0; j < 4; j++)
+ {
+ hold = -proj[k][j];
+ proj[k, j] = proj[i][j];
+ proj[i, j] = hold;
+ }
+ }
+
+ /* "Interchange" columns */
+ j = pvt_j[k];
+ if (j != k)
+ { /* If columns are different */
+ for (i = 0; i < 4; i++)
+ {
+ hold = -proj[i][k];
+ proj[i, k] = proj[i][j];
+ proj[i, j] = hold;
+ }
+ }
+
+ /* Divide column by minus pivot value */
+ for (i = 0; i < 4; i++)
+ {
+ if (i != k)
+ {
+ proj[i, k] /= (-pvt_val);
+ }
+ }
+
+ /* Reduce the matrix */
+ for (i = 0; i < 4; i++)
+ {
+ hold = proj[i][k];
+ for (j = 0; j < 4; j++)
+ {
+ if (i != k && j != k)
+ {
+ proj[i, j] += hold * proj[k][j];
+ }
+ }
+ }
+
+ /* Divide row by pivot */
+ for (j = 0; j < 4; j++)
+ {
+ if (j != k)
+ {
+ proj[k, j] /= pvt_val;
+ }
+ }
+
+ /* Replace pivot by reciprocal (at last we can touch it). */
+ proj[k, k] = (real_t)1.0 / pvt_val;
+ }
+
+ /* That was most of the work, one final pass of row/column interchange */
+ /* to finish */
+ for (k = 4 - 2; k >= 0; k--)
+ { /* Don't need to work with 1 by 1 corner*/
+ i = pvt_j[k]; /* Rows to swap correspond to pivot COLUMN */
+ if (i != k)
+ { /* If rows are different */
+ for (j = 0; j < 4; j++)
+ {
+ hold = proj[k][j];
+ proj[k, j] = -proj[i][j];
+ proj[i, j] = hold;
+ }
+ }
+
+ j = pvt_i[k]; /* Columns to swap correspond to pivot ROW */
+ if (j != k)
+ { /* If columns are different */
+ for (i = 0; i < 4; i++)
+ {
+ hold = proj[i][k];
+ proj[i, k] = -proj[i][j];
+ proj[i, j] = hold;
+ }
+ }
+ }
+ return proj;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this <see cref="Projection"/> performs an orthogonal projection.
+ /// </summary>
+ /// <returns>If the projection performs an orthogonal projection.</returns>
+ public readonly bool IsOrthogonal()
+ {
+ return w.w == (real_t)1.0;
+ }
+
+ // Constants
+ private static readonly Projection _zero = new Projection(
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0),
+ new Vector4(0, 0, 0, 0)
+ );
+ private static readonly Projection _identity = new Projection(
+ new Vector4(1, 0, 0, 0),
+ new Vector4(0, 1, 0, 0),
+ new Vector4(0, 0, 1, 0),
+ new Vector4(0, 0, 0, 1)
+ );
+
+ /// <summary>
+ /// Zero projection, a projection with all components set to <c>0</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Projection(Vector4.Zero, Vector4.Zero, Vector4.Zero, Vector4.Zero)</c>.</value>
+ public static Projection Zero { get { return _zero; } }
+
+ /// <summary>
+ /// The identity projection, with no distortion applied.
+ /// This is used as a replacement for <c>Projection()</c> in GDScript.
+ /// Do not use <c>new Projection()</c> with no arguments in C#, because it sets all values to zero.
+ /// </summary>
+ /// <value>Equivalent to <c>new Projection(new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1))</c>.</value>
+ public static Projection Identity { get { return _identity; } }
+
+ /// <summary>
+ /// Constructs a projection from 4 vectors (matrix columns).
+ /// </summary>
+ /// <param name="x">The X column, or column index 0.</param>
+ /// <param name="y">The Y column, or column index 1.</param>
+ /// <param name="z">The Z column, or column index 2.</param>
+ /// <param name="w">The W column, or column index 3.</param>
+ public Projection(Vector4 x, Vector4 y, Vector4 z, Vector4 w)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Projection"/> from a <see cref="Transform3D"/>.
+ /// </summary>
+ /// <param name="transform">The <see cref="Transform3D"/>.</param>
+ public Projection(Transform3D transform)
+ {
+ x = new Vector4(transform.basis.Row0.x, transform.basis.Row1.x, transform.basis.Row2.x, 0);
+ y = new Vector4(transform.basis.Row0.y, transform.basis.Row1.y, transform.basis.Row2.y, 0);
+ z = new Vector4(transform.basis.Row0.z, transform.basis.Row1.z, transform.basis.Row2.z, 0);
+ w = new Vector4(transform.origin.x, transform.origin.y, transform.origin.z, 1);
+ }
+
+ /// <summary>
+ /// Composes these two projections by multiplying them
+ /// together. This has the effect of applying the right
+ /// and then the left projection.
+ /// </summary>
+ /// <param name="left">The parent transform.</param>
+ /// <param name="right">The child transform.</param>
+ /// <returns>The composed projection.</returns>
+ public static Projection operator *(Projection left, Projection right)
+ {
+ return new Projection(
+ new Vector4(
+ left.x.x * right.x.x + left.y.x * right.x.y + left.z.x * right.x.z + left.w.x * right.x.w,
+ left.x.y * right.x.x + left.y.y * right.x.y + left.z.y * right.x.z + left.w.y * right.x.w,
+ left.x.z * right.x.x + left.y.z * right.x.y + left.z.z * right.x.z + left.w.z * right.x.w,
+ left.x.w * right.x.x + left.y.w * right.x.y + left.z.w * right.x.z + left.w.w * right.x.w
+ ), new Vector4(
+ left.x.x * right.y.x + left.y.x * right.y.y + left.z.x * right.y.z + left.w.x * right.y.w,
+ left.x.y * right.y.x + left.y.y * right.y.y + left.z.y * right.y.z + left.w.y * right.y.w,
+ left.x.z * right.y.x + left.y.z * right.y.y + left.z.z * right.y.z + left.w.z * right.y.w,
+ left.x.w * right.y.x + left.y.w * right.y.y + left.z.w * right.y.z + left.w.w * right.y.w
+ ), new Vector4(
+ left.x.x * right.z.x + left.y.x * right.z.y + left.z.x * right.z.z + left.w.x * right.z.w,
+ left.x.y * right.z.x + left.y.y * right.z.y + left.z.y * right.z.z + left.w.y * right.z.w,
+ left.x.z * right.z.x + left.y.z * right.z.y + left.z.z * right.z.z + left.w.z * right.z.w,
+ left.x.w * right.z.x + left.y.w * right.z.y + left.z.w * right.z.z + left.w.w * right.z.w
+ ), new Vector4(
+ left.x.x * right.w.x + left.y.x * right.w.y + left.z.x * right.w.z + left.w.x * right.w.w,
+ left.x.y * right.w.x + left.y.y * right.w.y + left.z.y * right.w.z + left.w.y * right.w.w,
+ left.x.z * right.w.x + left.y.z * right.w.y + left.z.z * right.w.z + left.w.z * right.w.w,
+ left.x.w * right.w.x + left.y.w * right.w.y + left.z.w * right.w.z + left.w.w * right.w.w
+ )
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector4 transformed (multiplied) by the projection.
+ /// </summary>
+ /// <param name="proj">The projection to apply.</param>
+ /// <param name="vector">A Vector4 to transform.</param>
+ /// <returns>The transformed Vector4.</returns>
+ public static Vector4 operator *(Projection proj, Vector4 vector)
+ {
+ return new Vector4(
+ proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x * vector.w,
+ proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y * vector.w,
+ proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z * vector.w,
+ proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w * vector.w
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector4 transformed (multiplied) by the inverse projection.
+ /// </summary>
+ /// <param name="proj">The projection to apply.</param>
+ /// <param name="vector">A Vector4 to transform.</param>
+ /// <returns>The inversely transformed Vector4.</returns>
+ public static Vector4 operator *(Vector4 vector, Projection proj)
+ {
+ return new Vector4(
+ proj.x.x * vector.x + proj.x.y * vector.y + proj.x.z * vector.z + proj.x.w * vector.w,
+ proj.y.x * vector.x + proj.y.y * vector.y + proj.y.z * vector.z + proj.y.w * vector.w,
+ proj.z.x * vector.x + proj.z.y * vector.y + proj.z.z * vector.z + proj.z.w * vector.w,
+ proj.w.x * vector.x + proj.w.y * vector.y + proj.w.z * vector.z + proj.w.w * vector.w
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the projection.
+ /// </summary>
+ /// <param name="proj">The projection to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Projection proj, Vector3 vector)
+ {
+ Vector3 ret = new Vector3(
+ proj.x.x * vector.x + proj.y.x * vector.y + proj.z.x * vector.z + proj.w.x,
+ proj.x.y * vector.x + proj.y.y * vector.y + proj.z.y * vector.z + proj.w.y,
+ proj.x.z * vector.x + proj.y.z * vector.y + proj.z.z * vector.z + proj.w.z
+ );
+ return ret / (proj.x.w * vector.x + proj.y.w * vector.y + proj.z.w * vector.z + proj.w.w);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the projections are exactly equal.
+ /// </summary>
+ /// <param name="left">The left projection.</param>
+ /// <param name="right">The right projection.</param>
+ /// <returns>Whether or not the projections are exactly equal.</returns>
+ public static bool operator ==(Projection left, Projection right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the projections are not exactly equal.
+ /// </summary>
+ /// <param name="left">The left projection.</param>
+ /// <param name="right">The right projection.</param>
+ /// <returns>Whether or not the projections are not exactly equal.</returns>
+ public static bool operator !=(Projection left, Projection right)
+ {
+ return !left.Equals(right);
+ }
+
+ /// <summary>
+ /// Constructs a new <see cref="Transform3D"/> from the <see cref="Projection"/>.
+ /// </summary>
+ /// <param name="proj">The <see cref="Projection"/>.</param>
+ public static explicit operator Transform3D(Projection proj)
+ {
+ return new Transform3D(
+ new Basis(
+ new Vector3(proj.x.x, proj.x.y, proj.x.z),
+ new Vector3(proj.y.x, proj.y.y, proj.y.z),
+ new Vector3(proj.z.x, proj.z.y, proj.z.z)
+ ),
+ new Vector3(proj.w.x, proj.w.y, proj.w.z)
+ );
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the projection is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
+ public override readonly bool Equals(object obj)
+ {
+ return obj is Projection other && Equals(other);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the projections are exactly equal.
+ /// </summary>
+ /// <param name="other">The other projection.</param>
+ /// <returns>Whether or not the projections are exactly equal.</returns>
+ public readonly bool Equals(Projection other)
+ {
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+ }
+
+ /// <summary>
+ /// Serves as the hash function for <see cref="Projection"/>.
+ /// </summary>
+ /// <returns>A hash code for this projection.</returns>
+ public override readonly int GetHashCode()
+ {
+ return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Projection"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this projection.</returns>
+ public override readonly string ToString()
+ {
+ return $"{x.x}, {x.y}, {x.z}, {x.w}\n{y.x}, {y.y}, {y.z}, {y.w}\n{z.x}, {z.y}, {z.z}, {z.w}\n{w.x}, {w.y}, {w.z}, {w.w}\n";
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Projection"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this projection.</returns>
+ public readonly string ToString(string format)
+ {
+ return $"{x.x.ToString(format)}, {x.y.ToString(format)}, {x.z.ToString(format)}, {x.w.ToString(format)}\n" +
+ $"{y.x.ToString(format)}, {y.y.ToString(format)}, {y.z.ToString(format)}, {y.w.ToString(format)}\n" +
+ $"{z.x.ToString(format)}, {z.y.ToString(format)}, {z.z.ToString(format)}, {z.w.ToString(format)}\n" +
+ $"{w.x.ToString(format)}, {w.y.ToString(format)}, {w.z.ToString(format)}, {w.w.ToString(format)}\n";
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index e38dca414f..8e4f9178f7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -52,6 +47,9 @@ namespace Godot
/// <summary>
/// Access quaternion components using their index.
/// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0, 1, 2 or 3.
+ /// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
/// <c>[1]</c> is equivalent to <see cref="y"/>,
@@ -60,7 +58,7 @@ namespace Godot
/// </value>
public real_t this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -73,7 +71,7 @@ namespace Godot
case 3:
return w;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -93,33 +91,12 @@ namespace Godot
w = value;
break;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
/// <summary>
- /// Returns the length (magnitude) of the quaternion.
- /// </summary>
- /// <seealso cref="LengthSquared"/>
- /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
- public real_t Length
- {
- get { return Mathf.Sqrt(LengthSquared); }
- }
-
- /// <summary>
- /// Returns the squared length (squared magnitude) of the quaternion.
- /// This method runs faster than <see cref="Length"/>, so prefer it if
- /// you need to compare quaternions or need the squared length for some formula.
- /// </summary>
- /// <value>Equivalent to <c>Dot(this)</c>.</value>
- public real_t LengthSquared
- {
- get { return Dot(this); }
- }
-
- /// <summary>
/// Returns the angle between this quaternion and <paramref name="to"/>.
/// This is the magnitude of the angle you would need to rotate
/// by to get from one to the other.
@@ -130,27 +107,143 @@ namespace Godot
/// </summary>
/// <param name="to">The other quaternion.</param>
/// <returns>The angle between the quaternions.</returns>
- public real_t AngleTo(Quaternion to)
+ public readonly real_t AngleTo(Quaternion to)
{
real_t dot = Dot(to);
return Mathf.Acos(Mathf.Clamp(dot * dot * 2 - 1, -1, 1));
}
/// <summary>
- /// Performs a cubic spherical interpolation between quaternions <paramref name="preA"/>, this quaternion,
+ /// Performs a spherical cubic interpolation between quaternions <paramref name="preA"/>, this quaternion,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// </summary>
+ /// <param name="b">The destination quaternion.</param>
+ /// <param name="preA">A quaternion before this quaternion.</param>
+ /// <param name="postB">A quaternion after <paramref name="b"/>.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated quaternion.</returns>
+ public readonly Quaternion SphericalCubicInterpolate(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
+ {
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+ if (!b.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(b));
+ }
+#endif
+
+ // Align flip phases.
+ Quaternion fromQ = new Basis(this).GetRotationQuaternion();
+ Quaternion preQ = new Basis(preA).GetRotationQuaternion();
+ Quaternion toQ = new Basis(b).GetRotationQuaternion();
+ Quaternion postQ = new Basis(postB).GetRotationQuaternion();
+
+ // Flip quaternions to shortest path if necessary.
+ bool flip1 = Math.Sign(fromQ.Dot(preQ)) < 0;
+ preQ = flip1 ? -preQ : preQ;
+ bool flip2 = Math.Sign(fromQ.Dot(toQ)) < 0;
+ toQ = flip2 ? -toQ : toQ;
+ bool flip3 = flip2 ? toQ.Dot(postQ) <= 0 : Math.Sign(toQ.Dot(postQ)) < 0;
+ postQ = flip3 ? -postQ : postQ;
+
+ // Calc by Expmap in fromQ space.
+ Quaternion lnFrom = new Quaternion(0, 0, 0, 0);
+ Quaternion lnTo = (fromQ.Inverse() * toQ).Log();
+ Quaternion lnPre = (fromQ.Inverse() * preQ).Log();
+ Quaternion lnPost = (fromQ.Inverse() * postQ).Log();
+ Quaternion ln = new Quaternion(
+ Mathf.CubicInterpolate(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight),
+ Mathf.CubicInterpolate(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight),
+ Mathf.CubicInterpolate(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight),
+ 0);
+ Quaternion q1 = fromQ * ln.Exp();
+
+ // Calc by Expmap in toQ space.
+ lnFrom = (toQ.Inverse() * fromQ).Log();
+ lnTo = new Quaternion(0, 0, 0, 0);
+ lnPre = (toQ.Inverse() * preQ).Log();
+ lnPost = (toQ.Inverse() * postQ).Log();
+ ln = new Quaternion(
+ Mathf.CubicInterpolate(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight),
+ Mathf.CubicInterpolate(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight),
+ Mathf.CubicInterpolate(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight),
+ 0);
+ Quaternion q2 = toQ * ln.Exp();
+
+ // To cancel error made by Expmap ambiguity, do blending.
+ return q1.Slerp(q2, weight);
+ }
+
+ /// <summary>
+ /// Performs a spherical cubic interpolation between quaternions <paramref name="preA"/>, this quaternion,
/// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// It can perform smoother interpolation than <see cref="SphericalCubicInterpolate"/>
+ /// by the time values.
/// </summary>
/// <param name="b">The destination quaternion.</param>
/// <param name="preA">A quaternion before this quaternion.</param>
/// <param name="postB">A quaternion after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="bT"></param>
+ /// <param name="preAT"></param>
+ /// <param name="postBT"></param>
/// <returns>The interpolated quaternion.</returns>
- public Quaternion CubicSlerp(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
+ public readonly Quaternion SphericalCubicInterpolateInTime(Quaternion b, Quaternion preA, Quaternion postB, real_t weight, real_t bT, real_t preAT, real_t postBT)
{
- real_t t2 = (1.0f - weight) * weight * 2f;
- Quaternion sp = Slerp(b, weight);
- Quaternion sq = preA.Slerpni(postB, weight);
- return sp.Slerpni(sq, t2);
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+ if (!b.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(b));
+ }
+#endif
+
+ // Align flip phases.
+ Quaternion fromQ = new Basis(this).GetRotationQuaternion();
+ Quaternion preQ = new Basis(preA).GetRotationQuaternion();
+ Quaternion toQ = new Basis(b).GetRotationQuaternion();
+ Quaternion postQ = new Basis(postB).GetRotationQuaternion();
+
+ // Flip quaternions to shortest path if necessary.
+ bool flip1 = Math.Sign(fromQ.Dot(preQ)) < 0;
+ preQ = flip1 ? -preQ : preQ;
+ bool flip2 = Math.Sign(fromQ.Dot(toQ)) < 0;
+ toQ = flip2 ? -toQ : toQ;
+ bool flip3 = flip2 ? toQ.Dot(postQ) <= 0 : Math.Sign(toQ.Dot(postQ)) < 0;
+ postQ = flip3 ? -postQ : postQ;
+
+ // Calc by Expmap in fromQ space.
+ Quaternion lnFrom = new Quaternion(0, 0, 0, 0);
+ Quaternion lnTo = (fromQ.Inverse() * toQ).Log();
+ Quaternion lnPre = (fromQ.Inverse() * preQ).Log();
+ Quaternion lnPost = (fromQ.Inverse() * postQ).Log();
+ Quaternion ln = new Quaternion(
+ Mathf.CubicInterpolateInTime(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight, bT, preAT, postBT),
+ Mathf.CubicInterpolateInTime(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight, bT, preAT, postBT),
+ Mathf.CubicInterpolateInTime(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight, bT, preAT, postBT),
+ 0);
+ Quaternion q1 = fromQ * ln.Exp();
+
+ // Calc by Expmap in toQ space.
+ lnFrom = (toQ.Inverse() * fromQ).Log();
+ lnTo = new Quaternion(0, 0, 0, 0);
+ lnPre = (toQ.Inverse() * preQ).Log();
+ lnPost = (toQ.Inverse() * postQ).Log();
+ ln = new Quaternion(
+ Mathf.CubicInterpolateInTime(lnFrom.x, lnTo.x, lnPre.x, lnPost.x, weight, bT, preAT, postBT),
+ Mathf.CubicInterpolateInTime(lnFrom.y, lnTo.y, lnPre.y, lnPost.y, weight, bT, preAT, postBT),
+ Mathf.CubicInterpolateInTime(lnFrom.z, lnTo.z, lnPre.z, lnPost.z, weight, bT, preAT, postBT),
+ 0);
+ Quaternion q2 = toQ * ln.Exp();
+
+ // To cancel error made by Expmap ambiguity, do blending.
+ return q1.Slerp(q2, weight);
}
/// <summary>
@@ -158,11 +251,39 @@ namespace Godot
/// </summary>
/// <param name="b">The other quaternion.</param>
/// <returns>The dot product.</returns>
- public real_t Dot(Quaternion b)
+ public readonly real_t Dot(Quaternion b)
{
return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w);
}
+ public readonly Quaternion Exp()
+ {
+ Vector3 v = new Vector3(x, y, z);
+ real_t theta = v.Length();
+ v = v.Normalized();
+ if (theta < Mathf.Epsilon || !v.IsNormalized())
+ {
+ return new Quaternion(0, 0, 0, 1);
+ }
+ return new Quaternion(v, theta);
+ }
+
+ public readonly real_t GetAngle()
+ {
+ return 2 * Mathf.Acos(w);
+ }
+
+ public readonly Vector3 GetAxis()
+ {
+ if (Mathf.Abs(w) > 1 - Mathf.Epsilon)
+ {
+ return new Vector3(x, y, z);
+ }
+
+ real_t r = 1 / Mathf.Sqrt(1 - w * w);
+ return new Vector3(x * r, y * r, z * r);
+ }
+
/// <summary>
/// Returns Euler angles (in the YXZ convention: when decomposing,
/// first Z, then X, and Y last) corresponding to the rotation
@@ -170,49 +291,86 @@ namespace Godot
/// the rotation angles in the format (X angle, Y angle, Z angle).
/// </summary>
/// <returns>The Euler angle representation of this quaternion.</returns>
- public Vector3 GetEuler()
+ public readonly Vector3 GetEuler(EulerOrder order = EulerOrder.Yxz)
{
#if DEBUG
if (!IsNormalized())
{
- throw new InvalidOperationException("Quaternion is not normalized");
+ throw new InvalidOperationException("Quaternion is not normalized.");
}
#endif
var basis = new Basis(this);
- return basis.GetEuler();
+ return basis.GetEuler(order);
}
/// <summary>
/// Returns the inverse of the quaternion.
/// </summary>
/// <returns>The inverse quaternion.</returns>
- public Quaternion Inverse()
+ public readonly Quaternion Inverse()
{
#if DEBUG
if (!IsNormalized())
{
- throw new InvalidOperationException("Quaternion is not normalized");
+ throw new InvalidOperationException("Quaternion is not normalized.");
}
#endif
return new Quaternion(-x, -y, -z, w);
}
/// <summary>
+ /// Returns <see langword="true"/> if this quaternion is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
/// Returns whether the quaternion is normalized or not.
/// </summary>
/// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
- public bool IsNormalized()
+ public readonly bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon;
+ }
+
+ public readonly Quaternion Log()
+ {
+ Vector3 v = GetAxis() * GetAngle();
+ return new Quaternion(v.x, v.y, v.z, 0);
+ }
+
+ /// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
+ public readonly real_t Length()
{
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ return Mathf.Sqrt(LengthSquared());
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of the quaternion.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare quaternions or need the squared length for some formula.
+ /// </summary>
+ /// <value>Equivalent to <c>Dot(this)</c>.</value>
+ public readonly real_t LengthSquared()
+ {
+ return Dot(this);
}
/// <summary>
/// Returns a copy of the quaternion, normalized to unit length.
/// </summary>
/// <returns>The normalized quaternion.</returns>
- public Quaternion Normalized()
+ public readonly Quaternion Normalized()
{
- return this / Length;
+ return this / Length();
}
/// <summary>
@@ -224,21 +382,21 @@ namespace Godot
/// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting quaternion of the interpolation.</returns>
- public Quaternion Slerp(Quaternion to, real_t weight)
+ public readonly Quaternion Slerp(Quaternion to, real_t weight)
{
#if DEBUG
if (!IsNormalized())
{
- throw new InvalidOperationException("Quaternion is not normalized");
+ throw new InvalidOperationException("Quaternion is not normalized.");
}
if (!to.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(to));
+ throw new ArgumentException("Argument is not normalized.", nameof(to));
}
#endif
// Calculate cosine.
- real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
+ real_t cosom = Dot(to);
var to1 = new Quaternion();
@@ -246,17 +404,11 @@ namespace Godot
if (cosom < 0.0)
{
cosom = -cosom;
- to1.x = -to.x;
- to1.y = -to.y;
- to1.z = -to.z;
- to1.w = -to.w;
+ to1 = -to;
}
else
{
- to1.x = to.x;
- to1.y = to.y;
- to1.z = to.z;
- to1.w = to.w;
+ to1 = to;
}
real_t sinom, scale0, scale1;
@@ -295,8 +447,19 @@ namespace Godot
/// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting quaternion of the interpolation.</returns>
- public Quaternion Slerpni(Quaternion to, real_t weight)
+ public readonly Quaternion Slerpni(Quaternion to, real_t weight)
{
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(to));
+ }
+#endif
+
real_t dot = Dot(to);
if (Mathf.Abs(dot) > 0.9999f)
@@ -318,24 +481,6 @@ namespace Godot
);
}
- /// <summary>
- /// Returns a vector transformed (multiplied) by this quaternion.
- /// </summary>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quaternion is not normalized");
- }
-#endif
- var u = new Vector3(x, y, z);
- Vector3 uv = u.Cross(v);
- return v + (((uv * w) + u.Cross(uv)) * 2);
- }
-
// Constants
private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
@@ -363,15 +508,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="q">The existing quaternion.</param>
- public Quaternion(Quaternion q)
- {
- this = q;
- }
-
- /// <summary>
/// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
/// </summary>
/// <param name="basis">The <see cref="Basis"/> to construct from.</param>
@@ -381,35 +517,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by
- /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last),
- /// given in the vector format as (X angle, Y angle, Z angle).
- /// </summary>
- /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
- public Quaternion(Vector3 eulerYXZ)
- {
- real_t halfA1 = eulerYXZ.y * 0.5f;
- real_t halfA2 = eulerYXZ.x * 0.5f;
- real_t halfA3 = eulerYXZ.z * 0.5f;
-
- // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
- // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
- // a3 is the angle of the first rotation, following the notation in this reference.
-
- real_t cosA1 = Mathf.Cos(halfA1);
- real_t sinA1 = Mathf.Sin(halfA1);
- real_t cosA2 = Mathf.Cos(halfA2);
- real_t sinA2 = Mathf.Sin(halfA2);
- real_t cosA3 = Mathf.Cos(halfA3);
- real_t sinA3 = Mathf.Sin(halfA3);
-
- x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3);
- y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3);
- z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3);
- w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3);
- }
-
- /// <summary>
/// Constructs a <see cref="Quaternion"/> that will rotate around the given axis
/// by the specified angle. The axis must be a normalized vector.
/// </summary>
@@ -420,7 +527,7 @@ namespace Godot
#if DEBUG
if (!axis.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(axis));
+ throw new ArgumentException("Argument is not normalized.", nameof(axis));
}
#endif
@@ -435,17 +542,68 @@ namespace Godot
}
else
{
- real_t sinAngle = Mathf.Sin(angle * 0.5f);
- real_t cosAngle = Mathf.Cos(angle * 0.5f);
- real_t s = sinAngle / d;
+ (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f);
+ real_t s = sin / d;
x = axis.x * s;
y = axis.y * s;
z = axis.z * s;
- w = cosAngle;
+ w = cos;
}
}
+ public Quaternion(Vector3 arcFrom, Vector3 arcTo)
+ {
+ Vector3 c = arcFrom.Cross(arcTo);
+ real_t d = arcFrom.Dot(arcTo);
+
+ if (d < -1.0f + Mathf.Epsilon)
+ {
+ x = 0f;
+ y = 1f;
+ z = 0f;
+ w = 0f;
+ }
+ else
+ {
+ real_t s = Mathf.Sqrt((1.0f + d) * 2.0f);
+ real_t rs = 1.0f / s;
+
+ x = c.x * rs;
+ y = c.y * rs;
+ z = c.z * rs;
+ w = s * 0.5f;
+ }
+ }
+
+ /// <summary>
+ /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by
+ /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last),
+ /// given in the vector format as (X angle, Y angle, Z angle).
+ /// </summary>
+ /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
+ public static Quaternion FromEuler(Vector3 eulerYXZ)
+ {
+ real_t halfA1 = eulerYXZ.y * 0.5f;
+ real_t halfA2 = eulerYXZ.x * 0.5f;
+ real_t halfA3 = eulerYXZ.z * 0.5f;
+
+ // R = Y(a1).X(a2).Z(a3) convention for Euler angles.
+ // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
+ // a3 is the angle of the first rotation, following the notation in this reference.
+
+ (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1);
+ (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2);
+ (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3);
+
+ return new Quaternion(
+ (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3),
+ (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3),
+ (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3),
+ (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3)
+ );
+ }
+
/// <summary>
/// Composes these two quaternions by multiplying them together.
/// This has the effect of rotating the second quaternion
@@ -466,6 +624,36 @@ namespace Godot
}
/// <summary>
+ /// Returns a Vector3 rotated (multiplied) by the quaternion.
+ /// </summary>
+ /// <param name="quaternion">The quaternion to rotate by.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The rotated Vector3.</returns>
+ public static Vector3 operator *(Quaternion quaternion, Vector3 vector)
+ {
+#if DEBUG
+ if (!quaternion.IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized.");
+ }
+#endif
+ var u = new Vector3(quaternion.x, quaternion.y, quaternion.z);
+ Vector3 uv = u.Cross(vector);
+ return vector + (((uv * quaternion.w) + u.Cross(uv)) * 2);
+ }
+
+ /// <summary>
+ /// Returns a Vector3 rotated (multiplied) by the inverse quaternion.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely rotate.</param>
+ /// <param name="quaternion">The quaternion to rotate by.</param>
+ /// <returns>The inversely rotated Vector3.</returns>
+ public static Vector3 operator *(Vector3 vector, Quaternion quaternion)
+ {
+ return quaternion.Inverse() * vector;
+ }
+
+ /// <summary>
/// Adds each component of the left <see cref="Quaternion"/>
/// to the right <see cref="Quaternion"/>. This operation is not
/// meaningful on its own, but it can be used as a part of a
@@ -508,38 +696,6 @@ namespace Godot
}
/// <summary>
- /// Rotates (multiplies) the <see cref="Vector3"/>
- /// by the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="quat">The quaternion to rotate by.</param>
- /// <param name="vec">The vector to rotate.</param>
- /// <returns>The rotated vector.</returns>
- public static Vector3 operator *(Quaternion quat, Vector3 vec)
- {
-#if DEBUG
- if (!quat.IsNormalized())
- {
- throw new InvalidOperationException("Quaternion is not normalized.");
- }
-#endif
- var u = new Vector3(quat.x, quat.y, quat.z);
- Vector3 uv = u.Cross(vec);
- return vec + (((uv * quat.w) + u.Cross(uv)) * 2);
- }
-
- /// <summary>
- /// Inversely rotates (multiplies) the <see cref="Vector3"/>
- /// by the given <see cref="Quaternion"/>.
- /// </summary>
- /// <param name="vec">The vector to rotate.</param>
- /// <param name="quat">The quaternion to rotate by.</param>
- /// <returns>The inversely rotated vector.</returns>
- public static Vector3 operator *(Vector3 vec, Quaternion quat)
- {
- return quat.Inverse() * vec;
- }
-
- /// <summary>
/// Multiplies each component of the <see cref="Quaternion"/>
/// by the given <see cref="real_t"/>. This operation is not
/// meaningful on its own, but it can be used as a part of a
@@ -612,14 +768,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The other object to compare.</param>
/// <returns>Whether or not the quaternion and the other object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Quaternion)
- {
- return Equals((Quaternion)obj);
- }
-
- return false;
+ return obj is Quaternion other && Equals(other);
}
/// <summary>
@@ -627,7 +778,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other quaternion to compare.</param>
/// <returns>Whether or not the quaternions are exactly equal.</returns>
- public bool Equals(Quaternion other)
+ public readonly bool Equals(Quaternion other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
@@ -638,7 +789,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other quaternion to compare.</param>
/// <returns>Whether or not the quaternions are approximately equal.</returns>
- public bool IsEqualApprox(Quaternion other)
+ public readonly bool IsEqualApprox(Quaternion other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
}
@@ -647,7 +798,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Quaternion"/>.
/// </summary>
/// <returns>A hash code for this quaternion.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
@@ -656,7 +807,7 @@ namespace Godot
/// Converts this <see cref="Quaternion"/> to a string.
/// </summary>
/// <returns>A string representation of this quaternion.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({x}, {y}, {z}, {w})";
}
@@ -665,7 +816,7 @@ namespace Godot
/// Converts this <see cref="Quaternion"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this quaternion.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
index 1588869ec0..59b9faf16c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
@@ -1,5 +1,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -9,99 +11,32 @@ namespace Godot
/// resource by themselves. They are used by and with the low-level Server
/// classes such as <see cref="RenderingServer"/>.
/// </summary>
- public sealed partial class RID : IDisposable
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly struct RID
{
- private bool _disposed = false;
+ private readonly ulong _id; // Default is 0
- internal IntPtr ptr;
-
- internal static IntPtr GetPtr(RID instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
-
- if (instance._disposed)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
-
- ~RID()
- {
- Dispose(false);
- }
-
- /// <summary>
- /// Disposes of this <see cref="RID"/>.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- private void Dispose(bool disposing)
- {
- if (_disposed)
- return;
-
- if (ptr != IntPtr.Zero)
- {
- godot_icall_RID_Dtor(ptr);
- ptr = IntPtr.Zero;
- }
-
- _disposed = true;
- }
-
- internal RID(IntPtr ptr)
+ internal RID(ulong id)
{
- this.ptr = ptr;
- }
-
- /// <summary>
- /// The pointer to the native instance of this <see cref="RID"/>.
- /// </summary>
- public IntPtr NativeInstance
- {
- get { return ptr; }
- }
-
- internal RID()
- {
- this.ptr = IntPtr.Zero;
+ _id = id;
}
/// <summary>
/// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>.
/// </summary>
public RID(Object from)
- {
- this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from));
- }
+ => _id = from is Resource res ? res.GetRid()._id : default;
/// <summary>
/// Returns the ID of the referenced resource.
/// </summary>
/// <returns>The ID of the referenced resource.</returns>
- public int GetId()
- {
- return godot_icall_RID_get_id(GetPtr(this));
- }
+ public ulong Id => _id;
/// <summary>
/// Converts this <see cref="RID"/> to a string.
/// </summary>
/// <returns>A string representation of this RID.</returns>
- public override string ToString() => "[RID]";
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_RID_Dtor(IntPtr ptr);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_RID_get_id(IntPtr ptr);
+ public override string ToString() => $"RID({Id})";
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index ec16920fed..1a8696d3bc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -25,7 +20,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector2 Position
{
- get { return _position; }
+ readonly get { return _position; }
set { _position = value; }
}
@@ -36,7 +31,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector2 Size
{
- get { return _size; }
+ readonly get { return _size; }
set { _size = value; }
}
@@ -50,7 +45,7 @@ namespace Godot
/// </value>
public Vector2 End
{
- get { return _position + _size; }
+ readonly get { return _position + _size; }
set { _size = value - _position; }
}
@@ -58,7 +53,7 @@ namespace Godot
/// The area of this <see cref="Rect2"/>.
/// </summary>
/// <value>Equivalent to <see cref="GetArea()"/>.</value>
- public real_t Area
+ public readonly real_t Area
{
get { return GetArea(); }
}
@@ -68,7 +63,7 @@ namespace Godot
/// the top-left corner is the origin and width and height are positive.
/// </summary>
/// <returns>The modified <see cref="Rect2"/>.</returns>
- public Rect2 Abs()
+ public readonly Rect2 Abs()
{
Vector2 end = End;
Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y));
@@ -84,7 +79,7 @@ namespace Godot
/// The intersection of this <see cref="Rect2"/> and <paramref name="b"/>,
/// or an empty <see cref="Rect2"/> if they do not intersect.
/// </returns>
- public Rect2 Intersection(Rect2 b)
+ public readonly Rect2 Intersection(Rect2 b)
{
Rect2 newRect = b;
@@ -106,13 +101,23 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public bool IsFinite()
+ {
+ return _position.IsFinite() && _size.IsFinite();
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one.
/// </summary>
/// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param>
/// <returns>
/// A <see langword="bool"/> for whether or not this <see cref="Rect2"/> encloses <paramref name="b"/>.
/// </returns>
- public bool Encloses(Rect2 b)
+ public readonly bool Encloses(Rect2 b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
b._position.x + b._size.x < _position.x + _size.x &&
@@ -124,7 +129,7 @@ namespace Godot
/// </summary>
/// <param name="to">The point to include.</param>
/// <returns>The expanded <see cref="Rect2"/>.</returns>
- public Rect2 Expand(Vector2 to)
+ public readonly Rect2 Expand(Vector2 to)
{
Rect2 expanded = this;
@@ -159,7 +164,7 @@ namespace Godot
/// Returns the area of the <see cref="Rect2"/>.
/// </summary>
/// <returns>The area.</returns>
- public real_t GetArea()
+ public readonly real_t GetArea()
{
return _size.x * _size.y;
}
@@ -169,7 +174,7 @@ namespace Godot
/// to <see cref="Position"/> + (<see cref="Size"/> / 2).
/// </summary>
/// <returns>The center.</returns>
- public Vector2 GetCenter()
+ public readonly Vector2 GetCenter()
{
return _position + (_size * 0.5f);
}
@@ -182,7 +187,7 @@ namespace Godot
/// <seealso cref="GrowSide(Side, real_t)"/>
/// <param name="by">The amount to grow by.</param>
/// <returns>The grown <see cref="Rect2"/>.</returns>
- public Rect2 Grow(real_t by)
+ public readonly Rect2 Grow(real_t by)
{
Rect2 g = this;
@@ -205,7 +210,7 @@ namespace Godot
/// <param name="right">The amount to grow by on the right side.</param>
/// <param name="bottom">The amount to grow by on the bottom side.</param>
/// <returns>The grown <see cref="Rect2"/>.</returns>
- public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
+ public readonly Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
Rect2 g = this;
@@ -226,7 +231,7 @@ namespace Godot
/// <param name="side">The side to grow.</param>
/// <param name="by">The amount to grow by.</param>
/// <returns>The grown <see cref="Rect2"/>.</returns>
- public Rect2 GrowSide(Side side, real_t by)
+ public readonly Rect2 GrowSide(Side side, real_t by)
{
Rect2 g = this;
@@ -239,15 +244,17 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if the <see cref="Rect2"/> is flat or empty,
- /// or <see langword="false"/> otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2"/> has
+ /// area, and <see langword="false"/> if the <see cref="Rect2"/>
+ /// is linear, empty, or has a negative <see cref="Size"/>.
+ /// See also <see cref="GetArea"/>.
/// </summary>
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area.
/// </returns>
- public bool HasNoArea()
+ public readonly bool HasArea()
{
- return _size.x <= 0 || _size.y <= 0;
+ return _size.x > 0.0f && _size.y > 0.0f;
}
/// <summary>
@@ -258,7 +265,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> contains <paramref name="point"/>.
/// </returns>
- public bool HasPoint(Vector2 point)
+ public readonly bool HasPoint(Vector2 point)
{
if (point.x < _position.x)
return false;
@@ -284,7 +291,7 @@ namespace Godot
/// <param name="b">The other <see cref="Rect2"/> to check for intersections with.</param>
/// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
- public bool Intersects(Rect2 b, bool includeBorders = false)
+ public readonly bool Intersects(Rect2 b, bool includeBorders = false)
{
if (includeBorders)
{
@@ -333,7 +340,7 @@ namespace Godot
/// </summary>
/// <param name="b">The other <see cref="Rect2"/>.</param>
/// <returns>The merged <see cref="Rect2"/>.</returns>
- public Rect2 Merge(Rect2 b)
+ public readonly Rect2 Merge(Rect2 b)
{
Rect2 newRect;
@@ -429,14 +436,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The other object to compare.</param>
/// <returns>Whether or not the rect and the other object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Rect2)
- {
- return Equals((Rect2)obj);
- }
-
- return false;
+ return obj is Rect2 other && Equals(other);
}
/// <summary>
@@ -444,7 +446,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other rect to compare.</param>
/// <returns>Whether or not the rects are exactly equal.</returns>
- public bool Equals(Rect2 other)
+ public readonly bool Equals(Rect2 other)
{
return _position.Equals(other._position) && _size.Equals(other._size);
}
@@ -455,7 +457,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other rect to compare.</param>
/// <returns>Whether or not the rects are approximately equal.</returns>
- public bool IsEqualApprox(Rect2 other)
+ public readonly bool IsEqualApprox(Rect2 other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
}
@@ -464,7 +466,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Rect2"/>.
/// </summary>
/// <returns>A hash code for this rect.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
@@ -473,7 +475,7 @@ namespace Godot
/// Converts this <see cref="Rect2"/> to a string.
/// </summary>
/// <returns>A string representation of this rect.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"{_position}, {_size}";
}
@@ -482,7 +484,7 @@ namespace Godot
/// Converts this <see cref="Rect2"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this rect.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index 5d53b8330e..cf8939a859 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -20,7 +20,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector2i Position
{
- get { return _position; }
+ readonly get { return _position; }
set { _position = value; }
}
@@ -31,7 +31,7 @@ namespace Godot
/// <value>Directly uses a private field.</value>
public Vector2i Size
{
- get { return _size; }
+ readonly get { return _size; }
set { _size = value; }
}
@@ -45,7 +45,7 @@ namespace Godot
/// </value>
public Vector2i End
{
- get { return _position + _size; }
+ readonly get { return _position + _size; }
set { _size = value - _position; }
}
@@ -53,7 +53,7 @@ namespace Godot
/// The area of this <see cref="Rect2i"/>.
/// </summary>
/// <value>Equivalent to <see cref="GetArea()"/>.</value>
- public int Area
+ public readonly int Area
{
get { return GetArea(); }
}
@@ -63,7 +63,7 @@ namespace Godot
/// the top-left corner is the origin and width and height are positive.
/// </summary>
/// <returns>The modified <see cref="Rect2i"/>.</returns>
- public Rect2i Abs()
+ public readonly Rect2i Abs()
{
Vector2i end = End;
Vector2i topLeft = new Vector2i(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y));
@@ -79,7 +79,7 @@ namespace Godot
/// The intersection of this <see cref="Rect2i"/> and <paramref name="b"/>,
/// or an empty <see cref="Rect2i"/> if they do not intersect.
/// </returns>
- public Rect2i Intersection(Rect2i b)
+ public readonly Rect2i Intersection(Rect2i b)
{
Rect2i newRect = b;
@@ -107,7 +107,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not this <see cref="Rect2i"/> encloses <paramref name="b"/>.
/// </returns>
- public bool Encloses(Rect2i b)
+ public readonly bool Encloses(Rect2i b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
b._position.x + b._size.x < _position.x + _size.x &&
@@ -119,7 +119,7 @@ namespace Godot
/// </summary>
/// <param name="to">The point to include.</param>
/// <returns>The expanded <see cref="Rect2i"/>.</returns>
- public Rect2i Expand(Vector2i to)
+ public readonly Rect2i Expand(Vector2i to)
{
Rect2i expanded = this;
@@ -154,7 +154,7 @@ namespace Godot
/// Returns the area of the <see cref="Rect2i"/>.
/// </summary>
/// <returns>The area.</returns>
- public int GetArea()
+ public readonly int GetArea()
{
return _size.x * _size.y;
}
@@ -166,7 +166,7 @@ namespace Godot
/// value will be rounded towards <see cref="Position"/>.
/// </summary>
/// <returns>The center.</returns>
- public Vector2i GetCenter()
+ public readonly Vector2i GetCenter()
{
return _position + (_size / 2);
}
@@ -179,7 +179,7 @@ namespace Godot
/// <seealso cref="GrowSide(Side, int)"/>
/// <param name="by">The amount to grow by.</param>
/// <returns>The grown <see cref="Rect2i"/>.</returns>
- public Rect2i Grow(int by)
+ public readonly Rect2i Grow(int by)
{
Rect2i g = this;
@@ -202,7 +202,7 @@ namespace Godot
/// <param name="right">The amount to grow by on the right side.</param>
/// <param name="bottom">The amount to grow by on the bottom side.</param>
/// <returns>The grown <see cref="Rect2i"/>.</returns>
- public Rect2i GrowIndividual(int left, int top, int right, int bottom)
+ public readonly Rect2i GrowIndividual(int left, int top, int right, int bottom)
{
Rect2i g = this;
@@ -223,7 +223,7 @@ namespace Godot
/// <param name="side">The side to grow.</param>
/// <param name="by">The amount to grow by.</param>
/// <returns>The grown <see cref="Rect2i"/>.</returns>
- public Rect2i GrowSide(Side side, int by)
+ public readonly Rect2i GrowSide(Side side, int by)
{
Rect2i g = this;
@@ -236,15 +236,17 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if the <see cref="Rect2i"/> is flat or empty,
- /// or <see langword="false"/> otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2i"/> has
+ /// area, and <see langword="false"/> if the <see cref="Rect2i"/>
+ /// is linear, empty, or has a negative <see cref="Size"/>.
+ /// See also <see cref="GetArea"/>.
/// </summary>
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> has area.
/// </returns>
- public bool HasNoArea()
+ public readonly bool HasArea()
{
- return _size.x <= 0 || _size.y <= 0;
+ return _size.x > 0 && _size.y > 0;
}
/// <summary>
@@ -255,7 +257,7 @@ namespace Godot
/// <returns>
/// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> contains <paramref name="point"/>.
/// </returns>
- public bool HasPoint(Vector2i point)
+ public readonly bool HasPoint(Vector2i point)
{
if (point.x < _position.x)
return false;
@@ -273,38 +275,19 @@ namespace Godot
/// <summary>
/// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/>
/// (i.e. they have at least one point in common).
- ///
- /// If <paramref name="includeBorders"/> is <see langword="true"/>,
- /// they will also be considered overlapping if their borders touch,
- /// even without intersection.
/// </summary>
/// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param>
- /// <param name="includeBorders">Whether or not to consider borders.</param>
/// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
- public bool Intersects(Rect2i b, bool includeBorders = false)
+ public readonly bool Intersects(Rect2i b)
{
- if (includeBorders)
- {
- if (_position.x > b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x < b._position.x)
- return false;
- if (_position.y > b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y < b._position.y)
- return false;
- }
- else
- {
- if (_position.x >= b._position.x + b._size.x)
- return false;
- if (_position.x + _size.x <= b._position.x)
- return false;
- if (_position.y >= b._position.y + b._size.y)
- return false;
- if (_position.y + _size.y <= b._position.y)
- return false;
- }
+ if (_position.x >= b._position.x + b._size.x)
+ return false;
+ if (_position.x + _size.x <= b._position.x)
+ return false;
+ if (_position.y >= b._position.y + b._size.y)
+ return false;
+ if (_position.y + _size.y <= b._position.y)
+ return false;
return true;
}
@@ -314,7 +297,7 @@ namespace Godot
/// </summary>
/// <param name="b">The other <see cref="Rect2i"/>.</param>
/// <returns>The merged <see cref="Rect2i"/>.</returns>
- public Rect2i Merge(Rect2i b)
+ public readonly Rect2i Merge(Rect2i b)
{
Rect2i newRect;
@@ -424,14 +407,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The other object to compare.</param>
/// <returns>Whether or not the rect and the other object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Rect2i)
- {
- return Equals((Rect2i)obj);
- }
-
- return false;
+ return obj is Rect2i other && Equals(other);
}
/// <summary>
@@ -439,7 +417,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other rect to compare.</param>
/// <returns>Whether or not the rects are equal.</returns>
- public bool Equals(Rect2i other)
+ public readonly bool Equals(Rect2i other)
{
return _position.Equals(other._position) && _size.Equals(other._size);
}
@@ -448,7 +426,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Rect2i"/>.
/// </summary>
/// <returns>A hash code for this rect.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
@@ -457,7 +435,7 @@ namespace Godot
/// Converts this <see cref="Rect2i"/> to a string.
/// </summary>
/// <returns>A string representation of this rect.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"{_position}, {_size}";
}
@@ -466,7 +444,7 @@ namespace Godot
/// Converts this <see cref="Rect2i"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this rect.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
new file mode 100644
index 0000000000..ee605f8d8f
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Linq;
+
+#nullable enable
+
+namespace Godot;
+
+internal class ReflectionUtils
+{
+ public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName)
+ {
+ return AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name == assemblyName)?
+ .GetType(typeFullName);
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs
index 5680c9d55a..f9b8f06603 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Signal.cs
@@ -3,7 +3,7 @@ namespace Godot
/// <summary>
/// Represents a signal defined in an object.
/// </summary>
- public struct SignalInfo
+ public readonly struct Signal : IAwaitable<Variant[]>
{
private readonly Object _owner;
private readonly StringName _signalName;
@@ -23,10 +23,15 @@ namespace Godot
/// </summary>
/// <param name="owner">Object that contains the signal.</param>
/// <param name="name">Name of the signal.</param>
- public SignalInfo(Object owner, StringName name)
+ public Signal(Object owner, StringName name)
{
_owner = owner;
_signalName = name;
}
+
+ public IAwaiter<Variant[]> GetAwaiter()
+ {
+ return new SignalAwaiter(_owner, _signalName, _owner);
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
index 2ba0493002..96fb891086 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
@@ -1,50 +1,67 @@
using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Godot.NativeInterop;
namespace Godot
{
- public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
+ public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]>
{
private bool _completed;
- private object[] _result;
- private Action _action;
+ private Variant[] _result;
+ private Action _continuation;
public SignalAwaiter(Object source, StringName signal, Object target)
{
- godot_icall_SignalAwaiter_connect(Object.GetPtr(source), StringName.GetPtr(signal), Object.GetPtr(target), this);
+ var awaiterGcHandle = CustomGCHandle.AllocStrong(this);
+ using godot_string_name signalSrc = NativeFuncs.godotsharp_string_name_new_copy(
+ (godot_string_name)(signal?.NativeValue ?? default));
+ NativeFuncs.godotsharp_internal_signal_awaiter_connect(Object.GetPtr(source), in signalSrc,
+ Object.GetPtr(target), GCHandle.ToIntPtr(awaiterGcHandle));
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
+ public bool IsCompleted => _completed;
- public bool IsCompleted
+ public void OnCompleted(Action continuation)
{
- get
- {
- return _completed;
- }
+ _continuation = continuation;
}
- public void OnCompleted(Action action)
- {
- this._action = action;
- }
+ public Variant[] GetResult() => _result;
- public object[] GetResult()
- {
- return _result;
- }
+ public IAwaiter<Variant[]> GetAwaiter() => this;
- public IAwaiter<object[]> GetAwaiter()
+ [UnmanagedCallersOnly]
+ internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
+ godot_bool* outAwaiterIsNull)
{
- return this;
- }
+ try
+ {
+ var awaiter = (SignalAwaiter)GCHandle.FromIntPtr(awaiterGCHandlePtr).Target;
- internal void SignalCallback(object[] args)
- {
- _completed = true;
- _result = args;
- _action?.Invoke();
+ if (awaiter == null)
+ {
+ *outAwaiterIsNull = godot_bool.True;
+ return;
+ }
+
+ *outAwaiterIsNull = godot_bool.False;
+
+ awaiter._completed = true;
+
+ Variant[] signalArgs = new Variant[argCount];
+
+ for (int i = 0; i < argCount; i++)
+ signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]);
+
+ awaiter._result = signalArgs;
+
+ awaiter._continuation?.Invoke();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outAwaiterIsNull = godot_bool.False;
+ }
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index a1f058ffe5..d4329d78c1 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Runtime.CompilerServices;
+using System.IO;
using System.Security;
+using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
+using Godot.NativeInterop;
+
+#nullable enable
namespace Godot
{
@@ -65,30 +69,13 @@ namespace Godot
}
/// <summary>
- /// If the string is a path to a file, return the path to the file without the extension.
- /// </summary>
- /// <seealso cref="GetExtension(string)"/>
- /// <seealso cref="GetBaseDir(string)"/>
- /// <seealso cref="GetFile(string)"/>
- /// <param name="instance">The path to a file.</param>
- /// <returns>The path to the file without the extension.</returns>
- public static string GetBaseName(this string instance)
- {
- int index = instance.LastIndexOf('.');
-
- if (index > 0)
- return instance.Substring(0, index);
-
- return instance;
- }
-
- /// <summary>
/// Returns <see langword="true"/> if the strings begins
/// with the given string <paramref name="text"/>.
/// </summary>
/// <param name="instance">The string to check.</param>
/// <param name="text">The beginning string.</param>
/// <returns>If the string begins with the given string.</returns>
+ [Obsolete("Use string.StartsWith instead.")]
public static bool BeginsWith(this string instance, string text)
{
return instance.StartsWith(text);
@@ -142,15 +129,15 @@ namespace Godot
}
/// <summary>
- /// Returns the amount of substrings <paramref name="what"/> in the string.
+ /// Returns the number of occurrences of substring <paramref name="what"/> in the string.
/// </summary>
/// <param name="instance">The string where the substring will be searched.</param>
/// <param name="what">The substring that will be counted.</param>
- /// <param name="caseSensitive">If the search is case sensitive.</param>
/// <param name="from">Index to start searching from.</param>
/// <param name="to">Index to stop searching at.</param>
- /// <returns>Amount of substrings in the string.</returns>
- public static int Count(this string instance, string what, bool caseSensitive = true, int from = 0, int to = 0)
+ /// <param name="caseSensitive">If the search is case sensitive.</param>
+ /// <returns>Number of occurrences of the substring in the string.</returns>
+ public static int Count(this string instance, string what, int from = 0, int to = 0, bool caseSensitive = true)
{
if (what.Length == 0)
{
@@ -177,6 +164,7 @@ namespace Godot
{
return 0;
}
+
if (from == 0 && to == len)
{
str = instance;
@@ -208,13 +196,89 @@ namespace Godot
}
/// <summary>
+ /// Returns the number of occurrences of substring <paramref name="what"/> (ignoring case)
+ /// between <paramref name="from"/> and <paramref name="to"/> positions. If <paramref name="from"/>
+ /// and <paramref name="to"/> equals 0 the whole string will be used. If only <paramref name="to"/>
+ /// equals 0 the remained substring will be used.
+ /// </summary>
+ /// <param name="instance">The string where the substring will be searched.</param>
+ /// <param name="what">The substring that will be counted.</param>
+ /// <param name="from">Index to start searching from.</param>
+ /// <param name="to">Index to stop searching at.</param>
+ /// <returns>Number of occurrences of the substring in the string.</returns>
+ public static int CountN(this string instance, string what, int from = 0, int to = 0)
+ {
+ return instance.Count(what, from, to, caseSensitive: false);
+ }
+
+ /// <summary>
+ /// Returns a copy of the string with indentation (leading tabs and spaces) removed.
+ /// See also <see cref="Indent"/> to add indentation.
+ /// </summary>
+ /// <param name="instance">The string to remove the indentation from.</param>
+ /// <returns>The string with the indentation removed.</returns>
+ public static string Dedent(this string instance)
+ {
+ var sb = new StringBuilder();
+ string indent = "";
+ bool hasIndent = false;
+ bool hasText = false;
+ int lineStart = 0;
+ int indentStop = -1;
+
+ for (int i = 0; i < instance.Length; i++)
+ {
+ char c = instance[i];
+ if (c == '\n')
+ {
+ if (hasText)
+ {
+ sb.Append(instance.Substring(indentStop, i - indentStop));
+ }
+ sb.Append('\n');
+ hasText = false;
+ lineStart = i + 1;
+ indentStop = -1;
+ }
+ else if (!hasText)
+ {
+ if (c > 32)
+ {
+ hasText = true;
+ if (!hasIndent)
+ {
+ hasIndent = true;
+ indent = instance.Substring(lineStart, i - lineStart);
+ indentStop = i;
+ }
+ }
+ if (hasIndent && indentStop < 0)
+ {
+ int j = i - lineStart;
+ if (j >= indent.Length || c != indent[j])
+ {
+ indentStop = i;
+ }
+ }
+ }
+ }
+
+ if (hasText)
+ {
+ sb.Append(instance.Substring(indentStop, instance.Length - indentStop));
+ }
+
+ return sb.ToString();
+ }
+
+ /// <summary>
/// Returns a copy of the string with special characters escaped using the C language standard.
/// </summary>
/// <param name="instance">The string to escape.</param>
/// <returns>The escaped string.</returns>
public static string CEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@@ -226,7 +290,6 @@ namespace Godot
sb.Replace("\v", "\\v");
sb.Replace("\'", "\\'");
sb.Replace("\"", "\\\"");
- sb.Replace("?", "\\?");
return sb.ToString();
}
@@ -239,7 +302,7 @@ namespace Godot
/// <returns>The unescaped string.</returns>
public static string CUnescape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@@ -250,7 +313,6 @@ namespace Godot
sb.Replace("\\v", "\v");
sb.Replace("\\'", "\'");
sb.Replace("\\\"", "\"");
- sb.Replace("\\?", "?");
sb.Replace("\\\\", "\\");
return sb.ToString();
@@ -284,6 +346,45 @@ namespace Godot
return cap;
}
+ /// <summary>
+ /// Returns the string converted to <c>camelCase</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToCamelCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_camel_case(instanceStr, out godot_string camelCase);
+ using (camelCase)
+ return Marshaling.ConvertStringToManaged(camelCase);
+ }
+
+ /// <summary>
+ /// Returns the string converted to <c>PascalCase</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToPascalCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_pascal_case(instanceStr, out godot_string pascalCase);
+ using (pascalCase)
+ return Marshaling.ConvertStringToManaged(pascalCase);
+ }
+
+ /// <summary>
+ /// Returns the string converted to <c>snake_case</c>.
+ /// </summary>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The converted string.</returns>
+ public static string ToSnakeCase(this string instance)
+ {
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_to_snake_case(instanceStr, out godot_string snakeCase);
+ using (snakeCase)
+ return Marshaling.ConvertStringToManaged(snakeCase);
+ }
+
private static string CamelcaseToUnderscore(this string instance, bool lowerCase)
{
string newString = string.Empty;
@@ -403,29 +504,6 @@ namespace Godot
}
/// <summary>
- /// Returns <see langword="true"/> if the strings ends
- /// with the given string <paramref name="text"/>.
- /// </summary>
- /// <param name="instance">The string to check.</param>
- /// <param name="text">The ending string.</param>
- /// <returns>If the string ends with the given string.</returns>
- public static bool EndsWith(this string instance, string text)
- {
- return instance.EndsWith(text);
- }
-
- /// <summary>
- /// Erase <paramref name="chars"/> characters from the string starting from <paramref name="pos"/>.
- /// </summary>
- /// <param name="instance">The string to modify.</param>
- /// <param name="pos">Starting position from which to erase.</param>
- /// <param name="chars">Amount of characters to erase.</param>
- public static void Erase(this StringBuilder instance, int pos, int chars)
- {
- instance.Remove(pos, chars);
- }
-
- /// <summary>
/// Returns the extension without the leading period character (<c>.</c>)
/// if the string is a valid file name or path. If the string does not contain
/// an extension, returns an empty string instead.
@@ -449,7 +527,7 @@ namespace Godot
/// <returns>The extension of the file or an empty string.</returns>
public static string GetExtension(this string instance)
{
- int pos = instance.FindLast(".");
+ int pos = instance.RFind(".");
if (pos < 0)
return instance;
@@ -458,12 +536,16 @@ namespace Godot
}
/// <summary>
- /// Find the first occurrence of a substring. Optionally, the search starting position can be passed.
+ /// Returns the index of the first occurrence of the specified string in this instance,
+ /// or <c>-1</c>. Optionally, the starting search index can be specified, continuing
+ /// to the end of the string.
+ /// Note: If you just want to know whether a string contains a substring, use the
+ /// <see cref="string.Contains(string)"/> method.
/// </summary>
/// <seealso cref="Find(string, char, int, bool)"/>
- /// <seealso cref="FindLast(string, string, bool)"/>
- /// <seealso cref="FindLast(string, string, int, bool)"/>
/// <seealso cref="FindN(string, string, int)"/>
+ /// <seealso cref="RFind(string, string, int, bool)"/>
+ /// <seealso cref="RFindN(string, string, int)"/>
/// <param name="instance">The string that will be searched.</param>
/// <param name="what">The substring to find.</param>
/// <param name="from">The search starting position.</param>
@@ -471,16 +553,17 @@ namespace Godot
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true)
{
- return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ return instance.IndexOf(what, from,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Find the first occurrence of a char. Optionally, the search starting position can be passed.
/// </summary>
/// <seealso cref="Find(string, string, int, bool)"/>
- /// <seealso cref="FindLast(string, string, bool)"/>
- /// <seealso cref="FindLast(string, string, int, bool)"/>
/// <seealso cref="FindN(string, string, int)"/>
+ /// <seealso cref="RFind(string, string, int, bool)"/>
+ /// <seealso cref="RFindN(string, string, int)"/>
/// <param name="instance">The string that will be searched.</param>
/// <param name="what">The substring to find.</param>
/// <param name="from">The search starting position.</param>
@@ -488,48 +571,21 @@ namespace Godot
/// <returns>The first instance of the char, or -1 if not found.</returns>
public static int Find(this string instance, char what, int from = 0, bool caseSensitive = true)
{
- // TODO: Could be more efficient if we get a char version of `IndexOf`.
- // See https://github.com/dotnet/runtime/issues/44116
- return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
- }
+ if (caseSensitive)
+ return instance.IndexOf(what, from);
- /// <summary>Find the last occurrence of a substring.</summary>
- /// <seealso cref="Find(string, string, int, bool)"/>
- /// <seealso cref="Find(string, char, int, bool)"/>
- /// <seealso cref="FindLast(string, string, int, bool)"/>
- /// <seealso cref="FindN(string, string, int)"/>
- /// <param name="instance">The string that will be searched.</param>
- /// <param name="what">The substring to find.</param>
- /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
- /// <returns>The starting position of the substring, or -1 if not found.</returns>
- public static int FindLast(this string instance, string what, bool caseSensitive = true)
- {
- return instance.FindLast(what, instance.Length - 1, caseSensitive);
- }
-
- /// <summary>Find the last occurrence of a substring specifying the search starting position.</summary>
- /// <seealso cref="Find(string, string, int, bool)"/>
- /// <seealso cref="Find(string, char, int, bool)"/>
- /// <seealso cref="FindLast(string, string, bool)"/>
- /// <seealso cref="FindN(string, string, int)"/>
- /// <param name="instance">The string that will be searched.</param>
- /// <param name="what">The substring to find.</param>
- /// <param name="from">The search starting position.</param>
- /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
- /// <returns>The starting position of the substring, or -1 if not found.</returns>
- public static int FindLast(this string instance, string what, int from, bool caseSensitive = true)
- {
- return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ return CultureInfo.InvariantCulture.CompareInfo.IndexOf(instance, what, from, CompareOptions.OrdinalIgnoreCase);
}
/// <summary>
- /// Find the first occurrence of a substring but search as case-insensitive.
- /// Optionally, the search starting position can be passed.
+ /// Returns the index of the first case-insensitive occurrence of the specified string in this instance,
+ /// or <c>-1</c>. Optionally, the starting search index can be specified, continuing
+ /// to the end of the string.
/// </summary>
/// <seealso cref="Find(string, string, int, bool)"/>
/// <seealso cref="Find(string, char, int, bool)"/>
- /// <seealso cref="FindLast(string, string, bool)"/>
- /// <seealso cref="FindLast(string, string, int, bool)"/>
+ /// <seealso cref="RFind(string, string, int, bool)"/>
+ /// <seealso cref="RFindN(string, string, int)"/>
/// <param name="instance">The string that will be searched.</param>
/// <param name="what">The substring to find.</param>
/// <param name="from">The search starting position.</param>
@@ -573,7 +629,7 @@ namespace Godot
}
}
- int sep = Mathf.Max(rs.FindLast("/"), rs.FindLast("\\"));
+ int sep = Mathf.Max(rs.RFind("/"), rs.RFind("\\"));
if (sep == -1)
return directory;
@@ -582,6 +638,24 @@ namespace Godot
}
/// <summary>
+ /// If the string is a path to a file, return the path to the file without the extension.
+ /// </summary>
+ /// <seealso cref="GetExtension(string)"/>
+ /// <seealso cref="GetBaseDir(string)"/>
+ /// <seealso cref="GetFile(string)"/>
+ /// <param name="instance">The path to a file.</param>
+ /// <returns>The path to the file without the extension.</returns>
+ public static string GetBaseName(this string instance)
+ {
+ int index = instance.RFind(".");
+
+ if (index > 0)
+ return instance.Substring(0, index);
+
+ return instance;
+ }
+
+ /// <summary>
/// If the string is a path to a file, return the file and ignore the base directory.
/// </summary>
/// <seealso cref="GetBaseName(string)"/>
@@ -591,7 +665,7 @@ namespace Godot
/// <returns>The file name.</returns>
public static string GetFile(this string instance)
{
- int sep = Mathf.Max(instance.FindLast("/"), instance.FindLast("\\"));
+ int sep = Mathf.Max(instance.RFind("/"), instance.RFind("\\"));
if (sep == -1)
return instance;
@@ -600,8 +674,8 @@ namespace Godot
}
/// <summary>
- /// Converts the given byte array of ASCII encoded text to a string.
- /// Faster alternative to <see cref="GetStringFromUTF8"/> if the
+ /// Converts ASCII encoded array to string.
+ /// Fast alternative to <see cref="GetStringFromUTF8"/> if the
/// content is ASCII-only. Unlike the UTF-8 function this function
/// maps every byte to a character in the array. Multibyte sequences
/// will not be interpreted correctly. For parsing user input always
@@ -615,13 +689,35 @@ namespace Godot
}
/// <summary>
- /// Converts the given byte array of UTF-8 encoded text to a string.
+ /// Converts UTF-16 encoded array to string using the little endian byte order.
+ /// </summary>
+ /// <param name="bytes">A byte array of UTF-16 characters.</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromUTF16(this byte[] bytes)
+ {
+ return Encoding.Unicode.GetString(bytes);
+ }
+
+ /// <summary>
+ /// Converts UTF-32 encoded array to string using the little endian byte order.
+ /// </summary>
+ /// <param name="bytes">A byte array of UTF-32 characters.</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromUTF32(this byte[] bytes)
+ {
+ return Encoding.UTF32.GetString(bytes);
+ }
+
+ /// <summary>
+ /// Converts UTF-8 encoded array to string.
/// Slower than <see cref="GetStringFromASCII"/> but supports UTF-8
/// encoded data. Use this function if you are unsure about the
/// source of the data. For user input this function
/// should always be preferred.
/// </summary>
- /// <param name="bytes">A byte array of UTF-8 characters (a character may take up multiple bytes).</param>
+ /// <param name="bytes">
+ /// A byte array of UTF-8 characters (a character may take up multiple bytes).
+ /// </param>
/// <returns>A string created from the bytes.</returns>
public static string GetStringFromUTF8(this byte[] bytes)
{
@@ -723,18 +819,44 @@ namespace Godot
}
/// <summary>
- /// Inserts a substring at a given position.
+ /// Returns a copy of the string with lines indented with <paramref name="prefix"/>.
+ /// For example, the string can be indented with two tabs using <c>"\t\t"</c>,
+ /// or four spaces using <c>" "</c>. The prefix can be any string so it can
+ /// also be used to comment out strings with e.g. <c>"// </c>.
+ /// See also <see cref="Dedent"/> to remove indentation.
+ /// Note: Empty lines are kept empty.
/// </summary>
- /// <param name="instance">The string to modify.</param>
- /// <param name="pos">Position at which to insert the substring.</param>
- /// <param name="what">Substring to insert.</param>
- /// <returns>
- /// The string with <paramref name="what"/> inserted at the given
- /// position <paramref name="pos"/>.
- /// </returns>
- public static string Insert(this string instance, int pos, string what)
+ /// <param name="instance">The string to add indentation to.</param>
+ /// <param name="prefix">The string to use as indentation.</param>
+ /// <returns>The string with indentation added.</returns>
+ public static string Indent(this string instance, string prefix)
{
- return instance.Insert(pos, what);
+ var sb = new StringBuilder();
+ int lineStart = 0;
+
+ for (int i = 0; i < instance.Length; i++)
+ {
+ char c = instance[i];
+ if (c == '\n')
+ {
+ if (i == lineStart)
+ {
+ sb.Append(c); // Leave empty lines empty.
+ }
+ else
+ {
+ sb.Append(prefix);
+ sb.Append(instance.Substring(lineStart, i - lineStart + 1));
+ }
+ lineStart = i + 1;
+ }
+ }
+ if (lineStart != instance.Length)
+ {
+ sb.Append(prefix);
+ sb.Append(instance.Substring(lineStart));
+ }
+ return sb.ToString();
}
/// <summary>
@@ -804,6 +926,7 @@ namespace Godot
{
match = instance[source] == text[target];
}
+
if (match)
{
source++;
@@ -829,19 +952,94 @@ namespace Godot
return instance.IsSubsequenceOf(text, caseSensitive: false);
}
+ private static readonly char[] _invalidFileNameCharacters = { ':', '/', '\\', '?', '*', '"', '|', '%', '<', '>' };
+
/// <summary>
- /// Check whether the string contains a valid <see langword="float"/>.
+ /// Returns <see langword="true"/> if this string is free from characters that
+ /// aren't allowed in file names.
/// </summary>
/// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid file name.</returns>
+ public static bool IsValidFileName(this string instance)
+ {
+ var stripped = instance.Trim();
+ if (instance != stripped)
+ return false;
+
+ if (string.IsNullOrEmpty(stripped))
+ return false;
+
+ return instance.IndexOfAny(_invalidFileNameCharacters) == -1;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this string contains a valid <see langword="float"/>.
+ /// This is inclusive of integers, and also supports exponents.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print("1.7".IsValidFloat()) // Prints "True"
+ /// GD.Print("24".IsValidFloat()) // Prints "True"
+ /// GD.Print("7e3".IsValidFloat()) // Prints "True"
+ /// GD.Print("Hello".IsValidFloat()) // Prints "False"
+ /// </code>
+ /// </example>
+ /// <param name="instance">The string to check.</param>
/// <returns>If the string contains a valid floating point number.</returns>
public static bool IsValidFloat(this string instance)
{
- float f;
- return float.TryParse(instance, out f);
+ return float.TryParse(instance, out _);
}
/// <summary>
- /// Check whether the string contains a valid color in HTML notation.
+ /// Returns <see langword="true"/> if this string contains a valid hexadecimal number.
+ /// If <paramref name="withPrefix"/> is <see langword="true"/>, then a validity of the
+ /// hexadecimal number is determined by <c>0x</c> prefix, for instance: <c>0xDEADC0DE</c>.
+ /// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="withPrefix">If the string must contain the <c>0x</c> prefix to be valid.</param>
+ /// <returns>If the string contains a valid hexadecimal number.</returns>
+ public static bool IsValidHexNumber(this string instance, bool withPrefix = false)
+ {
+ if (string.IsNullOrEmpty(instance))
+ return false;
+
+ int from = 0;
+ if (instance.Length != 1 && instance[0] == '+' || instance[0] == '-')
+ {
+ from++;
+ }
+
+ if (withPrefix)
+ {
+ if (instance.Length < 3)
+ return false;
+ if (instance[from] != '0' || instance[from + 1] != 'x')
+ return false;
+ from += 2;
+ }
+
+ for (int i = from; i < instance.Length; i++)
+ {
+ char c = instance[i];
+ if (IsHexDigit(c))
+ continue;
+
+ return false;
+ }
+
+ return true;
+
+ static bool IsHexDigit(char c)
+ {
+ return char.IsDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+ }
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this string contains a valid color in hexadecimal
+ /// HTML notation. Other HTML notations such as named colors or <c>hsl()</c> aren't
+ /// considered valid by this method and will return <see langword="false"/>.
/// </summary>
/// <param name="instance">The string to check.</param>
/// <returns>If the string contains a valid HTML color.</returns>
@@ -851,10 +1049,17 @@ namespace Godot
}
/// <summary>
- /// Check whether the string is a valid identifier. As is common in
- /// programming languages, a valid identifier may contain only letters,
- /// digits and underscores (_) and the first character may not be a digit.
+ /// Returns <see langword="true"/> if this string is a valid identifier.
+ /// A valid identifier may contain only letters, digits and underscores (<c>_</c>)
+ /// and the first character may not be a digit.
/// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print("good_ident_1".IsValidIdentifier()) // Prints "True"
+ /// GD.Print("1st_bad_ident".IsValidIdentifier()) // Prints "False"
+ /// GD.Print("bad_ident_#2".IsValidIdentifier()) // Prints "False"
+ /// </code>
+ /// </example>
/// <param name="instance">The string to check.</param>
/// <returns>If the string contains a valid identifier.</returns>
public static bool IsValidIdentifier(this string instance)
@@ -882,38 +1087,73 @@ namespace Godot
}
/// <summary>
- /// Check whether the string contains a valid integer.
+ /// Returns <see langword="true"/> if this string contains a valid <see langword="int"/>.
/// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print("7".IsValidInt()) // Prints "True"
+ /// GD.Print("14.6".IsValidInt()) // Prints "False"
+ /// GD.Print("L".IsValidInt()) // Prints "False"
+ /// GD.Print("+3".IsValidInt()) // Prints "True"
+ /// GD.Print("-12".IsValidInt()) // Prints "True"
+ /// </code>
+ /// </example>
/// <param name="instance">The string to check.</param>
/// <returns>If the string contains a valid integer.</returns>
- public static bool IsValidInteger(this string instance)
+ public static bool IsValidInt(this string instance)
{
- int f;
- return int.TryParse(instance, out f);
+ return int.TryParse(instance, out _);
}
/// <summary>
- /// Check whether the string contains a valid IP address.
+ /// Returns <see langword="true"/> if this string contains only a well-formatted
+ /// IPv4 or IPv6 address. This method considers reserved IP addresses such as
+ /// <c>0.0.0.0</c> as valid.
/// </summary>
/// <param name="instance">The string to check.</param>
/// <returns>If the string contains a valid IP address.</returns>
public static bool IsValidIPAddress(this string instance)
{
- // TODO: Support IPv6 addresses
- string[] ip = instance.Split(".");
+ if (instance.Contains(':'))
+ {
+ string[] ip = instance.Split(':');
- if (ip.Length != 4)
- return false;
+ for (int i = 0; i < ip.Length; i++)
+ {
+ string n = ip[i];
+ if (n.Length == 0)
+ continue;
- for (int i = 0; i < ip.Length; i++)
+ if (n.IsValidHexNumber(withPrefix: false))
+ {
+ long nint = n.HexToInt();
+ if (nint < 0 || nint > 0xffff)
+ return false;
+
+ continue;
+ }
+
+ if (!n.IsValidIPAddress())
+ return false;
+ }
+ }
+ else
{
- string n = ip[i];
- if (!n.IsValidInteger())
- return false;
+ string[] ip = instance.Split('.');
- int val = n.ToInt();
- if (val < 0 || val > 255)
+ if (ip.Length != 4)
return false;
+
+ for (int i = 0; i < ip.Length; i++)
+ {
+ string n = ip[i];
+ if (!n.IsValidInt())
+ return false;
+
+ int val = n.ToInt();
+ if (val < 0 || val > 255)
+ return false;
+ }
}
return true;
@@ -926,7 +1166,7 @@ namespace Godot
/// <returns>The escaped string.</returns>
public static string JSONEscape(this string instance)
{
- var sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(instance);
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@@ -959,41 +1199,20 @@ namespace Godot
}
/// <summary>
- /// Returns the length of the string in characters.
- /// </summary>
- /// <param name="instance">The string to check.</param>
- /// <returns>The length of the string.</returns>
- public static int Length(this string instance)
- {
- return instance.Length;
- }
-
- /// <summary>
/// Returns a copy of the string with characters removed from the left.
+ /// The <paramref name="chars"/> argument is a string specifying the set of characters
+ /// to be removed.
+ /// Note: The <paramref name="chars"/> is not a prefix. See <see cref="TrimPrefix"/>
+ /// method that will remove a single prefix string rather than a set of characters.
/// </summary>
/// <seealso cref="RStrip(string, string)"/>
/// <param name="instance">The string to remove characters from.</param>
/// <param name="chars">The characters to be removed.</param>
/// <returns>A copy of the string with characters removed from the left.</returns>
+ [Obsolete("Use string.TrimStart instead.")]
public static string LStrip(this string instance, string chars)
{
- int len = instance.Length;
- int beg;
-
- for (beg = 0; beg < len; beg++)
- {
- if (chars.Find(instance[beg]) == -1)
- {
- break;
- }
- }
-
- if (beg == 0)
- {
- return instance;
- }
-
- return instance.Substr(beg, len - beg);
+ return instance.TrimStart(chars.ToCharArray());
}
/// <summary>
@@ -1015,15 +1234,18 @@ namespace Godot
switch (expr[0])
{
case '*':
- return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 && ExprMatch(instance.Substring(1), expr, caseSensitive));
+ return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
+ ExprMatch(instance.Substring(1), expr, caseSensitive));
case '?':
- return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return instance.Length > 0 && instance[0] != '.' &&
+ ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
if (instance.Length == 0)
return false;
if (caseSensitive)
return instance[0] == expr[0];
- return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) &&
+ ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
@@ -1070,12 +1292,11 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static byte[] MD5Buffer(this string instance)
{
- return godot_icall_String_md5_buffer(instance);
+#pragma warning disable CA5351 // Do Not Use Broken Cryptographic Algorithms
+ return MD5.HashData(Encoding.UTF8.GetBytes(instance));
+#pragma warning restore CA5351
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_md5_buffer(string str);
-
/// <summary>
/// Returns the MD5 hash of the string as a string.
/// </summary>
@@ -1084,12 +1305,9 @@ namespace Godot
/// <returns>The MD5 hash of the string.</returns>
public static string MD5Text(this string instance)
{
- return godot_icall_String_md5_text(instance);
+ return instance.MD5Buffer().HexEncode();
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_md5_text(string str);
-
/// <summary>
/// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
@@ -1104,17 +1322,6 @@ namespace Godot
}
/// <summary>
- /// Returns the character code at position <paramref name="at"/>.
- /// </summary>
- /// <param name="instance">The string to check.</param>
- /// <param name="at">The position int the string for the character to check.</param>
- /// <returns>The character code.</returns>
- public static int OrdAt(this string instance, int at)
- {
- return instance[at];
- }
-
- /// <summary>
/// Format a number to have an exact number of <paramref name="digits"/>
/// after the decimal point.
/// </summary>
@@ -1196,12 +1403,12 @@ namespace Godot
/// <summary>
/// If the string is a path, this concatenates <paramref name="file"/>
/// at the end of the string as a subpath.
- /// E.g. <c>"this/is".PlusFile("path") == "this/is/path"</c>.
+ /// E.g. <c>"this/is".PathJoin("path") == "this/is/path"</c>.
/// </summary>
/// <param name="instance">The path that will be concatenated.</param>
/// <param name="file">File name to concatenate with the path.</param>
/// <returns>The concatenated path with the given file name.</returns>
- public static string PlusFile(this string instance, string file)
+ public static string PathJoin(this string instance, string file)
{
if (instance.Length > 0 && instance[instance.Length - 1] == '/')
return instance + file;
@@ -1235,37 +1442,48 @@ namespace Godot
}
/// <summary>
- /// Perform a search for a substring, but start from the end of the string instead of the beginning.
+ /// Returns the index of the last occurrence of the specified string in this instance,
+ /// or <c>-1</c>. Optionally, the starting search index can be specified, continuing to
+ /// the beginning of the string.
/// </summary>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
/// <seealso cref="RFindN(string, string, int)"/>
/// <param name="instance">The string that will be searched.</param>
/// <param name="what">The substring to search in the string.</param>
/// <param name="from">The position at which to start searching.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
- public static int RFind(this string instance, string what, int from = -1)
+ public static int RFind(this string instance, string what, int from = -1, bool caseSensitive = true)
{
- return godot_icall_String_rfind(instance, what, from);
- }
+ if (from == -1)
+ from = instance.Length - 1;
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_String_rfind(string str, string what, int from);
+ return instance.LastIndexOf(what, from,
+ caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
/// <summary>
- /// Perform a search for a substring, but start from the end of the string instead of the beginning.
- /// Also search case-insensitive.
+ /// Returns the index of the last case-insensitive occurrence of the specified string in this instance,
+ /// or <c>-1</c>. Optionally, the starting search index can be specified, continuing to
+ /// the beginning of the string.
/// </summary>
- /// <seealso cref="RFind(string, string, int)"/>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
+ /// <seealso cref="RFind(string, string, int, bool)"/>
/// <param name="instance">The string that will be searched.</param>
/// <param name="what">The substring to search in the string.</param>
/// <param name="from">The position at which to start searching.</param>
/// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFindN(this string instance, string what, int from = -1)
{
- return godot_icall_String_rfindn(instance, what, from);
- }
+ if (from == -1)
+ from = instance.Length - 1;
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int godot_icall_String_rfindn(string str, string what, int from);
+ return instance.LastIndexOf(what, from, StringComparison.OrdinalIgnoreCase);
+ }
/// <summary>
/// Returns the right side of the string from a given position.
@@ -1287,30 +1505,43 @@ namespace Godot
/// <summary>
/// Returns a copy of the string with characters removed from the right.
+ /// The <paramref name="chars"/> argument is a string specifying the set of characters
+ /// to be removed.
+ /// Note: The <paramref name="chars"/> is not a suffix. See <see cref="TrimSuffix"/>
+ /// method that will remove a single suffix string rather than a set of characters.
/// </summary>
/// <seealso cref="LStrip(string, string)"/>
/// <param name="instance">The string to remove characters from.</param>
/// <param name="chars">The characters to be removed.</param>
/// <returns>A copy of the string with characters removed from the right.</returns>
+ [Obsolete("Use string.TrimEnd instead.")]
public static string RStrip(this string instance, string chars)
{
- int len = instance.Length;
- int end;
-
- for (end = len - 1; end >= 0; end--)
- {
- if (chars.Find(instance[end]) == -1)
- {
- break;
- }
- }
+ return instance.TrimEnd(chars.ToCharArray());
+ }
- if (end == len - 1)
- {
- return instance;
- }
+ /// <summary>
+ /// Returns the SHA-1 hash of the string as an array of bytes.
+ /// </summary>
+ /// <seealso cref="SHA1Text(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The SHA-1 hash of the string.</returns>
+ public static byte[] SHA1Buffer(this string instance)
+ {
+#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms
+ return SHA1.HashData(Encoding.UTF8.GetBytes(instance));
+#pragma warning restore CA5350
+ }
- return instance.Substr(0, end + 1);
+ /// <summary>
+ /// Returns the SHA-1 hash of the string as a string.
+ /// </summary>
+ /// <seealso cref="SHA1Buffer(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The SHA-1 hash of the string.</returns>
+ public static string SHA1Text(this string instance)
+ {
+ return instance.SHA1Buffer().HexEncode();
}
/// <summary>
@@ -1321,12 +1552,9 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static byte[] SHA256Buffer(this string instance)
{
- return godot_icall_String_sha256_buffer(instance);
+ return SHA256.HashData(Encoding.UTF8.GetBytes(instance));
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern byte[] godot_icall_String_sha256_buffer(string str);
-
/// <summary>
/// Returns the SHA-256 hash of the string as a string.
/// </summary>
@@ -1335,12 +1563,9 @@ namespace Godot
/// <returns>The SHA-256 hash of the string.</returns>
public static string SHA256Text(this string instance)
{
- return godot_icall_String_sha256_text(instance);
+ return instance.SHA256Buffer().HexEncode();
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_sha256_text(string str);
-
/// <summary>
/// Returns the similarity index of the text compared to this string.
/// 1 means totally similar and 0 means totally dissimilar.
@@ -1355,6 +1580,7 @@ namespace Godot
// Equal strings are totally similar
return 1.0f;
}
+
if (instance.Length < 2 || text.Length < 2)
{
// No way to calculate similarity without a single bigram
@@ -1390,12 +1616,12 @@ namespace Godot
/// </summary>
public static string SimplifyPath(this string instance)
{
- return godot_icall_String_simplify_path(instance);
+ using godot_string instanceStr = Marshaling.ConvertStringToNative(instance);
+ NativeFuncs.godotsharp_string_simplify_path(instanceStr, out godot_string simplifiedPath);
+ using (simplifiedPath)
+ return Marshaling.ConvertStringToManaged(simplifiedPath);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string godot_icall_String_simplify_path(string str);
-
/// <summary>
/// Split the string by a divisor string, return an array of the substrings.
/// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
@@ -1409,7 +1635,8 @@ namespace Godot
/// <returns>The array of strings split from the string.</returns>
public static string[] Split(this string instance, string divisor, bool allowEmpty = true)
{
- return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
+ return instance.Split(divisor,
+ allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
}
/// <summary>
@@ -1456,8 +1683,10 @@ namespace Godot
};
/// <summary>
- /// Returns a copy of the string stripped of any non-printable character at the beginning and the end.
- /// The optional arguments are used to toggle stripping on the left and right edges respectively.
+ /// Returns a copy of the string stripped of any non-printable character
+ /// (including tabulations, spaces and line breaks) at the beginning and the end.
+ /// The optional arguments are used to toggle stripping on the left and right
+ /// edges respectively.
/// </summary>
/// <param name="instance">The string to strip.</param>
/// <param name="left">If the left side should be stripped.</param>
@@ -1475,6 +1704,30 @@ namespace Godot
return instance.TrimEnd(_nonPrintable);
}
+
+ /// <summary>
+ /// Returns a copy of the string stripped of any escape character.
+ /// These include all non-printable control characters of the first page
+ /// of the ASCII table (&lt; 32), such as tabulation (<c>\t</c>) and
+ /// newline (<c>\n</c> and <c>\r</c>) characters, but not spaces.
+ /// </summary>
+ /// <param name="instance">The string to strip.</param>
+ /// <returns>The string stripped of any escape characters.</returns>
+ public static string StripEscapes(this string instance)
+ {
+ var sb = new StringBuilder();
+ for (int i = 0; i < instance.Length; i++)
+ {
+ // Escape characters on first page of the ASCII table, before 32 (Space).
+ if (instance[i] < 32)
+ continue;
+
+ sb.Append(instance[i]);
+ }
+
+ return sb.ToString();
+ }
+
/// <summary>
/// Returns part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>.
/// </summary>
@@ -1492,13 +1745,15 @@ namespace Godot
/// <summary>
/// Converts the String (which is a character array) to PackedByteArray (which is an array of bytes).
- /// The conversion is speeded up in comparison to <see cref="ToUTF8(string)"/> with the assumption
- /// that all the characters the String contains are only ASCII characters.
+ /// The conversion is faster compared to <see cref="ToUTF8Buffer(string)"/>,
+ /// as this method assumes that all the characters in the String are ASCII characters.
/// </summary>
- /// <seealso cref="ToUTF8(string)"/>
+ /// <seealso cref="ToUTF8Buffer(string)"/>
+ /// <seealso cref="ToUTF16Buffer(string)"/>
+ /// <seealso cref="ToUTF32Buffer(string)"/>
/// <param name="instance">The string to convert.</param>
/// <returns>The string as ASCII encoded bytes.</returns>
- public static byte[] ToAscii(this string instance)
+ public static byte[] ToASCIIBuffer(this string instance)
{
return Encoding.ASCII.GetBytes(instance);
}
@@ -1526,41 +1781,76 @@ namespace Godot
}
/// <summary>
- /// Returns the string converted to lowercase.
+ /// Converts the string (which is an array of characters) to an UTF-16 encoded array of bytes.
/// </summary>
- /// <seealso cref="ToUpper(string)"/>
+ /// <seealso cref="ToASCIIBuffer(string)"/>
+ /// <seealso cref="ToUTF32Buffer(string)"/>
+ /// <seealso cref="ToUTF8Buffer(string)"/>
/// <param name="instance">The string to convert.</param>
- /// <returns>The string converted to lowercase.</returns>
- public static string ToLower(this string instance)
+ /// <returns>The string as UTF-16 encoded bytes.</returns>
+ public static byte[] ToUTF16Buffer(this string instance)
{
- return instance.ToLower();
+ return Encoding.Unicode.GetBytes(instance);
}
/// <summary>
- /// Returns the string converted to uppercase.
+ /// Converts the string (which is an array of characters) to an UTF-32 encoded array of bytes.
/// </summary>
- /// <seealso cref="ToLower(string)"/>
+ /// <seealso cref="ToASCIIBuffer(string)"/>
+ /// <seealso cref="ToUTF16Buffer(string)"/>
+ /// <seealso cref="ToUTF8Buffer(string)"/>
/// <param name="instance">The string to convert.</param>
- /// <returns>The string converted to uppercase.</returns>
- public static string ToUpper(this string instance)
+ /// <returns>The string as UTF-32 encoded bytes.</returns>
+ public static byte[] ToUTF32Buffer(this string instance)
{
- return instance.ToUpper();
+ return Encoding.UTF32.GetBytes(instance);
}
/// <summary>
- /// Converts the String (which is an array of characters) to PackedByteArray (which is an array of bytes).
- /// The conversion is a bit slower than <see cref="ToAscii(string)"/>, but supports all UTF-8 characters.
- /// Therefore, you should prefer this function over <see cref="ToAscii(string)"/>.
+ /// Converts the string (which is an array of characters) to an UTF-8 encoded array of bytes.
+ /// The conversion is a bit slower than <see cref="ToASCIIBuffer(string)"/>,
+ /// but supports all UTF-8 characters. Therefore, you should prefer this function
+ /// over <see cref="ToASCIIBuffer(string)"/>.
/// </summary>
- /// <seealso cref="ToAscii(string)"/>
+ /// <seealso cref="ToASCIIBuffer(string)"/>
+ /// <seealso cref="ToUTF16Buffer(string)"/>
+ /// <seealso cref="ToUTF32Buffer(string)"/>
/// <param name="instance">The string to convert.</param>
/// <returns>The string as UTF-8 encoded bytes.</returns>
- public static byte[] ToUTF8(this string instance)
+ public static byte[] ToUTF8Buffer(this string instance)
{
return Encoding.UTF8.GetBytes(instance);
}
/// <summary>
+ /// Removes a given string from the start if it starts with it or leaves the string unchanged.
+ /// </summary>
+ /// <param name="instance">The string to remove the prefix from.</param>
+ /// <param name="prefix">The string to remove from the start.</param>
+ /// <returns>A copy of the string with the prefix string removed from the start.</returns>
+ public static string TrimPrefix(this string instance, string prefix)
+ {
+ if (instance.StartsWith(prefix))
+ return instance.Substring(prefix.Length);
+
+ return instance;
+ }
+
+ /// <summary>
+ /// Removes a given string from the end if it ends with it or leaves the string unchanged.
+ /// </summary>
+ /// <param name="instance">The string to remove the suffix from.</param>
+ /// <param name="suffix">The string to remove from the end.</param>
+ /// <returns>A copy of the string with the suffix string removed from the end.</returns>
+ public static string TrimSuffix(this string instance, string suffix)
+ {
+ if (instance.EndsWith(suffix))
+ return instance.Substring(0, instance.Length - suffix.Length);
+
+ return instance;
+ }
+
+ /// <summary>
/// Decodes a string in URL encoded format. This is meant to
/// decode parameters in a URL when receiving an HTTP request.
/// This mostly wraps around <see cref="Uri.UnescapeDataString"/>,
@@ -1587,6 +1877,25 @@ namespace Godot
return Uri.EscapeDataString(instance);
}
+ private const string _uniqueNodePrefix = "%";
+ private static readonly string[] _invalidNodeNameCharacters = { ".", ":", "@", "/", "\"", _uniqueNodePrefix };
+
+ /// <summary>
+ /// Removes any characters from the string that are prohibited in
+ /// <see cref="Node"/> names (<c>.</c> <c>:</c> <c>@</c> <c>/</c> <c>"</c>).
+ /// </summary>
+ /// <param name="instance">The string to sanitize.</param>
+ /// <returns>The string sanitized as a valid node name.</returns>
+ public static string ValidateNodeName(this string instance)
+ {
+ string name = instance.Replace(_invalidNodeNameCharacters[0], "");
+ for (int i = 1; i < _invalidNodeNameCharacters.Length; i++)
+ {
+ name = name.Replace(_invalidNodeNameCharacters[i], "");
+ }
+ return name;
+ }
+
/// <summary>
/// Returns a copy of the string with special characters escaped using the XML standard.
/// </summary>
@@ -1605,9 +1914,9 @@ namespace Godot
/// <seealso cref="XMLEscape(string)"/>
/// <param name="instance">The string to unescape.</param>
/// <returns>The unescaped string.</returns>
- public static string XMLUnescape(this string instance)
+ public static string? XMLUnescape(this string instance)
{
- return SecurityElement.FromString(instance).Text;
+ return SecurityElement.FromString(instance)?.Text;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index b1d504410b..b9ee0bc278 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
namespace Godot
{
@@ -10,20 +10,11 @@ namespace Godot
/// Comparing them is much faster than with regular strings, because only the pointers are compared,
/// not the whole strings.
/// </summary>
- public sealed partial class StringName : IDisposable
+ public sealed class StringName : IDisposable
{
- private IntPtr ptr;
+ internal godot_string_name.movable NativeValue;
- internal static IntPtr GetPtr(StringName instance)
- {
- if (instance == null)
- throw new NullReferenceException($"The instance of type {nameof(StringName)} is null.");
-
- if (instance.ptr == IntPtr.Zero)
- throw new ObjectDisposedException(instance.GetType().FullName);
-
- return instance.ptr;
- }
+ private WeakReference<IDisposable> _weakReferenceToSelf;
~StringName()
{
@@ -39,35 +30,45 @@ namespace Godot
GC.SuppressFinalize(this);
}
- private void Dispose(bool disposing)
+ public void Dispose(bool disposing)
{
- if (ptr != IntPtr.Zero)
+ // Always dispose `NativeValue` even if disposing is true
+ NativeValue.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
{
- godot_icall_StringName_Dtor(ptr);
- ptr = IntPtr.Zero;
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
}
}
- internal StringName(IntPtr ptr)
+ private StringName(godot_string_name nativeValueToOwn)
{
- this.ptr = ptr;
+ NativeValue = (godot_string_name.movable)nativeValueToOwn;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
+ // Explicit name to make it very clear
+ internal static StringName CreateTakingOwnershipOfDisposableValue(godot_string_name nativeValueToOwn)
+ => new StringName(nativeValueToOwn);
+
/// <summary>
/// Constructs an empty <see cref="StringName"/>.
/// </summary>
public StringName()
{
- ptr = IntPtr.Zero;
}
/// <summary>
- /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string.
+ /// Constructs a <see cref="StringName"/> from the given <paramref name="name"/> string.
/// </summary>
- /// <param name="path">String to construct the <see cref="StringName"/> from.</param>
- public StringName(string path)
+ /// <param name="name">String to construct the <see cref="StringName"/> from.</param>
+ public StringName(string name)
{
- ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
+ if (!string.IsNullOrEmpty(name))
+ {
+ NativeValue = (godot_string_name.movable)NativeFuncs.godotsharp_string_name_new_from_string(name);
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
}
/// <summary>
@@ -80,7 +81,7 @@ namespace Godot
/// Converts a <see cref="StringName"/> to a string.
/// </summary>
/// <param name="from">The <see cref="StringName"/> to convert.</param>
- public static implicit operator string(StringName from) => from.ToString();
+ public static implicit operator string(StringName from) => from?.ToString();
/// <summary>
/// Converts this <see cref="StringName"/> to a string.
@@ -88,28 +89,75 @@ namespace Godot
/// <returns>A string representation of this <see cref="StringName"/>.</returns>
public override string ToString()
{
- return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
+ if (IsEmpty)
+ return string.Empty;
+
+ var src = (godot_string_name)NativeValue;
+ NativeFuncs.godotsharp_string_name_as_string(out godot_string dest, src);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
}
/// <summary>
/// Check whether this <see cref="StringName"/> is empty.
/// </summary>
/// <returns>If the <see cref="StringName"/> is empty.</returns>
- public bool IsEmpty()
+ public bool IsEmpty => NativeValue.DangerousSelfRef.IsEmpty;
+
+ public static bool operator ==(StringName left, StringName right)
{
- return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
+ if (left is null)
+ return right is null;
+ return left.Equals(right);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr godot_icall_StringName_Ctor(string path);
+ public static bool operator !=(StringName left, StringName right)
+ {
+ return !(left == right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void godot_icall_StringName_Dtor(IntPtr ptr);
+ public bool Equals(StringName other)
+ {
+ if (other is null)
+ return false;
+ return NativeValue.DangerousSelfRef == other.NativeValue.DangerousSelfRef;
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string godot_icall_StringName_operator_String(IntPtr ptr);
+ public static bool operator ==(StringName left, in godot_string_name right)
+ {
+ if (left is null)
+ return right.IsEmpty;
+ return left.Equals(right);
+ }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool godot_icall_StringName_is_empty(IntPtr ptr);
+ public static bool operator !=(StringName left, in godot_string_name right)
+ {
+ return !(left == right);
+ }
+
+ public static bool operator ==(in godot_string_name left, StringName right)
+ {
+ return right == left;
+ }
+
+ public static bool operator !=(in godot_string_name left, StringName right)
+ {
+ return !(right == left);
+ }
+
+ public bool Equals(in godot_string_name other)
+ {
+ return NativeValue.DangerousSelfRef == other;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || (obj is StringName other && Equals(other));
+ }
+
+ public override int GetHashCode()
+ {
+ return NativeValue.GetHashCode();
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index e01db7b88c..fa060e3a53 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -37,52 +32,16 @@ namespace Godot
public Vector2 origin;
/// <summary>
- /// The rotation of this transformation matrix.
- /// </summary>
- /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
- public real_t Rotation
- {
- get
- {
- return Mathf.Atan2(x.y, x.x);
- }
- set
- {
- Vector2 scale = Scale;
- x.x = y.y = Mathf.Cos(value);
- x.y = y.x = Mathf.Sin(value);
- y.x *= -1;
- Scale = scale;
- }
- }
-
- /// <summary>
- /// The scale of this transformation matrix.
- /// </summary>
- /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
- public Vector2 Scale
- {
- get
- {
- real_t detSign = Mathf.Sign(BasisDeterminant());
- return new Vector2(x.Length(), detSign * y.Length());
- }
- set
- {
- value /= Scale; // Value becomes what's called "delta_scale" in core.
- x *= value.x;
- y *= value.y;
- }
- }
-
- /// <summary>
/// Access whole columns in the form of <see cref="Vector2"/>.
/// The third column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column vector.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> is not 0, 1 or 2.
+ /// </exception>
public Vector2 this[int column]
{
- get
+ readonly get
{
switch (column)
{
@@ -93,7 +52,7 @@ namespace Godot
case 2:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -110,7 +69,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -123,7 +82,7 @@ namespace Godot
/// <param name="row">Which row, the matrix vertical position.</param>
public real_t this[int column, int row]
{
- get
+ readonly get
{
return this[column][row];
}
@@ -141,7 +100,7 @@ namespace Godot
/// </summary>
/// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
- public Transform2D AffineInverse()
+ public readonly Transform2D AffineInverse()
{
real_t det = BasisDeterminant();
@@ -150,9 +109,8 @@ namespace Godot
Transform2D inv = this;
- real_t temp = inv[0, 0];
- inv[0, 0] = inv[1, 1];
- inv[1, 1] = temp;
+ inv[0, 0] = this[1, 1];
+ inv[1, 1] = this[0, 0];
real_t detInv = 1.0f / det;
@@ -173,7 +131,7 @@ namespace Godot
/// and is usually considered invalid.
/// </summary>
/// <returns>The determinant of the basis matrix.</returns>
- private real_t BasisDeterminant()
+ private readonly real_t BasisDeterminant()
{
return (x.x * y.y) - (x.y * y.x);
}
@@ -185,7 +143,7 @@ namespace Godot
/// <seealso cref="BasisXformInv(Vector2)"/>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
- public Vector2 BasisXform(Vector2 v)
+ public readonly Vector2 BasisXform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v));
}
@@ -200,28 +158,47 @@ namespace Godot
/// <seealso cref="BasisXform(Vector2)"/>
/// <param name="v">A vector to inversely transform.</param>
/// <returns>The inversely transformed vector.</returns>
- public Vector2 BasisXformInv(Vector2 v)
+ public readonly Vector2 BasisXformInv(Vector2 v)
{
return new Vector2(x.Dot(v), y.Dot(v));
}
/// <summary>
+ /// Returns the transform's rotation (in radians).
+ /// </summary>
+ public readonly real_t GetRotation()
+ {
+ return Mathf.Atan2(x.y, x.x);
+ }
+
+ /// <summary>
+ /// Returns the scale.
+ /// </summary>
+ public readonly Vector2 GetScale()
+ {
+ real_t detSign = Mathf.Sign(BasisDeterminant());
+ return new Vector2(x.Length(), detSign * y.Length());
+ }
+
+ /// <summary>
/// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated transform.</returns>
- public Transform2D InterpolateWith(Transform2D transform, real_t weight)
+ public readonly Transform2D InterpolateWith(Transform2D transform, real_t weight)
{
- real_t r1 = Rotation;
- real_t r2 = transform.Rotation;
+ real_t r1 = GetRotation();
+ real_t r2 = transform.GetRotation();
- Vector2 s1 = Scale;
- Vector2 s2 = transform.Scale;
+ Vector2 s1 = GetScale();
+ Vector2 s2 = transform.GetScale();
// Slerp rotation
- var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
- var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
+ (real_t sin1, real_t cos1) = Mathf.SinCos(r1);
+ (real_t sin2, real_t cos2) = Mathf.SinCos(r2);
+ var v1 = new Vector2(cos1, sin1);
+ var v2 = new Vector2(cos2, sin2);
real_t dot = v1.Dot(v2);
@@ -238,7 +215,8 @@ namespace Godot
{
real_t angle = weight * Mathf.Acos(dot);
Vector2 v3 = (v2 - (v1 * dot)).Normalized();
- v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle));
+ (real_t sine, real_t cos) = Mathf.SinCos(angle);
+ v = (v1 * sine) + (v3 * cos);
}
// Extract parameters
@@ -260,14 +238,13 @@ namespace Godot
/// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
/// </summary>
/// <returns>The inverse matrix.</returns>
- public Transform2D Inverse()
+ public readonly Transform2D Inverse()
{
Transform2D inv = this;
// Swap
- real_t temp = inv.x.y;
- inv.x.y = inv.y.x;
- inv.y.x = temp;
+ inv.x.y = y.x;
+ inv.y.x = x.y;
inv.origin = inv.BasisXform(-inv.origin);
@@ -275,11 +252,21 @@ namespace Godot
}
/// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return x.IsFinite() && y.IsFinite() && origin.IsFinite();
+ }
+
+ /// <summary>
/// Returns the transform with the basis orthogonal (90 degrees),
/// and normalized axis vectors (scale of 1 or -1).
/// </summary>
/// <returns>The orthonormalized transform.</returns>
- public Transform2D Orthonormalized()
+ public readonly Transform2D Orthonormalized()
{
Transform2D on = this;
@@ -297,21 +284,37 @@ namespace Godot
}
/// <summary>
- /// Rotates the transform by <paramref name="angle"/> (in radians), using matrix multiplication.
+ /// Rotates the transform by <paramref name="angle"/> (in radians).
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
+ /// </summary>
+ /// <param name="angle">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
+ public readonly Transform2D Rotated(real_t angle)
+ {
+ return this * new Transform2D(angle, new Vector2());
+ }
+
+ /// <summary>
+ /// Rotates the transform by <paramref name="angle"/> (in radians).
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
/// </summary>
/// <param name="angle">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
- public Transform2D Rotated(real_t phi)
+ public readonly Transform2D RotatedLocal(real_t angle)
{
- return this * new Transform2D(phi, new Vector2());
+ return new Transform2D(angle, new Vector2()) * this;
}
/// <summary>
- /// Scales the transform by the given scaling factor, using matrix multiplication.
+ /// Scales the transform by the given scaling factor.
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
/// </summary>
/// <param name="scale">The scale to introduce.</param>
/// <returns>The scaled transformation matrix.</returns>
- public Transform2D Scaled(Vector2 scale)
+ public readonly Transform2D Scaled(Vector2 scale)
{
Transform2D copy = this;
copy.x *= scale;
@@ -320,63 +323,57 @@ namespace Godot
return copy;
}
- private void ScaleBasis(Vector2 scale)
+ /// <summary>
+ /// Scales the transform by the given scaling factor.
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
+ public readonly Transform2D ScaledLocal(Vector2 scale)
{
- x.x *= scale.x;
- x.y *= scale.y;
- y.x *= scale.x;
- y.y *= scale.y;
+ Transform2D copy = this;
+ copy.x *= scale;
+ copy.y *= scale;
+ return copy;
}
- private real_t Tdotx(Vector2 with)
+ private readonly real_t Tdotx(Vector2 with)
{
return (this[0, 0] * with[0]) + (this[1, 0] * with[1]);
}
- private real_t Tdoty(Vector2 with)
+ private readonly real_t Tdoty(Vector2 with)
{
return (this[0, 1] * with[0]) + (this[1, 1] * with[1]);
}
/// <summary>
- /// Translates the transform by the given <paramref name="offset"/>,
- /// relative to the transform's basis vectors.
- ///
- /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
- /// this does not use matrix multiplication.
+ /// Translates the transform by the given <paramref name="offset"/>.
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
/// </summary>
/// <param name="offset">The offset to translate by.</param>
/// <returns>The translated matrix.</returns>
- public Transform2D TranslatedLocal(Vector2 offset)
+ public readonly Transform2D Translated(Vector2 offset)
{
Transform2D copy = this;
- copy.origin += copy.BasisXform(offset);
+ copy.origin += offset;
return copy;
}
/// <summary>
- /// Returns a vector transformed (multiplied) by this transformation matrix.
+ /// Translates the transform by the given <paramref name="offset"/>.
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
/// </summary>
- /// <seealso cref="XformInv(Vector2)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- [Obsolete("Xform is deprecated. Use the multiplication operator (Transform2D * Vector2) instead.")]
- public Vector2 Xform(Vector2 v)
- {
- return new Vector2(Tdotx(v), Tdoty(v)) + origin;
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by the inverse transformation matrix.
- /// </summary>
- /// <seealso cref="Xform(Vector2)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- [Obsolete("XformInv is deprecated. Use the multiplication operator (Vector2 * Transform2D) instead.")]
- public Vector2 XformInv(Vector2 v)
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
+ public readonly Transform2D TranslatedLocal(Vector2 offset)
{
- Vector2 vInv = v - origin;
- return new Vector2(x.Dot(vInv), y.Dot(vInv));
+ Transform2D copy = this;
+ copy.origin += copy.BasisXform(offset);
+ return copy;
}
// Constants
@@ -417,7 +414,7 @@ namespace Godot
/// <summary>
/// Constructs a transformation matrix from the given components.
- /// Arguments are named such that xy is equal to calling x.y
+ /// Arguments are named such that xy is equal to calling <c>x.y</c>.
/// </summary>
/// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param>
/// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param>
@@ -440,13 +437,34 @@ namespace Godot
/// <param name="origin">The origin vector, or column index 2.</param>
public Transform2D(real_t rotation, Vector2 origin)
{
- x.x = y.y = Mathf.Cos(rotation);
- x.y = y.x = Mathf.Sin(rotation);
+ (real_t sin, real_t cos) = Mathf.SinCos(rotation);
+ x.x = y.y = cos;
+ x.y = y.x = sin;
y.x *= -1;
this.origin = origin;
}
/// <summary>
+ /// Constructs a transformation matrix from a <paramref name="rotation"/> value,
+ /// <paramref name="scale"/> vector, <paramref name="skew"/> value, and
+ /// <paramref name="origin"/> vector.
+ /// </summary>
+ /// <param name="rotation">The rotation of the new transform, in radians.</param>
+ /// <param name="scale">The scale of the new transform.</param>
+ /// <param name="skew">The skew of the new transform, in radians.</param>
+ /// <param name="origin">The origin vector, or column index 2.</param>
+ public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin)
+ {
+ (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation);
+ (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew);
+ x.x = rotationCos * scale.x;
+ y.y = rotationSkewCos * scale.y;
+ y.x = -rotationSkewSin * scale.y;
+ x.y = rotationSin * scale.x;
+ this.origin = origin;
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
@@ -472,7 +490,7 @@ namespace Godot
}
/// <summary>
- /// Returns a Vector2 transformed (multiplied) by transformation matrix.
+ /// Returns a Vector2 transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="vector">A Vector2 to transform.</param>
@@ -495,7 +513,7 @@ namespace Godot
}
/// <summary>
- /// Returns a Rect2 transformed (multiplied) by transformation matrix.
+ /// Returns a Rect2 transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="rect">A Rect2 to transform.</param>
@@ -506,7 +524,7 @@ namespace Godot
Vector2 toX = transform.x * rect.Size.x;
Vector2 toY = transform.y * rect.Size.y;
- return new Rect2(pos, rect.Size).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
+ return new Rect2(pos, new Vector2()).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
}
/// <summary>
@@ -522,11 +540,11 @@ namespace Godot
Vector2 to2 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y + rect.Size.y) * transform;
Vector2 to3 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y) * transform;
- return new Rect2(pos, rect.Size).Expand(to1).Expand(to2).Expand(to3);
+ return new Rect2(pos, new Vector2()).Expand(to1).Expand(to2).Expand(to3);
}
/// <summary>
- /// Returns a copy of the given Vector2[] transformed (multiplied) by transformation matrix.
+ /// Returns a copy of the given Vector2[] transformed (multiplied) by the transformation matrix.
/// </summary>
/// <param name="transform">The transformation to apply.</param>
/// <param name="array">A Vector2[] to transform.</param>
@@ -595,9 +613,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- return obj is Transform2D transform2D && Equals(transform2D);
+ return obj is Transform2D other && Equals(other);
}
/// <summary>
@@ -607,7 +625,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are exactly equal.</returns>
- public bool Equals(Transform2D other)
+ public readonly bool Equals(Transform2D other)
{
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
@@ -618,7 +636,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are approximately equal.</returns>
- public bool IsEqualApprox(Transform2D other)
+ public readonly bool IsEqualApprox(Transform2D other)
{
return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
}
@@ -627,7 +645,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Transform2D"/>.
/// </summary>
/// <returns>A hash code for this transform.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
}
@@ -636,7 +654,7 @@ namespace Godot
/// Converts this <see cref="Transform2D"/> to a string.
/// </summary>
/// <returns>A string representation of this transform.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"[X: {x}, Y: {y}, O: {origin}]";
}
@@ -645,7 +663,7 @@ namespace Godot
/// Converts this <see cref="Transform2D"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this transform.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, O: {origin.ToString(format)}]";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 41565bf680..6b2475fc59 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -37,9 +32,12 @@ namespace Godot
/// The fourth column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column vector.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="column"/> is not 0, 1, 2 or 3.
+ /// </exception>
public Vector3 this[int column]
{
- get
+ readonly get
{
switch (column)
{
@@ -52,7 +50,7 @@ namespace Godot
case 3:
return origin;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
set
@@ -72,7 +70,7 @@ namespace Godot
origin = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(column));
}
}
}
@@ -85,7 +83,7 @@ namespace Godot
/// <param name="row">Which row, the matrix vertical position.</param>
public real_t this[int column, int row]
{
- get
+ readonly get
{
if (column == 3)
{
@@ -110,27 +108,27 @@ namespace Godot
/// </summary>
/// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
- public Transform3D AffineInverse()
+ public readonly Transform3D AffineInverse()
{
Basis basisInv = basis.Inverse();
- return new Transform3D(basisInv, basisInv.Xform(-origin));
+ return new Transform3D(basisInv, basisInv * -origin);
}
/// <summary>
- /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
+ /// Returns a transform interpolated between this transform and another
+ /// <paramref name="transform"/> by a given <paramref name="weight"/>
+ /// (on the range of 0.0 to 1.0).
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated transform.</returns>
- public Transform3D InterpolateWith(Transform3D transform, real_t weight)
+ public readonly Transform3D InterpolateWith(Transform3D transform, real_t weight)
{
- /* not sure if very "efficient" but good enough? */
-
- Vector3 sourceScale = basis.Scale;
+ Vector3 sourceScale = basis.GetScale();
Quaternion sourceRotation = basis.GetRotationQuaternion();
Vector3 sourceLocation = origin;
- Vector3 destinationScale = transform.basis.Scale;
+ Vector3 destinationScale = transform.basis.GetScale();
Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
Vector3 destinationLocation = transform.origin;
@@ -149,10 +147,20 @@ namespace Godot
/// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
/// </summary>
/// <returns>The inverse matrix.</returns>
- public Transform3D Inverse()
+ public readonly Transform3D Inverse()
{
Basis basisTr = basis.Transposed();
- return new Transform3D(basisTr, basisTr.Xform(-origin));
+ return new Transform3D(basisTr, basisTr * -origin);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this transform is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return basis.IsFinite() && origin.IsFinite();
}
/// <summary>
@@ -168,7 +176,7 @@ namespace Godot
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
/// <returns>The resulting transform.</returns>
- public Transform3D LookingAt(Vector3 target, Vector3 up)
+ public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
{
Transform3D t = this;
t.SetLookAt(origin, target, up);
@@ -180,33 +188,65 @@ namespace Godot
/// and normalized axis vectors (scale of 1 or -1).
/// </summary>
/// <returns>The orthonormalized transform.</returns>
- public Transform3D Orthonormalized()
+ public readonly Transform3D Orthonormalized()
{
return new Transform3D(basis.Orthonormalized(), origin);
}
/// <summary>
- /// 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.
+ /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians).
+ /// The axis must be a normalized vector.
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
+ /// </summary>
+ /// <param name="axis">The axis to rotate around. Must be normalized.</param>
+ /// <param name="angle">The angle to rotate, in radians.</param>
+ /// <returns>The rotated transformation matrix.</returns>
+ public readonly Transform3D Rotated(Vector3 axis, real_t angle)
+ {
+ return new Transform3D(new Basis(axis, angle), new Vector3()) * this;
+ }
+
+ /// <summary>
+ /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="angle"/> (in radians).
+ /// The axis must be a normalized vector.
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</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)
+ public readonly Transform3D RotatedLocal(Vector3 axis, real_t angle)
{
- return new Transform3D(new Basis(axis, phi), new Vector3()) * this;
+ Basis tmpBasis = new Basis(axis, angle);
+ return new Transform3D(basis * tmpBasis, origin);
}
/// <summary>
- /// Scales the transform by the given 3D scaling factor, using matrix multiplication.
+ /// Scales the transform by the given 3D <paramref name="scale"/> factor.
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
/// </summary>
/// <param name="scale">The scale to introduce.</param>
/// <returns>The scaled transformation matrix.</returns>
- public Transform3D Scaled(Vector3 scale)
+ public readonly Transform3D Scaled(Vector3 scale)
{
return new Transform3D(basis.Scaled(scale), origin * scale);
}
+ /// <summary>
+ /// Scales the transform by the given 3D <paramref name="scale"/> factor.
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
+ /// </summary>
+ /// <param name="scale">The scale to introduce.</param>
+ /// <returns>The scaled transformation matrix.</returns>
+ public readonly Transform3D ScaledLocal(Vector3 scale)
+ {
+ Basis tmpBasis = Basis.FromScale(scale);
+ return new Transform3D(basis * tmpBasis, origin);
+ }
+
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
{
// Make rotation matrix
@@ -231,59 +271,32 @@ namespace Godot
}
/// <summary>
- /// Translates the transform by the given <paramref name="offset"/>,
- /// relative to the transform's basis vectors.
- ///
- /// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
- /// this does not use matrix multiplication.
+ /// Translates the transform by the given <paramref name="offset"/>.
+ /// The operation is done in the parent/global frame, equivalent to
+ /// multiplying the matrix from the left.
/// </summary>
/// <param name="offset">The offset to translate by.</param>
/// <returns>The translated matrix.</returns>
- public Transform3D TranslatedLocal(Vector3 offset)
- {
- return new Transform3D(basis, new Vector3
- (
- origin[0] += basis.Row0.Dot(offset),
- origin[1] += basis.Row1.Dot(offset),
- origin[2] += basis.Row2.Dot(offset)
- ));
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by this transformation matrix.
- /// </summary>
- /// <seealso cref="XformInv(Vector3)"/>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
+ public readonly Transform3D Translated(Vector3 offset)
{
- return new Vector3
- (
- basis.Row0.Dot(v) + origin.x,
- basis.Row1.Dot(v) + origin.y,
- basis.Row2.Dot(v) + origin.z
- );
+ return new Transform3D(basis, origin + offset);
}
/// <summary>
- /// Returns a vector transformed (multiplied) by the transposed transformation matrix.
- ///
- /// Note: This results in a multiplication by the inverse of the
- /// transformation matrix only if it represents a rotation-reflection.
+ /// Translates the transform by the given <paramref name="offset"/>.
+ /// The operation is done in the local frame, equivalent to
+ /// multiplying the matrix from the right.
/// </summary>
- /// <seealso cref="Xform(Vector3)"/>
- /// <param name="v">A vector to inversely transform.</param>
- /// <returns>The inversely transformed vector.</returns>
- public Vector3 XformInv(Vector3 v)
+ /// <param name="offset">The offset to translate by.</param>
+ /// <returns>The translated matrix.</returns>
+ public readonly Transform3D TranslatedLocal(Vector3 offset)
{
- Vector3 vInv = v - origin;
-
- return new Vector3
+ return new Transform3D(basis, new Vector3
(
- (basis.Row0[0] * vInv.x) + (basis.Row1[0] * vInv.y) + (basis.Row2[0] * vInv.z),
- (basis.Row0[1] * vInv.x) + (basis.Row1[1] * vInv.y) + (basis.Row2[1] * vInv.z),
- (basis.Row0[2] * vInv.x) + (basis.Row1[2] * vInv.y) + (basis.Row2[2] * vInv.z)
- );
+ origin[0] + basis.Row0.Dot(offset),
+ origin[1] + basis.Row1.Dot(offset),
+ origin[2] + basis.Row2.Dot(offset)
+ ));
}
// Constants
@@ -329,15 +342,25 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from the given <paramref name="quaternion"/>
- /// and <paramref name="origin"/> vector.
+ /// Constructs a transformation matrix from the given components.
+ /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>.
/// </summary>
- /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param>
- /// <param name="origin">The origin vector, or column index 3.</param>
- public Transform3D(Quaternion quaternion, Vector3 origin)
+ /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param>
+ /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param>
+ /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param>
+ /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param>
+ /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param>
+ public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz)
{
- basis = new Basis(quaternion);
- this.origin = origin;
+ basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz);
+ origin = new Vector3(ox, oy, oz);
}
/// <summary>
@@ -353,6 +376,29 @@ namespace Godot
}
/// <summary>
+ /// Constructs a transformation matrix from the given <paramref name="projection"/>
+ /// by trimming the last row of the projection matrix (<c>projection.x.w</c>,
+ /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c>
+ /// are not copied over).
+ /// </summary>
+ /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param>
+ public Transform3D(Projection projection)
+ {
+ basis = new Basis
+ (
+ projection.x.x, projection.y.x, projection.z.x,
+ projection.x.y, projection.y.y, projection.z.y,
+ projection.x.z, projection.y.z, projection.z.z
+ );
+ origin = new Vector3
+ (
+ projection.w.x,
+ projection.w.y,
+ projection.w.z
+ );
+ }
+
+ /// <summary>
/// Composes these two transformation matrices by multiplying them
/// together. This has the effect of transforming the second transform
/// (the child) by the first transform (the parent).
@@ -362,12 +408,188 @@ namespace Godot
/// <returns>The composed transform.</returns>
public static Transform3D operator *(Transform3D left, Transform3D right)
{
- left.origin = left.Xform(right.origin);
+ left.origin = left * right.origin;
left.basis *= right.basis;
return left;
}
/// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="vector">A Vector3 to transform.</param>
+ /// <returns>The transformed Vector3.</returns>
+ public static Vector3 operator *(Transform3D transform, Vector3 vector)
+ {
+ return new Vector3
+ (
+ transform.basis.Row0.Dot(vector) + transform.origin.x,
+ transform.basis.Row1.Dot(vector) + transform.origin.y,
+ transform.basis.Row2.Dot(vector) + transform.origin.z
+ );
+ }
+
+ /// <summary>
+ /// Returns a Vector3 transformed (multiplied) by the transposed transformation matrix.
+ ///
+ /// Note: This results in a multiplication by the inverse of the
+ /// transformation matrix only if it represents a rotation-reflection.
+ /// </summary>
+ /// <param name="vector">A Vector3 to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Vector3.</returns>
+ public static Vector3 operator *(Vector3 vector, Transform3D transform)
+ {
+ Vector3 vInv = vector - transform.origin;
+
+ return new Vector3
+ (
+ (transform.basis.Row0[0] * vInv.x) + (transform.basis.Row1[0] * vInv.y) + (transform.basis.Row2[0] * vInv.z),
+ (transform.basis.Row0[1] * vInv.x) + (transform.basis.Row1[1] * vInv.y) + (transform.basis.Row2[1] * vInv.z),
+ (transform.basis.Row0[2] * vInv.x) + (transform.basis.Row1[2] * vInv.y) + (transform.basis.Row2[2] * vInv.z)
+ );
+ }
+
+ /// <summary>
+ /// Returns an AABB transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="aabb">An AABB to transform.</param>
+ /// <returns>The transformed AABB.</returns>
+ public static AABB operator *(Transform3D transform, AABB aabb)
+ {
+ Vector3 min = aabb.Position;
+ Vector3 max = aabb.Position + aabb.Size;
+
+ Vector3 tmin = transform.origin;
+ Vector3 tmax = transform.origin;
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ real_t e = transform.basis[i][j] * min[j];
+ real_t f = transform.basis[i][j] * max[j];
+ if (e < f)
+ {
+ tmin[i] += e;
+ tmax[i] += f;
+ }
+ else
+ {
+ tmin[i] += f;
+ tmax[i] += e;
+ }
+ }
+ }
+
+ return new AABB(tmin, tmax - tmin);
+ }
+
+ /// <summary>
+ /// Returns an AABB transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="aabb">An AABB to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed AABB.</returns>
+ public static AABB operator *(AABB aabb, Transform3D transform)
+ {
+ Vector3 pos = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to1 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform;
+ Vector3 to2 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to3 = new Vector3(aabb.Position.x + aabb.Size.x, aabb.Position.y, aabb.Position.z) * transform;
+ Vector3 to4 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to5 = new Vector3(aabb.Position.x, aabb.Position.y + aabb.Size.y, aabb.Position.z) * transform;
+ Vector3 to6 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z + aabb.Size.z) * transform;
+ Vector3 to7 = new Vector3(aabb.Position.x, aabb.Position.y, aabb.Position.z) * transform;
+
+ return new AABB(pos, new Vector3()).Expand(to1).Expand(to2).Expand(to3).Expand(to4).Expand(to5).Expand(to6).Expand(to7);
+ }
+
+ /// <summary>
+ /// Returns a Plane transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="plane">A Plane to transform.</param>
+ /// <returns>The transformed Plane.</returns>
+ public static Plane operator *(Transform3D transform, Plane plane)
+ {
+ Basis bInvTrans = transform.basis.Inverse().Transposed();
+
+ // Transform a single point on the plane.
+ Vector3 point = transform * (plane.Normal * plane.D);
+
+ // Use inverse transpose for correct normals with non-uniform scaling.
+ Vector3 normal = (bInvTrans * plane.Normal).Normalized();
+
+ real_t d = normal.Dot(point);
+ return new Plane(normal, d);
+ }
+
+ /// <summary>
+ /// Returns a Plane transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="plane">A Plane to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Plane.</returns>
+ public static Plane operator *(Plane plane, Transform3D transform)
+ {
+ Transform3D tInv = transform.AffineInverse();
+ Basis bTrans = transform.basis.Transposed();
+
+ // Transform a single point on the plane.
+ Vector3 point = tInv * (plane.Normal * plane.D);
+
+ // Note that instead of precalculating the transpose, an alternative
+ // would be to use the transpose for the basis transform.
+ // However that would be less SIMD friendly (requiring a swizzle).
+ // So the cost is one extra precalced value in the calling code.
+ // This is probably worth it, as this could be used in bottleneck areas. And
+ // where it is not a bottleneck, the non-fast method is fine.
+
+ // Use transpose for correct normals with non-uniform scaling.
+ Vector3 normal = (bTrans * plane.Normal).Normalized();
+
+ real_t d = normal.Dot(point);
+ return new Plane(normal, d);
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector3[] transformed (multiplied) by the transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="array">A Vector3[] to transform.</param>
+ /// <returns>The transformed copy of the Vector3[].</returns>
+ public static Vector3[] operator *(Transform3D transform, Vector3[] array)
+ {
+ Vector3[] newArray = new Vector3[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = transform * array[i];
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="array">A Vector3[] to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed copy of the Vector3[].</returns>
+ public static Vector3[] operator *(Vector3[] array, Transform3D transform)
+ {
+ Vector3[] newArray = new Vector3[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = array[i] * transform;
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the transforms are exactly equal.
/// Note: Due to floating-point precision errors, consider using
/// <see cref="IsEqualApprox"/> instead, which is more reliable.
@@ -401,14 +623,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the transform and the object are exactly equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Transform3D)
- {
- return Equals((Transform3D)obj);
- }
-
- return false;
+ return obj is Transform3D other && Equals(other);
}
/// <summary>
@@ -418,7 +635,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are exactly equal.</returns>
- public bool Equals(Transform3D other)
+ public readonly bool Equals(Transform3D other)
{
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
@@ -429,7 +646,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are approximately equal.</returns>
- public bool IsEqualApprox(Transform3D other)
+ public readonly bool IsEqualApprox(Transform3D other)
{
return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin);
}
@@ -438,7 +655,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Transform3D"/>.
/// </summary>
/// <returns>A hash code for this transform.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return basis.GetHashCode() ^ origin.GetHashCode();
}
@@ -447,7 +664,7 @@ namespace Godot
/// Converts this <see cref="Transform3D"/> to a string.
/// </summary>
/// <returns>A string representation of this transform.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"[X: {basis.x}, Y: {basis.y}, Z: {basis.z}, O: {origin}]";
}
@@ -456,7 +673,7 @@ namespace Godot
/// Converts this <see cref="Transform3D"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this transform.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"[X: {basis.x.ToString(format)}, Y: {basis.y.ToString(format)}, Z: {basis.z.ToString(format)}, O: {origin.ToString(format)}]";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
deleted file mode 100644
index eae8927ceb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace Godot
-{
- /// <summary>
- /// Event arguments for when unhandled exceptions occur.
- /// </summary>
- public class UnhandledExceptionArgs
- {
- /// <summary>
- /// Exception object.
- /// </summary>
- public Exception Exception { get; private set; }
-
- internal UnhandledExceptionArgs(Exception exception)
- {
- Exception = exception;
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
new file mode 100644
index 0000000000..da12309217
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs
@@ -0,0 +1,933 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using Godot.NativeInterop;
+
+namespace Godot;
+
+#nullable enable
+
+[SuppressMessage("ReSharper", "RedundantNameQualifier")]
+public partial struct Variant : IDisposable
+{
+ internal godot_variant.movable NativeVar;
+ private object? _obj;
+ private Disposer? _disposer;
+
+ private sealed class Disposer : IDisposable
+ {
+ private godot_variant.movable _native;
+
+ private WeakReference<IDisposable>? _weakReferenceToSelf;
+
+ public Disposer(in godot_variant.movable nativeVar)
+ {
+ _native = nativeVar;
+ _weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
+ }
+
+ ~Disposer()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ _native.DangerousSelfRef.Dispose();
+
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterDisposable(_weakReferenceToSelf);
+ }
+ }
+ }
+
+ private Variant(in godot_variant nativeVar)
+ {
+ NativeVar = (godot_variant.movable)nativeVar;
+ _obj = null;
+
+ switch (nativeVar.Type)
+ {
+ case Type.Nil:
+ case Type.Bool:
+ case Type.Int:
+ case Type.Float:
+ case Type.Vector2:
+ case Type.Vector2i:
+ case Type.Rect2:
+ case Type.Rect2i:
+ case Type.Vector3:
+ case Type.Vector3i:
+ case Type.Vector4:
+ case Type.Vector4i:
+ case Type.Plane:
+ case Type.Quaternion:
+ case Type.Color:
+ case Type.Rid:
+ _disposer = null;
+ break;
+ default:
+ {
+ _disposer = new Disposer(NativeVar);
+ break;
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ // Explicit name to make it very clear
+ public static Variant CreateTakingOwnershipOfDisposableValue(in godot_variant nativeValueToOwn) =>
+ new(nativeValueToOwn);
+
+ // Explicit name to make it very clear
+ public static Variant CreateCopyingBorrowed(in godot_variant nativeValueToOwn) =>
+ new(NativeFuncs.godotsharp_variant_new_copy(nativeValueToOwn));
+
+ /// <summary>
+ /// Constructs a new <see cref="Godot.NativeInterop.godot_variant"/> from this instance.
+ /// The caller is responsible of disposing the new instance to avoid memory leaks.
+ /// </summary>
+ public godot_variant CopyNativeVariant() =>
+ NativeFuncs.godotsharp_variant_new_copy((godot_variant)NativeVar);
+
+ public void Dispose()
+ {
+ _disposer?.Dispose();
+ NativeVar = default;
+ _obj = null;
+ }
+
+ // TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type.
+ public Type VariantType => NativeVar.DangerousSelfRef.Type;
+
+ public override string ToString() => AsString();
+
+ public object? Obj =>
+ _obj ??= NativeVar.DangerousSelfRef.Type switch
+ {
+ Type.Bool => AsBool(),
+ Type.Int => AsInt64(),
+ Type.Float => AsDouble(),
+ Type.String => AsString(),
+ Type.Vector2 => AsVector2(),
+ Type.Vector2i => AsVector2i(),
+ Type.Rect2 => AsRect2(),
+ Type.Rect2i => AsRect2i(),
+ Type.Vector3 => AsVector3(),
+ Type.Vector3i => AsVector3i(),
+ Type.Transform2d => AsTransform2D(),
+ Type.Vector4 => AsVector4(),
+ Type.Vector4i => AsVector4i(),
+ Type.Plane => AsPlane(),
+ Type.Quaternion => AsQuaternion(),
+ Type.Aabb => AsAABB(),
+ Type.Basis => AsBasis(),
+ Type.Transform3d => AsTransform3D(),
+ Type.Projection => AsProjection(),
+ Type.Color => AsColor(),
+ Type.StringName => AsStringName(),
+ Type.NodePath => AsNodePath(),
+ Type.Rid => AsRID(),
+ Type.Object => AsGodotObject(),
+ Type.Callable => AsCallable(),
+ Type.Signal => AsSignal(),
+ Type.Dictionary => AsGodotDictionary(),
+ Type.Array => AsGodotArray(),
+ Type.PackedByteArray => AsByteArray(),
+ Type.PackedInt32Array => AsInt32Array(),
+ Type.PackedInt64Array => AsInt64Array(),
+ Type.PackedFloat32Array => AsFloat32Array(),
+ Type.PackedFloat64Array => AsFloat64Array(),
+ Type.PackedStringArray => AsStringArray(),
+ Type.PackedVector2Array => AsVector2Array(),
+ Type.PackedVector3Array => AsVector3Array(),
+ Type.PackedColorArray => AsColorArray(),
+ Type.Nil => null,
+ Type.Max or _ =>
+ throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"),
+ };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant From<[MustBeVariant] T>(in T from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFrom(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T As<[MustBeVariant] T>() =>
+ VariantUtils.ConvertTo<T>(NativeVar.DangerousSelfRef);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool AsBool() =>
+ VariantUtils.ConvertToBool((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public char AsChar() =>
+ (char)VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public sbyte AsSByte() =>
+ VariantUtils.ConvertToInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public short AsInt16() =>
+ VariantUtils.ConvertToInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int AsInt32() =>
+ VariantUtils.ConvertToInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long AsInt64() =>
+ VariantUtils.ConvertToInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte AsByte() =>
+ VariantUtils.ConvertToUInt8((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort AsUInt16() =>
+ VariantUtils.ConvertToUInt16((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint AsUInt32() =>
+ VariantUtils.ConvertToUInt32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong AsUInt64() =>
+ VariantUtils.ConvertToUInt64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float AsSingle() =>
+ VariantUtils.ConvertToFloat32((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double AsDouble() =>
+ VariantUtils.ConvertToFloat64((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string AsString() =>
+ VariantUtils.ConvertToString((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2 AsVector2() =>
+ VariantUtils.ConvertToVector2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2i AsVector2i() =>
+ VariantUtils.ConvertToVector2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2 AsRect2() =>
+ VariantUtils.ConvertToRect2((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rect2i AsRect2i() =>
+ VariantUtils.ConvertToRect2i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform2D AsTransform2D() =>
+ VariantUtils.ConvertToTransform2D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3 AsVector3() =>
+ VariantUtils.ConvertToVector3((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3i AsVector3i() =>
+ VariantUtils.ConvertToVector3i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Basis AsBasis() =>
+ VariantUtils.ConvertToBasis((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Quaternion AsQuaternion() =>
+ VariantUtils.ConvertToQuaternion((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Transform3D AsTransform3D() =>
+ VariantUtils.ConvertToTransform3D((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 AsVector4() =>
+ VariantUtils.ConvertToVector4((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4i AsVector4i() =>
+ VariantUtils.ConvertToVector4i((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Projection AsProjection() =>
+ VariantUtils.ConvertToProjection((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public AABB AsAABB() =>
+ VariantUtils.ConvertToAABB((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color AsColor() =>
+ VariantUtils.ConvertToColor((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Plane AsPlane() =>
+ VariantUtils.ConvertToPlane((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Callable AsCallable() =>
+ VariantUtils.ConvertToCallable((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Signal AsSignal() =>
+ VariantUtils.ConvertToSignal((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte[] AsByteArray() =>
+ VariantUtils.ConvertAsPackedByteArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int[] AsInt32Array() =>
+ VariantUtils.ConvertAsPackedInt32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long[] AsInt64Array() =>
+ VariantUtils.ConvertAsPackedInt64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float[] AsFloat32Array() =>
+ VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double[] AsFloat64Array() =>
+ VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string[] AsStringArray() =>
+ VariantUtils.ConvertAsPackedStringArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector2[] AsVector2Array() =>
+ VariantUtils.ConvertAsPackedVector2ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector3[] AsVector3Array() =>
+ VariantUtils.ConvertAsPackedVector3ArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Color[] AsColorArray() =>
+ VariantUtils.ConvertAsPackedColorArrayToSystemArray((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] AsGodotObjectArray<T>()
+ where T : Godot.Object =>
+ VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() =>
+ VariantUtils.ConvertToDictionary<TKey, TValue>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array<T> AsGodotArray<T>() =>
+ VariantUtils.ConvertToArray<T>((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName[] AsSystemArrayOfStringName() =>
+ VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath[] AsSystemArrayOfNodePath() =>
+ VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID[] AsSystemArrayOfRID() =>
+ VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Godot.Object AsGodotObject() =>
+ VariantUtils.ConvertToGodotObject((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public StringName AsStringName() =>
+ VariantUtils.ConvertToStringName((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public NodePath AsNodePath() =>
+ VariantUtils.ConvertToNodePath((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RID AsRID() =>
+ VariantUtils.ConvertToRID((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Dictionary AsGodotDictionary() =>
+ VariantUtils.ConvertToDictionary((godot_variant)NativeVar);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Collections.Array AsGodotArray() =>
+ VariantUtils.ConvertToArray((godot_variant)NativeVar);
+
+ // Explicit conversion operators to supported types
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator bool(Variant from) => from.AsBool();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator char(Variant from) => from.AsChar();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator sbyte(Variant from) => from.AsSByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator short(Variant from) => from.AsInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int(Variant from) => from.AsInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long(Variant from) => from.AsInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte(Variant from) => from.AsByte();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ushort(Variant from) => from.AsUInt16();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator uint(Variant from) => from.AsUInt32();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator ulong(Variant from) => from.AsUInt64();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float(Variant from) => from.AsSingle();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double(Variant from) => from.AsDouble();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string(Variant from) => from.AsString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2(Variant from) => from.AsVector2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2i(Variant from) => from.AsVector2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2(Variant from) => from.AsRect2();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Rect2i(Variant from) => from.AsRect2i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform2D(Variant from) => from.AsTransform2D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3(Variant from) => from.AsVector3();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3i(Variant from) => from.AsVector3i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Basis(Variant from) => from.AsBasis();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Quaternion(Variant from) => from.AsQuaternion();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Transform3D(Variant from) => from.AsTransform3D();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4(Variant from) => from.AsVector4();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector4i(Variant from) => from.AsVector4i();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Projection(Variant from) => from.AsProjection();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator AABB(Variant from) => from.AsAABB();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color(Variant from) => from.AsColor();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Plane(Variant from) => from.AsPlane();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Callable(Variant from) => from.AsCallable();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Signal(Variant from) => from.AsSignal();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator byte[](Variant from) => from.AsByteArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator int[](Variant from) => from.AsInt32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator long[](Variant from) => from.AsInt64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator float[](Variant from) => from.AsFloat32Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator double[](Variant from) => from.AsFloat64Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator string[](Variant from) => from.AsStringArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector2[](Variant from) => from.AsVector2Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Vector3[](Variant from) => from.AsVector3Array();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Color[](Variant from) => from.AsColorArray();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Godot.Object(Variant from) => from.AsGodotObject();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator StringName(Variant from) => from.AsStringName();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator NodePath(Variant from) => from.AsNodePath();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator RID(Variant from) => from.AsRID();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Dictionary(Variant from) => from.AsGodotDictionary();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static explicit operator Collections.Array(Variant from) => from.AsGodotArray();
+
+ // While we provide implicit conversion operators, normal methods are still needed for
+ // casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc).
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(bool from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(char from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(sbyte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(short from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(int from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(long from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(byte from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ushort from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(uint from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(ulong from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(float from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(double from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(string from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Rect2i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform2D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector3i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Basis from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Quaternion from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Transform3D from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4 from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Vector4i from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Projection from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(AABB from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Color from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Plane from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Callable from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Signal from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<byte> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<int> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<long> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<float> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<double> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<string> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector2> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Vector3> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<Color> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object[] from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom<T>(Collections.Array<T> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<StringName> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<NodePath> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Span<RID> from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Godot.Object from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(StringName from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(NodePath from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(RID from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Dictionary from) => from;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Variant CreateFrom(Collections.Array from) => from;
+
+ // Implicit conversion operators
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(bool from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(char from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(sbyte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(short from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(int from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(long from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(byte from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ushort from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(uint from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(ulong from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromInt(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(float from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(double from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromFloat(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(string from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromString(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Rect2i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRect2i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform2D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform2D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector3i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Basis from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBasis(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Quaternion from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromQuaternion(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Transform3D from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4 from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector4i from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Projection from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(AABB from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Color from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromColor(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Plane from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPlane(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Callable from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromCallable(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Signal from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSignal(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(byte[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(int[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(long[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(float[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(double[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(string[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector2[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Vector3[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Color[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Godot.Object[] from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(StringName[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(NodePath[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(RID[] from) =>
+ (Variant)from.AsSpan();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<byte> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedByteArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<int> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<long> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedInt64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<float> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat32Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<double> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedFloat64Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<string> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedStringArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Vector2> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector2Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Vector3> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedVector3Array(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<Color> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<StringName> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<NodePath> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Span<RID> from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Godot.Object from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromGodotObject(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(StringName from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromStringName(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(NodePath from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromNodePath(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(RID from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromRID(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Dictionary from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator Variant(Collections.Array from) =>
+ CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 7bdbe1c28b..07cb34cadd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -44,8 +39,8 @@ namespace Godot
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the given the <paramref name="index"/> is not 0 or 1.
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0 or 1.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
@@ -53,7 +48,7 @@ namespace Godot
/// </value>
public real_t this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -84,7 +79,7 @@ namespace Godot
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
- public void Deconstruct(out real_t x, out real_t y)
+ public readonly void Deconstruct(out real_t x, out real_t y)
{
x = this.x;
y = this.y;
@@ -110,7 +105,7 @@ namespace Godot
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
- public Vector2 Abs()
+ public readonly Vector2 Abs()
{
return new Vector2(Mathf.Abs(x), Mathf.Abs(y));
}
@@ -122,7 +117,7 @@ namespace Godot
/// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>.
/// </summary>
/// <returns>The angle of this vector, in radians.</returns>
- public real_t Angle()
+ public readonly real_t Angle()
{
return Mathf.Atan2(y, x);
}
@@ -132,7 +127,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to compare this vector to.</param>
/// <returns>The angle between the two vectors, in radians.</returns>
- public real_t AngleTo(Vector2 to)
+ public readonly real_t AngleTo(Vector2 to)
{
return Mathf.Atan2(Cross(to), Dot(to));
}
@@ -142,16 +137,16 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to compare this vector to.</param>
/// <returns>The angle between the two vectors, in radians.</returns>
- public real_t AngleToPoint(Vector2 to)
+ public readonly real_t AngleToPoint(Vector2 to)
{
- return Mathf.Atan2(y - to.y, x - to.x);
+ return Mathf.Atan2(to.y - y, to.x - x);
}
/// <summary>
/// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>.
/// </summary>
/// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns>
- public real_t Aspect()
+ public readonly real_t Aspect()
{
return x / y;
}
@@ -161,7 +156,7 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
/// <returns>The bounced vector.</returns>
- public Vector2 Bounce(Vector2 normal)
+ public readonly Vector2 Bounce(Vector2 normal)
{
return -Reflect(normal);
}
@@ -170,7 +165,7 @@ namespace Godot
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
- public Vector2 Ceil()
+ public readonly Vector2 Ceil()
{
return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y));
}
@@ -183,7 +178,7 @@ namespace Godot
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
- public Vector2 Clamp(Vector2 min, Vector2 max)
+ public readonly Vector2 Clamp(Vector2 min, Vector2 max)
{
return new Vector2
(
@@ -197,7 +192,7 @@ namespace Godot
/// </summary>
/// <param name="with">The other vector.</param>
/// <returns>The cross product value.</returns>
- public real_t Cross(Vector2 with)
+ public readonly real_t Cross(Vector2 with)
{
return (x * with.y) - (y * with.x);
}
@@ -211,7 +206,7 @@ namespace Godot
/// <param name="postB">A vector after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
- public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight)
+ public readonly Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight)
{
return new Vector2
(
@@ -221,24 +216,61 @@ namespace Godot
}
/// <summary>
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// It can perform smoother interpolation than <see cref="CubicInterpolate"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="t"></param>
+ /// <param name="preAT"></param>
+ /// <param name="postBT"></param>
+ /// <returns>The interpolated vector.</returns>
+ public readonly Vector2 CubicInterpolateInTime(Vector2 b, Vector2 preA, Vector2 postB, real_t weight, real_t t, real_t preAT, real_t postBT)
+ {
+ return new Vector2
+ (
+ Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT)
+ );
+ }
+
+ /// <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.
+ /// 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)
+ public readonly 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 new Vector2
+ (
+ Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t)
+ );
+ }
- return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ /// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on the 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 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 readonly Vector2 BezierDerivative(Vector2 control1, Vector2 control2, Vector2 end, real_t t)
+ {
+ return new Vector2(
+ Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t)
+ );
}
/// <summary>
@@ -246,7 +278,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to point towards.</param>
/// <returns>The direction from this vector to <paramref name="to"/>.</returns>
- public Vector2 DirectionTo(Vector2 to)
+ public readonly Vector2 DirectionTo(Vector2 to)
{
return new Vector2(to.x - x, to.y - y).Normalized();
}
@@ -258,7 +290,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to use.</param>
/// <returns>The squared distance between the two vectors.</returns>
- public real_t DistanceSquaredTo(Vector2 to)
+ public readonly real_t DistanceSquaredTo(Vector2 to)
{
return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y);
}
@@ -268,7 +300,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector2 to)
+ public readonly real_t DistanceTo(Vector2 to)
{
return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y));
}
@@ -278,7 +310,7 @@ namespace Godot
/// </summary>
/// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
- public real_t Dot(Vector2 with)
+ public readonly real_t Dot(Vector2 with)
{
return (x * with.x) + (y * with.y);
}
@@ -287,7 +319,7 @@ namespace Godot
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
- public Vector2 Floor()
+ public readonly Vector2 Floor()
{
return new Vector2(Mathf.Floor(x), Mathf.Floor(y));
}
@@ -296,16 +328,26 @@ namespace Godot
/// Returns the inverse of this vector. This is the same as <c>new Vector2(1 / v.x, 1 / v.y)</c>.
/// </summary>
/// <returns>The inverse of this vector.</returns>
- public Vector2 Inverse()
+ public readonly Vector2 Inverse()
{
return new Vector2(1 / x, 1 / y);
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
- public bool IsNormalized()
+ public readonly bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
@@ -315,7 +357,7 @@ namespace Godot
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
- public real_t Length()
+ public readonly real_t Length()
{
return Mathf.Sqrt((x * x) + (y * y));
}
@@ -326,7 +368,7 @@ namespace Godot
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
- public real_t LengthSquared()
+ public readonly real_t LengthSquared()
{
return (x * x) + (y * y);
}
@@ -338,7 +380,7 @@ namespace Godot
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
- public Vector2 Lerp(Vector2 to, real_t weight)
+ public readonly Vector2 Lerp(Vector2 to, real_t weight)
{
return new Vector2
(
@@ -348,29 +390,11 @@ namespace Godot
}
/// <summary>
- /// Returns the result of the linear interpolation between
- /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>.
- /// </summary>
- /// <param name="to">The destination vector for interpolation.</param>
- /// <param name="weight">
- /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.
- /// </param>
- /// <returns>The resulting vector of the interpolation.</returns>
- public Vector2 Lerp(Vector2 to, Vector2 weight)
- {
- return new Vector2
- (
- Mathf.Lerp(x, to.x, weight.x),
- Mathf.Lerp(y, to.y, weight.y)
- );
- }
-
- /// <summary>
/// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>.
/// </summary>
/// <param name="length">The length to limit to.</param>
/// <returns>The vector with its length limited.</returns>
- public Vector2 LimitLength(real_t length = 1.0f)
+ public readonly Vector2 LimitLength(real_t length = 1.0f)
{
Vector2 v = this;
real_t l = Length();
@@ -389,7 +413,7 @@ namespace Godot
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
- public Axis MaxAxisIndex()
+ public readonly Axis MaxAxisIndex()
{
return x < y ? Axis.Y : Axis.X;
}
@@ -399,7 +423,7 @@ namespace Godot
/// If both components are equal, this method returns <see cref="Axis.Y"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
- public Axis MinAxisIndex()
+ public readonly Axis MinAxisIndex()
{
return x < y ? Axis.X : Axis.Y;
}
@@ -410,7 +434,7 @@ namespace Godot
/// <param name="to">The vector to move towards.</param>
/// <param name="delta">The amount to move towards by.</param>
/// <returns>The resulting vector.</returns>
- public Vector2 MoveToward(Vector2 to, real_t delta)
+ public readonly Vector2 MoveToward(Vector2 to, real_t delta)
{
Vector2 v = this;
Vector2 vd = to - v;
@@ -425,7 +449,7 @@ namespace Godot
/// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
/// </summary>
/// <returns>A normalized version of the vector.</returns>
- public Vector2 Normalized()
+ public readonly Vector2 Normalized()
{
Vector2 v = this;
v.Normalize();
@@ -440,7 +464,7 @@ namespace Godot
/// <returns>
/// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>.
/// </returns>
- public Vector2 PosMod(real_t mod)
+ public readonly Vector2 PosMod(real_t mod)
{
Vector2 v;
v.x = Mathf.PosMod(x, mod);
@@ -456,7 +480,7 @@ namespace Godot
/// <returns>
/// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components.
/// </returns>
- public Vector2 PosMod(Vector2 modv)
+ public readonly Vector2 PosMod(Vector2 modv)
{
Vector2 v;
v.x = Mathf.PosMod(x, modv.x);
@@ -469,7 +493,7 @@ namespace Godot
/// </summary>
/// <param name="onNormal">The vector to project onto.</param>
/// <returns>The projected vector.</returns>
- public Vector2 Project(Vector2 onNormal)
+ public readonly Vector2 Project(Vector2 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
@@ -479,12 +503,12 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
/// <returns>The reflected vector.</returns>
- public Vector2 Reflect(Vector2 normal)
+ public readonly Vector2 Reflect(Vector2 normal)
{
#if DEBUG
if (!normal.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(normal));
+ throw new ArgumentException("Argument is not normalized.", nameof(normal));
}
#endif
return (2 * Dot(normal) * normal) - this;
@@ -495,13 +519,14 @@ namespace Godot
/// </summary>
/// <param name="angle">The angle to rotate by, in radians.</param>
/// <returns>The rotated vector.</returns>
- public Vector2 Rotated(real_t phi)
+ public readonly Vector2 Rotated(real_t angle)
{
- real_t sine = Mathf.Sin(phi);
- real_t cosi = Mathf.Cos(phi);
- return new Vector2(
- x * cosi - y * sine,
- x * sine + y * cosi);
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2
+ (
+ x * cos - y * sin,
+ x * sin + y * cos
+ );
}
/// <summary>
@@ -509,7 +534,7 @@ namespace Godot
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
/// <returns>The rounded vector.</returns>
- public Vector2 Round()
+ public readonly Vector2 Round()
{
return new Vector2(Mathf.Round(x), Mathf.Round(y));
}
@@ -520,7 +545,7 @@ namespace Godot
/// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
- public Vector2 Sign()
+ public readonly Vector2 Sign()
{
Vector2 v;
v.x = Mathf.Sign(x);
@@ -534,12 +559,12 @@ namespace Godot
///
/// This method also handles interpolating the lengths if the input vectors
/// have different lengths. For the special case of one or both input vectors
- /// having zero length, this method behaves like <see cref="Lerp"/>.
+ /// having zero length, this method behaves like <see cref="Lerp(Vector2, real_t)"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
- public Vector2 Slerp(Vector2 to, real_t weight)
+ public readonly Vector2 Slerp(Vector2 to, real_t weight)
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
@@ -559,7 +584,7 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to slide on.</param>
/// <returns>The slid vector.</returns>
- public Vector2 Slide(Vector2 normal)
+ public readonly Vector2 Slide(Vector2 normal)
{
return this - (normal * Dot(normal));
}
@@ -570,7 +595,7 @@ namespace Godot
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
/// <returns>The snapped vector.</returns>
- public Vector2 Snapped(Vector2 step)
+ public readonly Vector2 Snapped(Vector2 step)
{
return new Vector2(Mathf.Snapped(x, step.x), Mathf.Snapped(y, step.y));
}
@@ -580,7 +605,7 @@ namespace Godot
/// compared to the original, with the same length.
/// </summary>
/// <returns>The perpendicular vector.</returns>
- public Vector2 Orthogonal()
+ public readonly Vector2 Orthogonal()
{
return new Vector2(y, -x);
}
@@ -644,16 +669,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector2"/> from an existing <see cref="Vector2"/>.
- /// </summary>
- /// <param name="v">The existing <see cref="Vector2"/>.</param>
- public Vector2(Vector2 v)
- {
- x = v.x;
- y = v.y;
- }
-
- /// <summary>
/// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing
/// <c>Vector2(Mathf.Cos(angle), Mathf.Sin(angle))</c> or <c>Vector2.Right.Rotated(angle)</c>.
/// </summary>
@@ -661,7 +676,8 @@ namespace Godot
/// <returns>The resulting vector.</returns>
public static Vector2 FromAngle(real_t angle)
{
- return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
+ (real_t sin, real_t cos) = Mathf.SinCos(angle);
+ return new Vector2(cos, sin);
}
/// <summary>
@@ -938,13 +954,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Vector2)
- {
- return Equals((Vector2)obj);
- }
- return false;
+ return obj is Vector2 other && Equals(other);
}
/// <summary>
@@ -954,7 +966,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are exactly equal.</returns>
- public bool Equals(Vector2 other)
+ public readonly bool Equals(Vector2 other)
{
return x == other.x && y == other.y;
}
@@ -965,16 +977,28 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector to compare.</param>
/// <returns>Whether or not the vectors are approximately equal.</returns>
- public bool IsEqualApprox(Vector2 other)
+ public readonly bool IsEqualApprox(Vector2 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector2"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode();
}
@@ -983,7 +1007,7 @@ namespace Godot
/// Converts this <see cref="Vector2"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({x}, {y})";
}
@@ -992,7 +1016,7 @@ namespace Godot
/// Converts this <see cref="Vector2"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index b61954a84c..740fedec66 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -44,8 +39,8 @@ namespace Godot
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the given the <paramref name="index"/> is not 0 or 1.
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0 or 1.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
@@ -53,7 +48,7 @@ namespace Godot
/// </value>
public int this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -62,7 +57,7 @@ namespace Godot
case 1:
return y;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -76,7 +71,7 @@ namespace Godot
y = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -84,7 +79,7 @@ namespace Godot
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
- public void Deconstruct(out int x, out int y)
+ public readonly void Deconstruct(out int x, out int y)
{
x = this.x;
y = this.y;
@@ -94,48 +89,16 @@ namespace Godot
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
- public Vector2i Abs()
+ public readonly Vector2i Abs()
{
return new Vector2i(Mathf.Abs(x), Mathf.Abs(y));
}
/// <summary>
- /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
- ///
- /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
- /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>.
- /// </summary>
- /// <returns>The angle of this vector, in radians.</returns>
- public real_t Angle()
- {
- return Mathf.Atan2(y, x);
- }
-
- /// <summary>
- /// Returns the angle to the given vector, in radians.
- /// </summary>
- /// <param name="to">The other vector to compare this vector to.</param>
- /// <returns>The angle between the two vectors, in radians.</returns>
- public real_t AngleTo(Vector2i to)
- {
- return Mathf.Atan2(Cross(to), Dot(to));
- }
-
- /// <summary>
- /// Returns the angle between the line connecting the two points and the X axis, in radians.
- /// </summary>
- /// <param name="to">The other vector to compare this vector to.</param>
- /// <returns>The angle between the two vectors, in radians.</returns>
- public real_t AngleToPoint(Vector2i to)
- {
- return Mathf.Atan2(y - to.y, x - to.x);
- }
-
- /// <summary>
/// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>.
/// </summary>
/// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns>
- public real_t Aspect()
+ public readonly real_t Aspect()
{
return x / (real_t)y;
}
@@ -148,7 +111,7 @@ namespace Godot
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
- public Vector2i Clamp(Vector2i min, Vector2i max)
+ public readonly Vector2i Clamp(Vector2i min, Vector2i max)
{
return new Vector2i
(
@@ -158,53 +121,11 @@ namespace Godot
}
/// <summary>
- /// Returns the cross product of this vector and <paramref name="with"/>.
- /// </summary>
- /// <param name="with">The other vector.</param>
- /// <returns>The cross product vector.</returns>
- public int Cross(Vector2i with)
- {
- return x * with.y - y * with.x;
- }
-
- /// <summary>
- /// Returns the squared distance between this vector and <paramref name="to"/>.
- /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
- /// you need to compare vectors or need the squared distance for some formula.
- /// </summary>
- /// <param name="to">The other vector to use.</param>
- /// <returns>The squared distance between the two vectors.</returns>
- public int DistanceSquaredTo(Vector2i to)
- {
- return (to - this).LengthSquared();
- }
-
- /// <summary>
- /// Returns the distance between this vector and <paramref name="to"/>.
- /// </summary>
- /// <param name="to">The other vector to use.</param>
- /// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector2i to)
- {
- return (to - this).Length();
- }
-
- /// <summary>
- /// Returns the dot product of this vector and <paramref name="with"/>.
- /// </summary>
- /// <param name="with">The other vector to use.</param>
- /// <returns>The dot product of the two vectors.</returns>
- public int Dot(Vector2i with)
- {
- return x * with.x + y * with.y;
- }
-
- /// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
- public real_t Length()
+ public readonly real_t Length()
{
int x2 = x * x;
int y2 = y * y;
@@ -218,7 +139,7 @@ namespace Godot
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
- public int LengthSquared()
+ public readonly int LengthSquared()
{
int x2 = x * x;
int y2 = y * y;
@@ -231,7 +152,7 @@ namespace Godot
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
- public Axis MaxAxisIndex()
+ public readonly Axis MaxAxisIndex()
{
return x < y ? Axis.Y : Axis.X;
}
@@ -241,50 +162,18 @@ namespace Godot
/// If both components are equal, this method returns <see cref="Axis.Y"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
- public Axis MinAxisIndex()
+ public readonly Axis MinAxisIndex()
{
return x < y ? Axis.X : Axis.Y;
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
- /// and <paramref name="mod"/>.
- /// </summary>
- /// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>
- /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>.
- /// </returns>
- public Vector2i PosMod(int mod)
- {
- Vector2i v = this;
- v.x = Mathf.PosMod(v.x, mod);
- v.y = Mathf.PosMod(v.y, mod);
- return v;
- }
-
- /// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
- /// and <paramref name="modv"/>'s components.
- /// </summary>
- /// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>
- /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components.
- /// </returns>
- public Vector2i PosMod(Vector2i modv)
- {
- Vector2i v = this;
- v.x = Mathf.PosMod(v.x, modv.x);
- v.y = Mathf.PosMod(v.y, modv.y);
- return v;
- }
-
- /// <summary>
/// Returns a vector with each component set to one or negative one, depending
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
- public Vector2i Sign()
+ public readonly Vector2i Sign()
{
Vector2i v = this;
v.x = Mathf.Sign(v.x);
@@ -292,16 +181,6 @@ namespace Godot
return v;
}
- /// <summary>
- /// Returns a perpendicular vector rotated 90 degrees counter-clockwise
- /// compared to the original, with the same length.
- /// </summary>
- /// <returns>The perpendicular vector.</returns>
- public Vector2i Orthogonal()
- {
- return new Vector2i(y, -x);
- }
-
// Constants
private static readonly Vector2i _zero = new Vector2i(0, 0);
private static readonly Vector2i _one = new Vector2i(1, 1);
@@ -355,27 +234,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2i"/>.
- /// </summary>
- /// <param name="vi">The existing <see cref="Vector2i"/>.</param>
- public Vector2i(Vector2i vi)
- {
- this.x = vi.x;
- this.y = vi.y;
- }
-
- /// <summary>
- /// Constructs a new <see cref="Vector2i"/> from an existing <see cref="Vector2"/>
- /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
- /// </summary>
- /// <param name="v">The <see cref="Vector2"/> to convert.</param>
- public Vector2i(Vector2 v)
- {
- this.x = Mathf.RoundToInt(v.x);
- this.y = Mathf.RoundToInt(v.y);
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector2i"/>
/// with the components of the given <see cref="Vector2i"/>.
/// </summary>
@@ -493,7 +351,7 @@ namespace Godot
/// with the components of the given <see langword="int"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
- /// Consider using <see cref="PosMod(int)"/> instead
+ /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
/// if you want to handle negative numbers.
/// </summary>
/// <example>
@@ -516,7 +374,7 @@ namespace Godot
/// with the components of the given <see cref="Vector2i"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
- /// Consider using <see cref="PosMod(Vector2i)"/> instead
+ /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
/// if you want to handle negative numbers.
/// </summary>
/// <example>
@@ -535,34 +393,6 @@ namespace Godot
}
/// <summary>
- /// Performs a bitwise AND operation with this <see cref="Vector2i"/>
- /// and the given <see langword="int"/>.
- /// </summary>
- /// <param name="vec">The vector to AND with.</param>
- /// <param name="and">The integer to AND with.</param>
- /// <returns>The result of the bitwise AND.</returns>
- public static Vector2i operator &(Vector2i vec, int and)
- {
- vec.x &= and;
- vec.y &= and;
- return vec;
- }
-
- /// <summary>
- /// Performs a bitwise AND operation with this <see cref="Vector2i"/>
- /// and the given <see cref="Vector2i"/>.
- /// </summary>
- /// <param name="vec">The left vector to AND with.</param>
- /// <param name="andv">The right vector to AND with.</param>
- /// <returns>The result of the bitwise AND.</returns>
- public static Vector2i operator &(Vector2i vec, Vector2i andv)
- {
- vec.x &= andv.x;
- vec.y &= andv.y;
- return vec;
- }
-
- /// <summary>
/// Returns <see langword="true"/> if the vectors are equal.
/// </summary>
/// <param name="left">The left vector.</param>
@@ -679,7 +509,10 @@ namespace Godot
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector2i(Vector2 value)
{
- return new Vector2i(value);
+ return new Vector2i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y)
+ );
}
/// <summary>
@@ -688,14 +521,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Vector2i)
- {
- return Equals((Vector2i)obj);
- }
-
- return false;
+ return obj is Vector2i other && Equals(other);
}
/// <summary>
@@ -703,7 +531,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are equal.</returns>
- public bool Equals(Vector2i other)
+ public readonly bool Equals(Vector2i other)
{
return x == other.x && y == other.y;
}
@@ -712,7 +540,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Vector2i"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode();
}
@@ -721,7 +549,7 @@ namespace Godot
/// Converts this <see cref="Vector2i"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({x}, {y})";
}
@@ -730,7 +558,7 @@ namespace Godot
/// Converts this <see cref="Vector2i"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 480165d44a..b017ba5853 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -53,8 +48,8 @@ namespace Godot
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the given the <paramref name="index"/> is not 0, 1 or 2.
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0, 1 or 2.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
@@ -63,7 +58,7 @@ namespace Godot
/// </value>
public real_t this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -99,7 +94,7 @@ namespace Godot
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
- public void Deconstruct(out real_t x, out real_t y, out real_t z)
+ public readonly void Deconstruct(out real_t x, out real_t y, out real_t z)
{
x = this.x;
y = this.y;
@@ -127,7 +122,7 @@ namespace Godot
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
- public Vector3 Abs()
+ public readonly Vector3 Abs()
{
return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
@@ -137,7 +132,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to compare this vector to.</param>
/// <returns>The unsigned angle between the two vectors, in radians.</returns>
- public real_t AngleTo(Vector3 to)
+ public readonly real_t AngleTo(Vector3 to)
{
return Mathf.Atan2(Cross(to).Length(), Dot(to));
}
@@ -147,7 +142,7 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to bounce off. Must be normalized.</param>
/// <returns>The bounced vector.</returns>
- public Vector3 Bounce(Vector3 normal)
+ public readonly Vector3 Bounce(Vector3 normal)
{
return -Reflect(normal);
}
@@ -156,7 +151,7 @@ namespace Godot
/// Returns a new vector with all components rounded up (towards positive infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
- public Vector3 Ceil()
+ public readonly Vector3 Ceil()
{
return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z));
}
@@ -169,7 +164,7 @@ namespace Godot
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
- public Vector3 Clamp(Vector3 min, Vector3 max)
+ public readonly Vector3 Clamp(Vector3 min, Vector3 max)
{
return new Vector3
(
@@ -184,7 +179,7 @@ namespace Godot
/// </summary>
/// <param name="with">The other vector.</param>
/// <returns>The cross product vector.</returns>
- public Vector3 Cross(Vector3 with)
+ public readonly Vector3 Cross(Vector3 with)
{
return new Vector3
(
@@ -203,7 +198,7 @@ namespace Godot
/// <param name="postB">A vector after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
- public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight)
+ public readonly Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight)
{
return new Vector3
(
@@ -214,24 +209,64 @@ namespace Godot
}
/// <summary>
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// It can perform smoother interpolation than <see cref="CubicInterpolate"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="t"></param>
+ /// <param name="preAT"></param>
+ /// <param name="postBT"></param>
+ /// <returns>The interpolated vector.</returns>
+ public readonly Vector3 CubicInterpolateInTime(Vector3 b, Vector3 preA, Vector3 postB, real_t weight, real_t t, real_t preAT, real_t postBT)
+ {
+ return new Vector3
+ (
+ Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(z, b.z, preA.z, postB.z, weight, t, preAT, postBT)
+ );
+ }
+
+ /// <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.
+ /// 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)
+ public readonly 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 new Vector3
+ (
+ Mathf.BezierInterpolate(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierInterpolate(y, control1.y, control2.y, end.y, t),
+ Mathf.BezierInterpolate(z, control1.z, control2.z, end.z, t)
+ );
+ }
- return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ /// <summary>
+ /// Returns the derivative at the given <paramref name="t"/> on the 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 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 readonly Vector3 BezierDerivative(Vector3 control1, Vector3 control2, Vector3 end, real_t t)
+ {
+ return new Vector3(
+ Mathf.BezierDerivative(x, control1.x, control2.x, end.x, t),
+ Mathf.BezierDerivative(y, control1.y, control2.y, end.y, t),
+ Mathf.BezierDerivative(z, control1.z, control2.z, end.y, t)
+ );
}
/// <summary>
@@ -239,7 +274,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to point towards.</param>
/// <returns>The direction from this vector to <paramref name="to"/>.</returns>
- public Vector3 DirectionTo(Vector3 to)
+ public readonly Vector3 DirectionTo(Vector3 to)
{
return new Vector3(to.x - x, to.y - y, to.z - z).Normalized();
}
@@ -251,7 +286,7 @@ namespace Godot
/// </summary>
/// <param name="to">The other vector to use.</param>
/// <returns>The squared distance between the two vectors.</returns>
- public real_t DistanceSquaredTo(Vector3 to)
+ public readonly real_t DistanceSquaredTo(Vector3 to)
{
return (to - this).LengthSquared();
}
@@ -262,7 +297,7 @@ namespace Godot
/// <seealso cref="DistanceSquaredTo(Vector3)"/>
/// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector3 to)
+ public readonly real_t DistanceTo(Vector3 to)
{
return (to - this).Length();
}
@@ -272,7 +307,7 @@ namespace Godot
/// </summary>
/// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
- public real_t Dot(Vector3 with)
+ public readonly real_t Dot(Vector3 with)
{
return (x * with.x) + (y * with.y) + (z * with.z);
}
@@ -281,7 +316,7 @@ namespace Godot
/// Returns a new vector with all components rounded down (towards negative infinity).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
- public Vector3 Floor()
+ public readonly Vector3 Floor()
{
return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z));
}
@@ -290,16 +325,26 @@ namespace Godot
/// Returns the inverse of this vector. This is the same as <c>new Vector3(1 / v.x, 1 / v.y, 1 / v.z)</c>.
/// </summary>
/// <returns>The inverse of this vector.</returns>
- public Vector3 Inverse()
+ public readonly Vector3 Inverse()
{
return new Vector3(1 / x, 1 / y, 1 / z);
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z);
+ }
+
+ /// <summary>
/// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
/// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
- public bool IsNormalized()
+ public readonly bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
}
@@ -309,7 +354,7 @@ namespace Godot
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
- public real_t Length()
+ public readonly real_t Length()
{
real_t x2 = x * x;
real_t y2 = y * y;
@@ -324,7 +369,7 @@ namespace Godot
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
- public real_t LengthSquared()
+ public readonly real_t LengthSquared()
{
real_t x2 = x * x;
real_t y2 = y * y;
@@ -340,7 +385,7 @@ namespace Godot
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
- public Vector3 Lerp(Vector3 to, real_t weight)
+ public readonly Vector3 Lerp(Vector3 to, real_t weight)
{
return new Vector3
(
@@ -351,28 +396,11 @@ namespace Godot
}
/// <summary>
- /// Returns the result of the linear interpolation between
- /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>.
- /// </summary>
- /// <param name="to">The destination vector for interpolation.</param>
- /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting vector of the interpolation.</returns>
- public Vector3 Lerp(Vector3 to, Vector3 weight)
- {
- return new Vector3
- (
- Mathf.Lerp(x, to.x, weight.x),
- Mathf.Lerp(y, to.y, weight.y),
- Mathf.Lerp(z, to.z, weight.z)
- );
- }
-
- /// <summary>
/// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>.
/// </summary>
/// <param name="length">The length to limit to.</param>
/// <returns>The vector with its length limited.</returns>
- public Vector3 LimitLength(real_t length = 1.0f)
+ public readonly Vector3 LimitLength(real_t length = 1.0f)
{
Vector3 v = this;
real_t l = Length();
@@ -391,7 +419,7 @@ namespace Godot
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
- public Axis MaxAxisIndex()
+ public readonly Axis MaxAxisIndex()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
@@ -401,7 +429,7 @@ namespace Godot
/// If all components are equal, this method returns <see cref="Axis.Z"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
- public Axis MinAxisIndex()
+ public readonly Axis MinAxisIndex()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
@@ -412,7 +440,7 @@ namespace Godot
/// <param name="to">The vector to move towards.</param>
/// <param name="delta">The amount to move towards by.</param>
/// <returns>The resulting vector.</returns>
- public Vector3 MoveToward(Vector3 to, real_t delta)
+ public readonly Vector3 MoveToward(Vector3 to, real_t delta)
{
Vector3 v = this;
Vector3 vd = to - v;
@@ -427,7 +455,7 @@ namespace Godot
/// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
/// </summary>
/// <returns>A normalized version of the vector.</returns>
- public Vector3 Normalized()
+ public readonly Vector3 Normalized()
{
Vector3 v = this;
v.Normalize();
@@ -439,7 +467,7 @@ namespace Godot
/// </summary>
/// <param name="with">The other vector.</param>
/// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns>
- public Basis Outer(Vector3 with)
+ public readonly Basis Outer(Vector3 with)
{
return new Basis(
x * with.x, x * with.y, x * with.z,
@@ -456,7 +484,7 @@ namespace Godot
/// <returns>
/// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>.
/// </returns>
- public Vector3 PosMod(real_t mod)
+ public readonly Vector3 PosMod(real_t mod)
{
Vector3 v;
v.x = Mathf.PosMod(x, mod);
@@ -473,7 +501,7 @@ namespace Godot
/// <returns>
/// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components.
/// </returns>
- public Vector3 PosMod(Vector3 modv)
+ public readonly Vector3 PosMod(Vector3 modv)
{
Vector3 v;
v.x = Mathf.PosMod(x, modv.x);
@@ -487,7 +515,7 @@ namespace Godot
/// </summary>
/// <param name="onNormal">The vector to project onto.</param>
/// <returns>The projected vector.</returns>
- public Vector3 Project(Vector3 onNormal)
+ public readonly Vector3 Project(Vector3 onNormal)
{
return onNormal * (Dot(onNormal) / onNormal.LengthSquared());
}
@@ -497,12 +525,12 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
/// <returns>The reflected vector.</returns>
- public Vector3 Reflect(Vector3 normal)
+ public readonly Vector3 Reflect(Vector3 normal)
{
#if DEBUG
if (!normal.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(normal));
+ throw new ArgumentException("Argument is not normalized.", nameof(normal));
}
#endif
return (2.0f * Dot(normal) * normal) - this;
@@ -515,15 +543,15 @@ namespace Godot
/// <param name="axis">The vector to rotate around. Must be normalized.</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)
+ public readonly Vector3 Rotated(Vector3 axis, real_t angle)
{
#if DEBUG
if (!axis.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(axis));
+ throw new ArgumentException("Argument is not normalized.", nameof(axis));
}
#endif
- return new Basis(axis, phi).Xform(this);
+ return new Basis(axis, angle) * this;
}
/// <summary>
@@ -531,7 +559,7 @@ namespace Godot
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
/// <returns>The rounded vector.</returns>
- public Vector3 Round()
+ public readonly Vector3 Round()
{
return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
}
@@ -542,7 +570,7 @@ namespace Godot
/// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
- public Vector3 Sign()
+ public readonly Vector3 Sign()
{
Vector3 v;
v.x = Mathf.Sign(x);
@@ -560,7 +588,7 @@ namespace Godot
/// <param name="to">The other vector to compare this vector to.</param>
/// <param name="axis">The reference axis to use for the angle sign.</param>
/// <returns>The signed angle between the two vectors, in radians.</returns>
- public real_t SignedAngleTo(Vector3 to, Vector3 axis)
+ public readonly real_t SignedAngleTo(Vector3 to, Vector3 axis)
{
Vector3 crossTo = Cross(to);
real_t unsignedAngle = Mathf.Atan2(crossTo.Length(), Dot(to));
@@ -574,12 +602,12 @@ namespace Godot
///
/// This method also handles interpolating the lengths if the input vectors
/// have different lengths. For the special case of one or both input vectors
- /// having zero length, this method behaves like <see cref="Lerp"/>.
+ /// having zero length, this method behaves like <see cref="Lerp(Vector3, real_t)"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
- public Vector3 Slerp(Vector3 to, real_t weight)
+ public readonly Vector3 Slerp(Vector3 to, real_t weight)
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
@@ -599,7 +627,7 @@ namespace Godot
/// </summary>
/// <param name="normal">The normal vector defining the plane to slide on.</param>
/// <returns>The slid vector.</returns>
- public Vector3 Slide(Vector3 normal)
+ public readonly Vector3 Slide(Vector3 normal)
{
return this - (normal * Dot(normal));
}
@@ -610,7 +638,7 @@ namespace Godot
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
/// <returns>The snapped vector.</returns>
- public Vector3 Snapped(Vector3 step)
+ public readonly Vector3 Snapped(Vector3 step)
{
return new Vector3
(
@@ -620,22 +648,6 @@ namespace Godot
);
}
- /// <summary>
- /// Returns a diagonal matrix with the vector as main diagonal.
- ///
- /// This is equivalent to a <see cref="Basis"/> with no rotation or shearing and
- /// this vector's components set as the scale.
- /// </summary>
- /// <returns>A <see cref="Basis"/> with the vector as its main diagonal.</returns>
- public Basis ToDiagonalMatrix()
- {
- return new Basis(
- x, 0, 0,
- 0, y, 0,
- 0, 0, z
- );
- }
-
// Constants
private static readonly Vector3 _zero = new Vector3(0, 0, 0);
private static readonly Vector3 _one = new Vector3(1, 1, 1);
@@ -713,17 +725,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector3"/> from an existing <see cref="Vector3"/>.
- /// </summary>
- /// <param name="v">The existing <see cref="Vector3"/>.</param>
- public Vector3(Vector3 v)
- {
- x = v.x;
- y = v.y;
- z = v.z;
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector3"/>
/// with the components of the given <see cref="Vector3"/>.
/// </summary>
@@ -1023,14 +1024,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Vector3)
- {
- return Equals((Vector3)obj);
- }
-
- return false;
+ return obj is Vector3 other && Equals(other);
}
/// <summary>
@@ -1040,7 +1036,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are exactly equal.</returns>
- public bool Equals(Vector3 other)
+ public readonly bool Equals(Vector3 other)
{
return x == other.x && y == other.y && z == other.z;
}
@@ -1051,16 +1047,28 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector to compare.</param>
/// <returns>Whether or not the vectors are approximately equal.</returns>
- public bool IsEqualApprox(Vector3 other)
+ public readonly bool IsEqualApprox(Vector3 other)
{
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
}
/// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z);
+ }
+
+ /// <summary>
/// Serves as the hash function for <see cref="Vector3"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
}
@@ -1069,7 +1077,7 @@ namespace Godot
/// Converts this <see cref="Vector3"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({x}, {y}, {z})";
}
@@ -1078,7 +1086,7 @@ namespace Godot
/// Converts this <see cref="Vector3"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index 0d4894f206..de0c6d27e7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -1,8 +1,3 @@
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
using System;
using System.Runtime.InteropServices;
@@ -53,8 +48,8 @@ namespace Godot
/// <summary>
/// Access vector components using their <paramref name="index"/>.
/// </summary>
- /// <exception cref="IndexOutOfRangeException">
- /// Thrown when the given the <paramref name="index"/> is not 0, 1 or 2.
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0, 1 or 2.
/// </exception>
/// <value>
/// <c>[0]</c> is equivalent to <see cref="x"/>,
@@ -63,7 +58,7 @@ namespace Godot
/// </value>
public int this[int index]
{
- get
+ readonly get
{
switch (index)
{
@@ -74,7 +69,7 @@ namespace Godot
case 2:
return z;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
set
@@ -91,7 +86,7 @@ namespace Godot
z = value;
return;
default:
- throw new IndexOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
@@ -99,7 +94,7 @@ namespace Godot
/// <summary>
/// Helper method for deconstruction into a tuple.
/// </summary>
- public void Deconstruct(out int x, out int y, out int z)
+ public readonly void Deconstruct(out int x, out int y, out int z)
{
x = this.x;
y = this.y;
@@ -110,7 +105,7 @@ namespace Godot
/// Returns a new vector with all components in absolute values (i.e. positive).
/// </summary>
/// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
- public Vector3i Abs()
+ public readonly Vector3i Abs()
{
return new Vector3i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z));
}
@@ -123,7 +118,7 @@ namespace Godot
/// <param name="min">The vector with minimum allowed values.</param>
/// <param name="max">The vector with maximum allowed values.</param>
/// <returns>The vector with all components clamped.</returns>
- public Vector3i Clamp(Vector3i min, Vector3i max)
+ public readonly Vector3i Clamp(Vector3i min, Vector3i max)
{
return new Vector3i
(
@@ -134,44 +129,11 @@ namespace Godot
}
/// <summary>
- /// Returns the squared distance between this vector and <paramref name="to"/>.
- /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
- /// you need to compare vectors or need the squared distance for some formula.
- /// </summary>
- /// <param name="to">The other vector to use.</param>
- /// <returns>The squared distance between the two vectors.</returns>
- public int DistanceSquaredTo(Vector3i to)
- {
- return (to - this).LengthSquared();
- }
-
- /// <summary>
- /// Returns the distance between this vector and <paramref name="to"/>.
- /// </summary>
- /// <seealso cref="DistanceSquaredTo(Vector3i)"/>
- /// <param name="to">The other vector to use.</param>
- /// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector3i to)
- {
- return (to - this).Length();
- }
-
- /// <summary>
- /// Returns the dot product of this vector and <paramref name="with"/>.
- /// </summary>
- /// <param name="with">The other vector to use.</param>
- /// <returns>The dot product of the two vectors.</returns>
- public int Dot(Vector3i with)
- {
- return x * with.x + y * with.y + z * with.z;
- }
-
- /// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
/// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
- public real_t Length()
+ public readonly real_t Length()
{
int x2 = x * x;
int y2 = y * y;
@@ -186,7 +148,7 @@ namespace Godot
/// you need to compare vectors or need the squared length for some formula.
/// </summary>
/// <returns>The squared length of this vector.</returns>
- public int LengthSquared()
+ public readonly int LengthSquared()
{
int x2 = x * x;
int y2 = y * y;
@@ -200,7 +162,7 @@ namespace Godot
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
/// <returns>The index of the highest axis.</returns>
- public Axis MaxAxisIndex()
+ public readonly Axis MaxAxisIndex()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
@@ -210,52 +172,18 @@ namespace Godot
/// If all components are equal, this method returns <see cref="Axis.Z"/>.
/// </summary>
/// <returns>The index of the lowest axis.</returns>
- public Axis MinAxisIndex()
+ public readonly Axis MinAxisIndex()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
- /// and <paramref name="mod"/>.
- /// </summary>
- /// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>
- /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>.
- /// </returns>
- public Vector3i PosMod(int mod)
- {
- Vector3i v = this;
- v.x = Mathf.PosMod(v.x, mod);
- v.y = Mathf.PosMod(v.y, mod);
- v.z = Mathf.PosMod(v.z, mod);
- return v;
- }
-
- /// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
- /// and <paramref name="modv"/>'s components.
- /// </summary>
- /// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>
- /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components.
- /// </returns>
- public Vector3i PosMod(Vector3i modv)
- {
- Vector3i v = this;
- v.x = Mathf.PosMod(v.x, modv.x);
- v.y = Mathf.PosMod(v.y, modv.y);
- v.z = Mathf.PosMod(v.z, modv.z);
- return v;
- }
-
- /// <summary>
/// Returns a vector with each component set to one or negative one, depending
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
/// </summary>
/// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
- public Vector3i Sign()
+ public readonly Vector3i Sign()
{
Vector3i v = this;
v.x = Mathf.Sign(v.x);
@@ -335,29 +263,6 @@ namespace Godot
}
/// <summary>
- /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3i"/>.
- /// </summary>
- /// <param name="vi">The existing <see cref="Vector3i"/>.</param>
- public Vector3i(Vector3i vi)
- {
- this.x = vi.x;
- this.y = vi.y;
- this.z = vi.z;
- }
-
- /// <summary>
- /// Constructs a new <see cref="Vector3i"/> from an existing <see cref="Vector3"/>
- /// by rounding the components via <see cref="Mathf.RoundToInt(real_t)"/>.
- /// </summary>
- /// <param name="v">The <see cref="Vector3"/> to convert.</param>
- public Vector3i(Vector3 v)
- {
- this.x = Mathf.RoundToInt(v.x);
- this.y = Mathf.RoundToInt(v.y);
- this.z = Mathf.RoundToInt(v.z);
- }
-
- /// <summary>
/// Adds each component of the <see cref="Vector3i"/>
/// with the components of the given <see cref="Vector3i"/>.
/// </summary>
@@ -483,7 +388,7 @@ namespace Godot
/// with the components of the given <see langword="int"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
- /// Consider using <see cref="PosMod(int)"/> instead
+ /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
/// if you want to handle negative numbers.
/// </summary>
/// <example>
@@ -507,7 +412,7 @@ namespace Godot
/// with the components of the given <see cref="Vector3i"/>.
/// This operation uses truncated division, which is often not desired
/// as it does not work well with negative numbers.
- /// Consider using <see cref="PosMod(Vector3i)"/> instead
+ /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead
/// if you want to handle negative numbers.
/// </summary>
/// <example>
@@ -527,36 +432,6 @@ namespace Godot
}
/// <summary>
- /// Performs a bitwise AND operation with this <see cref="Vector3i"/>
- /// and the given <see langword="int"/>.
- /// </summary>
- /// <param name="vec">The vector to AND with.</param>
- /// <param name="and">The integer to AND with.</param>
- /// <returns>The result of the bitwise AND.</returns>
- public static Vector3i operator &(Vector3i vec, int and)
- {
- vec.x &= and;
- vec.y &= and;
- vec.z &= and;
- return vec;
- }
-
- /// <summary>
- /// Performs a bitwise AND operation with this <see cref="Vector3i"/>
- /// and the given <see cref="Vector3i"/>.
- /// </summary>
- /// <param name="vec">The left vector to AND with.</param>
- /// <param name="andv">The right vector to AND with.</param>
- /// <returns>The result of the bitwise AND.</returns>
- public static Vector3i operator &(Vector3i vec, Vector3i andv)
- {
- vec.x &= andv.x;
- vec.y &= andv.y;
- vec.z &= andv.z;
- return vec;
- }
-
- /// <summary>
/// Returns <see langword="true"/> if the vectors are equal.
/// </summary>
/// <param name="left">The left vector.</param>
@@ -689,7 +564,11 @@ namespace Godot
/// <param name="value">The vector to convert.</param>
public static explicit operator Vector3i(Vector3 value)
{
- return new Vector3i(value);
+ return new Vector3i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y),
+ Mathf.RoundToInt(value.z)
+ );
}
/// <summary>
@@ -698,14 +577,9 @@ namespace Godot
/// </summary>
/// <param name="obj">The object to compare with.</param>
/// <returns>Whether or not the vector and the object are equal.</returns>
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj is Vector3i)
- {
- return Equals((Vector3i)obj);
- }
-
- return false;
+ return obj is Vector3i other && Equals(other);
}
/// <summary>
@@ -713,7 +587,7 @@ namespace Godot
/// </summary>
/// <param name="other">The other vector.</param>
/// <returns>Whether or not the vectors are equal.</returns>
- public bool Equals(Vector3i other)
+ public readonly bool Equals(Vector3i other)
{
return x == other.x && y == other.y && z == other.z;
}
@@ -722,7 +596,7 @@ namespace Godot
/// Serves as the hash function for <see cref="Vector3i"/>.
/// </summary>
/// <returns>A hash code for this vector.</returns>
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
}
@@ -731,7 +605,7 @@ namespace Godot
/// Converts this <see cref="Vector3i"/> to a string.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public override string ToString()
+ public override readonly string ToString()
{
return $"({x}, {y}, {z})";
}
@@ -740,7 +614,7 @@ namespace Godot
/// Converts this <see cref="Vector3i"/> to a string with the given <paramref name="format"/>.
/// </summary>
/// <returns>A string representation of this vector.</returns>
- public string ToString(string format)
+ public readonly string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
new file mode 100644
index 0000000000..0f4528bb40
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs
@@ -0,0 +1,908 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ /// <summary>
+ /// 4-element structure that can be used to represent positions in 4D space or any other pair of numeric values.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Vector4 : IEquatable<Vector4>
+ {
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
+ /// </summary>
+ public enum Axis
+ {
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
+ X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
+ Y,
+ /// <summary>
+ /// The vector's Z axis.
+ /// </summary>
+ Z,
+ /// <summary>
+ /// The vector's W axis.
+ /// </summary>
+ W
+ }
+
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
+ /// </summary>
+ public real_t x;
+
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
+ /// </summary>
+ public real_t y;
+
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
+ /// </summary>
+ public real_t z;
+
+ /// <summary>
+ /// The vector's W component. Also accessible by using the index position <c>[3]</c>.
+ /// </summary>
+ public real_t w;
+
+ /// <summary>
+ /// Access vector components using their index.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0, 1, 2 or 3.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>,
+ /// <c>[2]</c> is equivalent to <see cref="z"/>.
+ /// <c>[3]</c> is equivalent to <see cref="w"/>.
+ /// </value>
+ public real_t this[int index]
+ {
+ readonly get
+ {
+ switch (index)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Helper method for deconstruction into a tuple.
+ /// </summary>
+ public readonly void Deconstruct(out real_t x, out real_t y, out real_t z, out real_t w)
+ {
+ x = this.x;
+ y = this.y;
+ z = this.z;
+ w = this.w;
+ }
+
+ internal void Normalize()
+ {
+ real_t lengthsq = LengthSquared();
+
+ if (lengthsq == 0)
+ {
+ x = y = z = w = 0f;
+ }
+ else
+ {
+ real_t length = Mathf.Sqrt(lengthsq);
+ x /= length;
+ y /= length;
+ z /= length;
+ w /= length;
+ }
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(real_t)"/> called on each component.</returns>
+ public readonly Vector4 Abs()
+ {
+ return new Vector4(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w));
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components rounded up (towards positive infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Ceil"/> called on each component.</returns>
+ public readonly Vector4 Ceil()
+ {
+ return new Vector4(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z), Mathf.Ceil(w));
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
+ /// </summary>
+ /// <param name="min">The vector with minimum allowed values.</param>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector4 Clamp(Vector4 min, Vector4 max)
+ {
+ return new Vector4
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y),
+ Mathf.Clamp(z, min.z, max.z),
+ Mathf.Clamp(w, min.w, max.w)
+ );
+ }
+
+ /// <summary>
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
+ public readonly Vector4 CubicInterpolate(Vector4 b, Vector4 preA, Vector4 postB, real_t weight)
+ {
+ return new Vector4
+ (
+ Mathf.CubicInterpolate(x, b.x, preA.x, postB.x, weight),
+ Mathf.CubicInterpolate(y, b.y, preA.y, postB.y, weight),
+ Mathf.CubicInterpolate(z, b.z, preA.z, postB.z, weight),
+ Mathf.CubicInterpolate(w, b.w, preA.w, postB.w, weight)
+ );
+ }
+
+ /// <summary>
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
+ /// It can perform smoother interpolation than <see cref="CubicInterpolate"/>
+ /// by the time values.
+ /// </summary>
+ /// <param name="b">The destination vector.</param>
+ /// <param name="preA">A vector before this vector.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="t"></param>
+ /// <param name="preAT"></param>
+ /// <param name="postBT"></param>
+ /// <returns>The interpolated vector.</returns>
+ public readonly Vector4 CubicInterpolateInTime(Vector4 b, Vector4 preA, Vector4 postB, real_t weight, real_t t, real_t preAT, real_t postBT)
+ {
+ return new Vector4
+ (
+ Mathf.CubicInterpolateInTime(x, b.x, preA.x, postB.x, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(y, b.y, preA.y, postB.y, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(z, b.z, preA.z, postB.z, weight, t, preAT, postBT),
+ Mathf.CubicInterpolateInTime(w, b.w, preA.w, postB.w, weight, t, preAT, postBT)
+ );
+ }
+
+ /// <summary>
+ /// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
+ /// </summary>
+ /// <param name="to">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to <paramref name="to"/>.</returns>
+ public readonly Vector4 DirectionTo(Vector4 to)
+ {
+ Vector4 ret = new Vector4(to.x - x, to.y - y, to.z - z, to.w - w);
+ ret.Normalize();
+ return ret;
+ }
+
+ /// <summary>
+ /// Returns the squared distance between this vector and <paramref name="to"/>.
+ /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
+ /// you need to compare vectors or need the squared distance for some formula.
+ /// </summary>
+ /// <param name="to">The other vector to use.</param>
+ /// <returns>The squared distance between the two vectors.</returns>
+ public readonly real_t DistanceSquaredTo(Vector4 to)
+ {
+ return (to - this).LengthSquared();
+ }
+
+ /// <summary>
+ /// Returns the distance between this vector and <paramref name="to"/>.
+ /// </summary>
+ /// <param name="to">The other vector to use.</param>
+ /// <returns>The distance between the two vectors.</returns>
+ public readonly real_t DistanceTo(Vector4 to)
+ {
+ return (to - this).Length();
+ }
+
+ /// <summary>
+ /// Returns the dot product of this vector and <paramref name="with"/>.
+ /// </summary>
+ /// <param name="with">The other vector to use.</param>
+ /// <returns>The dot product of the two vectors.</returns>
+ public readonly real_t Dot(Vector4 with)
+ {
+ return (x * with.x) + (y * with.y) + (z * with.z) + (w * with.w);
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components rounded down (towards negative infinity).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Floor"/> called on each component.</returns>
+ public readonly Vector4 Floor()
+ {
+ return new Vector4(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z), Mathf.Floor(w));
+ }
+
+ /// <summary>
+ /// Returns the inverse of this vector. This is the same as <c>new Vector4(1 / v.x, 1 / v.y, 1 / v.z, 1 / v.w)</c>.
+ /// </summary>
+ /// <returns>The inverse of this vector.</returns>
+ public readonly Vector4 Inverse()
+ {
+ return new Vector4(1 / x, 1 / y, 1 / z, 1 / w);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this vector is finite, by calling
+ /// <see cref="Mathf.IsFinite"/> on each component.
+ /// </summary>
+ /// <returns>Whether this vector is finite or not.</returns>
+ public readonly bool IsFinite()
+ {
+ return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
+ /// </summary>
+ /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
+ public readonly bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
+ }
+
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <returns>The length of this vector.</returns>
+ public readonly real_t Length()
+ {
+ real_t x2 = x * x;
+ real_t y2 = y * y;
+ real_t z2 = z * z;
+ real_t w2 = w * w;
+
+ return Mathf.Sqrt(x2 + y2 + z2 + w2);
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
+ public readonly real_t LengthSquared()
+ {
+ real_t x2 = x * x;
+ real_t y2 = y * y;
+ real_t z2 = z * z;
+ real_t w2 = w * w;
+
+ return x2 + y2 + z2 + w2;
+ }
+
+ /// <summary>
+ /// Returns the result of the linear interpolation between
+ /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
+ /// </summary>
+ /// <param name="to">The destination vector for interpolation.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting vector of the interpolation.</returns>
+ public readonly Vector4 Lerp(Vector4 to, real_t weight)
+ {
+ return new Vector4
+ (
+ Mathf.Lerp(x, to.x, weight),
+ Mathf.Lerp(y, to.y, weight),
+ Mathf.Lerp(z, to.z, weight),
+ Mathf.Lerp(w, to.w, weight)
+ );
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the highest axis.</returns>
+ public readonly Axis MaxAxisIndex()
+ {
+ int max_index = 0;
+ real_t max_value = x;
+ for (int i = 1; i < 4; i++)
+ {
+ if (this[i] > max_value)
+ {
+ max_index = i;
+ max_value = this[i];
+ }
+ }
+ return (Axis)max_index;
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.W"/>.
+ /// </summary>
+ /// <returns>The index of the lowest axis.</returns>
+ public readonly Axis MinAxisIndex()
+ {
+ int min_index = 0;
+ real_t min_value = x;
+ for (int i = 1; i < 4; i++)
+ {
+ if (this[i] <= min_value)
+ {
+ min_index = i;
+ min_value = this[i];
+ }
+ }
+ return (Axis)min_index;
+ }
+
+ /// <summary>
+ /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
+ /// </summary>
+ /// <returns>A normalized version of the vector.</returns>
+ public readonly Vector4 Normalized()
+ {
+ Vector4 v = this;
+ v.Normalize();
+ return v;
+ }
+
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="mod"/>.
+ /// </summary>
+ /// <param name="mod">A value representing the divisor of the operation.</param>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>.
+ /// </returns>
+ public readonly Vector4 PosMod(real_t mod)
+ {
+ return new Vector4(
+ Mathf.PosMod(x, mod),
+ Mathf.PosMod(y, mod),
+ Mathf.PosMod(z, mod),
+ Mathf.PosMod(w, mod)
+ );
+ }
+
+ /// <summary>
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="modv"/>'s components.
+ /// </summary>
+ /// <param name="modv">A vector representing the divisors of the operation.</param>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components.
+ /// </returns>
+ public readonly Vector4 PosMod(Vector4 modv)
+ {
+ return new Vector4(
+ Mathf.PosMod(x, modv.x),
+ Mathf.PosMod(y, modv.y),
+ Mathf.PosMod(z, modv.z),
+ Mathf.PosMod(w, modv.w)
+ );
+ }
+
+ /// <summary>
+ /// Returns this vector with all components rounded to the nearest integer,
+ /// with halfway cases rounded towards the nearest multiple of two.
+ /// </summary>
+ /// <returns>The rounded vector.</returns>
+ public readonly Vector4 Round()
+ {
+ return new Vector4(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z), Mathf.Round(w));
+ }
+
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
+ public readonly Vector4 Sign()
+ {
+ Vector4 v;
+ v.x = Mathf.Sign(x);
+ v.y = Mathf.Sign(y);
+ v.z = Mathf.Sign(z);
+ v.w = Mathf.Sign(w);
+ return v;
+ }
+
+ /// <summary>
+ /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
+ /// This can also be used to round to an arbitrary number of decimals.
+ /// </summary>
+ /// <param name="step">A vector value representing the step size to snap to.</param>
+ /// <returns>The snapped vector.</returns>
+ public readonly Vector4 Snapped(Vector4 step)
+ {
+ return new Vector4(
+ Mathf.Snapped(x, step.x),
+ Mathf.Snapped(y, step.y),
+ Mathf.Snapped(z, step.z),
+ Mathf.Snapped(w, step.w)
+ );
+ }
+
+ // Constants
+ private static readonly Vector4 _zero = new Vector4(0, 0, 0, 0);
+ private static readonly Vector4 _one = new Vector4(1, 1, 1, 1);
+ private static readonly Vector4 _inf = new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf);
+
+ /// <summary>
+ /// Zero vector, a vector with all components set to <c>0</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4(0, 0, 0, 0)</c>.</value>
+ public static Vector4 Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to <c>1</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4(1, 1, 1, 1)</c>.</value>
+ public static Vector4 One { get { return _one; } }
+ /// <summary>
+ /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4(Mathf.Inf, Mathf.Inf, Mathf.Inf, Mathf.Inf)</c>.</value>
+ public static Vector4 Inf { get { return _inf; } }
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector4"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
+ /// <param name="z">The vector's Z component.</param>
+ /// <param name="w">The vector's W component.</param>
+ public Vector4(real_t x, real_t y, real_t z, real_t w)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /// <summary>
+ /// Adds each component of the <see cref="Vector4"/>
+ /// with the components of the given <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
+ public static Vector4 operator +(Vector4 left, Vector4 right)
+ {
+ left.x += right.x;
+ left.y += right.y;
+ left.z += right.z;
+ left.w += right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector4"/>
+ /// by the components of the given <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
+ public static Vector4 operator -(Vector4 left, Vector4 right)
+ {
+ left.x -= right.x;
+ left.y -= right.y;
+ left.z -= right.z;
+ left.w -= right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector4"/>.
+ /// This is the same as writing <c>new Vector4(-v.x, -v.y, -v.z, -v.w)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// With floats, the number zero can be either positive or negative.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
+ public static Vector4 operator -(Vector4 vec)
+ {
+ vec.x = -vec.x;
+ vec.y = -vec.y;
+ vec.z = -vec.z;
+ vec.w = -vec.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4 operator *(Vector4 vec, real_t scale)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ vec.w *= scale;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4 operator *(real_t scale, Vector4 vec)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ vec.w *= scale;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4"/>
+ /// by the components of the given <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4 operator *(Vector4 left, Vector4 right)
+ {
+ left.x *= right.x;
+ left.y *= right.y;
+ left.z *= right.z;
+ left.w *= right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Divides each component of the <see cref="Vector4"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
+ public static Vector4 operator /(Vector4 vec, real_t divisor)
+ {
+ vec.x /= divisor;
+ vec.y /= divisor;
+ vec.z /= divisor;
+ vec.w /= divisor;
+ return vec;
+ }
+
+ /// <summary>
+ /// Divides each component of the <see cref="Vector4"/>
+ /// by the components of the given <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
+ public static Vector4 operator /(Vector4 vec, Vector4 divisorv)
+ {
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ vec.z /= divisorv.z;
+ vec.w /= divisorv.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector4"/>
+ /// with the components of the given <see cref="real_t"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(real_t)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector4(10, -20, 30, 40) % 7); // Prints "(3, -6, 2, 5)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
+ public static Vector4 operator %(Vector4 vec, real_t divisor)
+ {
+ vec.x %= divisor;
+ vec.y %= divisor;
+ vec.z %= divisor;
+ vec.w %= divisor;
+ return vec;
+ }
+
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector4"/>
+ /// with the components of the given <see cref="Vector4"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(Vector4)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector4(10, -20, 30, 10) % new Vector4(7, 8, 9, 10)); // Prints "(3, -4, 3, 0)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
+ public static Vector4 operator %(Vector4 vec, Vector4 divisorv)
+ {
+ vec.x %= divisorv.x;
+ vec.y %= divisorv.y;
+ vec.z %= divisorv.z;
+ vec.w %= divisorv.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
+ public static bool operator ==(Vector4 left, Vector4 right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
+ public static bool operator !=(Vector4 left, Vector4 right)
+ {
+ return !left.Equals(right);
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
+ public static bool operator <(Vector4 left, Vector4 right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w < right.w;
+ }
+ return left.z < right.z;
+ }
+ return left.y < right.y;
+ }
+ return left.x < right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
+ public static bool operator >(Vector4 left, Vector4 right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w > right.w;
+ }
+ return left.z > right.z;
+ }
+ return left.y > right.y;
+ }
+ return left.x > right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
+ public static bool operator <=(Vector4 left, Vector4 right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w <= right.w;
+ }
+ return left.z < right.z;
+ }
+ return left.y < right.y;
+ }
+ return left.x < right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
+ public static bool operator >=(Vector4 left, Vector4 right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w >= right.w;
+ }
+ return left.z > right.z;
+ }
+ return left.y > right.y;
+ }
+ return left.x > right.x;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
+ public override readonly bool Equals(object obj)
+ {
+ return obj is Vector4 other && Equals(other);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
+ public readonly bool Equals(Vector4 other)
+ {
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// </summary>
+ /// <param name="other">The other vector to compare.</param>
+ /// <returns>Whether or not the vectors are approximately equal.</returns>
+ public readonly bool IsEqualApprox(Vector4 other)
+ {
+ return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this vector's values are approximately zero,
+ /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component.
+ /// This method is faster than using <see cref="IsEqualApprox"/> with one value
+ /// as a zero vector.
+ /// </summary>
+ /// <returns>Whether or not the vector is approximately zero.</returns>
+ public readonly bool IsZeroApprox()
+ {
+ return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w);
+ }
+
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector4"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
+ public override readonly int GetHashCode()
+ {
+ return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Vector4"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
+ public override string ToString()
+ {
+ return $"({x}, {y}, {z}, {w})";
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Vector4"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
+ public readonly string ToString(string format)
+ {
+ return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
new file mode 100644
index 0000000000..00ecc64856
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs
@@ -0,0 +1,640 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ /// <summary>
+ /// 4-element structure that can be used to represent 4D grid coordinates or sets of integers.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Vector4i : IEquatable<Vector4i>
+ {
+ /// <summary>
+ /// Enumerated index values for the axes.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
+ /// </summary>
+ public enum Axis
+ {
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
+ X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
+ Y,
+ /// <summary>
+ /// The vector's Z axis.
+ /// </summary>
+ Z,
+ /// <summary>
+ /// The vector's W axis.
+ /// </summary>
+ W
+ }
+
+ /// <summary>
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
+ /// </summary>
+ public int x;
+
+ /// <summary>
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
+ /// </summary>
+ public int y;
+
+ /// <summary>
+ /// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
+ /// </summary>
+ public int z;
+
+ /// <summary>
+ /// The vector's W component. Also accessible by using the index position <c>[3]</c>.
+ /// </summary>
+ public int w;
+
+ /// <summary>
+ /// Access vector components using their <paramref name="index"/>.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="index"/> is not 0, 1, 2 or 3.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>,
+ /// <c>[2]</c> is equivalent to <see cref="z"/>.
+ /// <c>[3]</c> is equivalent to <see cref="w"/>.
+ /// </value>
+ public int this[int index]
+ {
+ readonly get
+ {
+ switch (index)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ x = value;
+ return;
+ case 1:
+ y = value;
+ return;
+ case 2:
+ z = value;
+ return;
+ case 3:
+ w = value;
+ return;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Helper method for deconstruction into a tuple.
+ /// </summary>
+ public readonly void Deconstruct(out int x, out int y, out int z, out int w)
+ {
+ x = this.x;
+ y = this.y;
+ z = this.z;
+ w = this.w;
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components in absolute values (i.e. positive).
+ /// </summary>
+ /// <returns>A vector with <see cref="Mathf.Abs(int)"/> called on each component.</returns>
+ public readonly Vector4i Abs()
+ {
+ return new Vector4i(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z), Mathf.Abs(w));
+ }
+
+ /// <summary>
+ /// Returns a new vector with all components clamped between the
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(int, int, int)"/>.
+ /// </summary>
+ /// <param name="min">The vector with minimum allowed values.</param>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public readonly Vector4i Clamp(Vector4i min, Vector4i max)
+ {
+ return new Vector4i
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y),
+ Mathf.Clamp(z, min.z, max.z),
+ Mathf.Clamp(w, min.w, max.w)
+ );
+ }
+
+ /// <summary>
+ /// Returns the length (magnitude) of this vector.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <returns>The length of this vector.</returns>
+ public readonly real_t Length()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+ int z2 = z * z;
+ int w2 = w * w;
+
+ return Mathf.Sqrt(x2 + y2 + z2 + w2);
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of this vector.
+ /// This method runs faster than <see cref="Length"/>, so prefer it if
+ /// you need to compare vectors or need the squared length for some formula.
+ /// </summary>
+ /// <returns>The squared length of this vector.</returns>
+ public readonly int LengthSquared()
+ {
+ int x2 = x * x;
+ int y2 = y * y;
+ int z2 = z * z;
+ int w2 = w * w;
+
+ return x2 + y2 + z2 + w2;
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.X"/>.
+ /// </summary>
+ /// <returns>The index of the highest axis.</returns>
+ public readonly Axis MaxAxisIndex()
+ {
+ int max_index = 0;
+ int max_value = x;
+ for (int i = 1; i < 4; i++)
+ {
+ if (this[i] > max_value)
+ {
+ max_index = i;
+ max_value = this[i];
+ }
+ }
+ return (Axis)max_index;
+ }
+
+ /// <summary>
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
+ /// If all components are equal, this method returns <see cref="Axis.W"/>.
+ /// </summary>
+ /// <returns>The index of the lowest axis.</returns>
+ public readonly Axis MinAxisIndex()
+ {
+ int min_index = 0;
+ int min_value = x;
+ for (int i = 1; i < 4; i++)
+ {
+ if (this[i] <= min_value)
+ {
+ min_index = i;
+ min_value = this[i];
+ }
+ }
+ return (Axis)min_index;
+ }
+
+ /// <summary>
+ /// Returns a vector with each component set to one or negative one, depending
+ /// on the signs of this vector's components, or zero if the component is zero,
+ /// by calling <see cref="Mathf.Sign(int)"/> on each component.
+ /// </summary>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
+ public readonly Vector4i Sign()
+ {
+ return new Vector4i(Mathf.Sign(x), Mathf.Sign(y), Mathf.Sign(z), Mathf.Sign(w));
+ }
+
+ // Constants
+ private static readonly Vector4i _zero = new Vector4i(0, 0, 0, 0);
+ private static readonly Vector4i _one = new Vector4i(1, 1, 1, 1);
+
+ /// <summary>
+ /// Zero vector, a vector with all components set to <c>0</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4i(0, 0, 0, 0)</c>.</value>
+ public static Vector4i Zero { get { return _zero; } }
+ /// <summary>
+ /// One vector, a vector with all components set to <c>1</c>.
+ /// </summary>
+ /// <value>Equivalent to <c>new Vector4i(1, 1, 1, 1)</c>.</value>
+ public static Vector4i One { get { return _one; } }
+
+ /// <summary>
+ /// Constructs a new <see cref="Vector4i"/> with the given components.
+ /// </summary>
+ /// <param name="x">The vector's X component.</param>
+ /// <param name="y">The vector's Y component.</param>
+ /// <param name="z">The vector's Z component.</param>
+ /// <param name="w">The vector's W component.</param>
+ public Vector4i(int x, int y, int z, int w)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /// <summary>
+ /// Adds each component of the <see cref="Vector4i"/>
+ /// with the components of the given <see cref="Vector4i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
+ public static Vector4i operator +(Vector4i left, Vector4i right)
+ {
+ left.x += right.x;
+ left.y += right.y;
+ left.z += right.z;
+ left.w += right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector4i"/>
+ /// by the components of the given <see cref="Vector4i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
+ public static Vector4i operator -(Vector4i left, Vector4i right)
+ {
+ left.x -= right.x;
+ left.y -= right.y;
+ left.z -= right.z;
+ left.w -= right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector4i"/>.
+ /// This is the same as writing <c>new Vector4i(-v.x, -v.y, -v.z, -v.w)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
+ public static Vector4i operator -(Vector4i vec)
+ {
+ vec.x = -vec.x;
+ vec.y = -vec.y;
+ vec.z = -vec.z;
+ vec.w = -vec.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4i operator *(Vector4i vec, int scale)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ vec.w *= scale;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4i operator *(int scale, Vector4i vec)
+ {
+ vec.x *= scale;
+ vec.y *= scale;
+ vec.z *= scale;
+ vec.w *= scale;
+ return vec;
+ }
+
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector4i"/>
+ /// by the components of the given <see cref="Vector4i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
+ public static Vector4i operator *(Vector4i left, Vector4i right)
+ {
+ left.x *= right.x;
+ left.y *= right.y;
+ left.z *= right.z;
+ left.w *= right.w;
+ return left;
+ }
+
+ /// <summary>
+ /// Divides each component of the <see cref="Vector4i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
+ public static Vector4i operator /(Vector4i vec, int divisor)
+ {
+ vec.x /= divisor;
+ vec.y /= divisor;
+ vec.z /= divisor;
+ vec.w /= divisor;
+ return vec;
+ }
+
+ /// <summary>
+ /// Divides each component of the <see cref="Vector4i"/>
+ /// by the components of the given <see cref="Vector4i"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
+ public static Vector4i operator /(Vector4i vec, Vector4i divisorv)
+ {
+ vec.x /= divisorv.x;
+ vec.y /= divisorv.y;
+ vec.z /= divisorv.z;
+ vec.w /= divisorv.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector4i"/>
+ /// with the components of the given <see langword="int"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vecto43i(10, -20, 30, -40) % 7); // Prints "(3, -6, 2, -5)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
+ public static Vector4i operator %(Vector4i vec, int divisor)
+ {
+ vec.x %= divisor;
+ vec.y %= divisor;
+ vec.z %= divisor;
+ vec.w %= divisor;
+ return vec;
+ }
+
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector4i"/>
+ /// with the components of the given <see cref="Vector4i"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector4i(10, -20, 30, -40) % new Vector4i(6, 7, 8, 9)); // Prints "(4, -6, 6, -4)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
+ public static Vector4i operator %(Vector4i vec, Vector4i divisorv)
+ {
+ vec.x %= divisorv.x;
+ vec.y %= divisorv.y;
+ vec.z %= divisorv.z;
+ vec.w %= divisorv.w;
+ return vec;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
+ public static bool operator ==(Vector4i left, Vector4i right)
+ {
+ return left.Equals(right);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
+ public static bool operator !=(Vector4i left, Vector4i right)
+ {
+ return !left.Equals(right);
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
+ public static bool operator <(Vector4i left, Vector4i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w < right.w;
+ }
+ return left.z < right.z;
+ }
+ return left.y < right.y;
+ }
+ return left.x < right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
+ public static bool operator >(Vector4i left, Vector4i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w > right.w;
+ }
+ return left.z > right.z;
+ }
+ return left.y > right.y;
+ }
+ return left.x > right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
+ public static bool operator <=(Vector4i left, Vector4i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w <= right.w;
+ }
+ return left.z < right.z;
+ }
+ return left.y < right.y;
+ }
+ return left.x < right.x;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Vector4i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y, Z and finally W values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
+ public static bool operator >=(Vector4i left, Vector4i right)
+ {
+ if (left.x == right.x)
+ {
+ if (left.y == right.y)
+ {
+ if (left.z == right.z)
+ {
+ return left.w >= right.w;
+ }
+ return left.z > right.z;
+ }
+ return left.y > right.y;
+ }
+ return left.x > right.x;
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Vector4i"/> to a <see cref="Vector4"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
+ public static implicit operator Vector4(Vector4i value)
+ {
+ return new Vector4(value.x, value.y, value.z, value.w);
+ }
+
+ /// <summary>
+ /// Converts a <see cref="Vector4"/> to a <see cref="Vector4i"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
+ public static explicit operator Vector4i(Vector4 value)
+ {
+ return new Vector4i(
+ Mathf.RoundToInt(value.x),
+ Mathf.RoundToInt(value.y),
+ Mathf.RoundToInt(value.z),
+ Mathf.RoundToInt(value.w)
+ );
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is equal
+ /// to the given object (<see paramref="obj"/>).
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
+ public override readonly bool Equals(object obj)
+ {
+ return obj is Vector4i other && Equals(other);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
+ public readonly bool Equals(Vector4i other)
+ {
+ return x == other.x && y == other.y && z == other.z && w == other.w;
+ }
+
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector4i"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
+ public override readonly int GetHashCode()
+ {
+ return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Vector4i"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
+ public override readonly string ToString()
+ {
+ return $"({x}, {y}, {z}, {w})";
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Vector4i"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
+ public readonly string ToString(string format)
+ {
+ return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}), {w.ToString(format)})";
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
new file mode 100644
index 0000000000..263a934fae
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GlobalUsings.cs
@@ -0,0 +1,5 @@
+#if REAL_T_IS_DOUBLE
+global using real_t = System.Double;
+#else
+global using real_t = System.Single;
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index e59f45bbf6..644212c74d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -4,25 +4,75 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+
+ <AnalysisMode>Recommended</AnalysisMode>
+
+ <!-- Disabled temporarily as it pollutes the warnings, but we need to document public APIs. -->
+ <NoWarn>CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
+ <Description>Godot C# Core API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharp</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ <IncludeSymbols>true</IncludeSymbols>
+ <SymbolPackageFormat>snupkg</SymbolPackageFormat>
+ </PropertyGroup>
+ <ItemGroup>
+ <!-- SdkPackageVersions.props for easy access -->
+ <None Include="$(GodotSdkPackageVersionsFilePath)">
+ <Link>SdkPackageVersions.props</Link>
+ </None>
+ </ItemGroup>
+ <PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
+ <DefineConstants Condition=" '$(GodotFloat64)' == 'true' ">REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="ReflectionAnalyzers" Version="0.1.22-dev" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
+ <!--PackageReference Include="IDisposableAnalyzers" Version="3.4.13" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" /-->
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <!-- Sources -->
+ <ItemGroup>
<Compile Include="Core\AABB.cs" />
+ <Compile Include="Core\Bridge\GodotSerializationInfo.cs" />
+ <Compile Include="Core\Bridge\MethodInfo.cs" />
+ <Compile Include="Core\Callable.generics.cs" />
+ <Compile Include="Core\CustomGCHandle.cs" />
<Compile Include="Core\Array.cs" />
<Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
- <Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
- <Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
+ <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
+ <Compile Include="Core\Attributes\MustBeVariantAttribute.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" />
<Compile Include="Core\Basis.cs" />
+ <Compile Include="Core\Bridge\CSharpInstanceBridge.cs" />
+ <Compile Include="Core\Bridge\GCHandleBridge.cs" />
+ <Compile Include="Core\Bridge\AlcReloadCfg.cs" />
+ <Compile Include="Core\Bridge\ManagedCallbacks.cs" />
+ <Compile Include="Core\Bridge\PropertyInfo.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.cs" />
+ <Compile Include="Core\Bridge\ScriptManagerBridge.types.cs" />
<Compile Include="Core\Callable.cs" />
<Compile Include="Core\Color.cs" />
<Compile Include="Core\Colors.cs" />
@@ -30,41 +80,56 @@
<Compile Include="Core\DelegateUtils.cs" />
<Compile Include="Core\Dictionary.cs" />
<Compile Include="Core\Dispatcher.cs" />
- <Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
- <Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
<Compile Include="Core\GodotTraceListener.cs" />
<Compile Include="Core\GodotUnhandledExceptionEvent.cs" />
+ <Compile Include="Core\DisposablesTracker.cs" />
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
- <Compile Include="Core\MarshalUtils.cs" />
<Compile Include="Core\Mathf.cs" />
<Compile Include="Core\MathfEx.cs" />
+ <Compile Include="Core\NativeInterop\CustomUnsafe.cs" />
+ <Compile Include="Core\NativeInterop\ExceptionUtils.cs" />
+ <Compile Include="Core\NativeInterop\GodotDllImportResolver.cs" />
+ <Compile Include="Core\NativeInterop\InteropUtils.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.extended.cs" />
+ <Compile Include="Core\NativeInterop\NativeVariantPtrArgs.cs" />
+ <Compile Include="Core\NativeInterop\VariantUtils.cs" />
+ <Compile Include="Core\NativeInterop\VariantUtils.generic.cs" />
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
+ <Compile Include="Core\Object.exceptions.cs" />
<Compile Include="Core\Plane.cs" />
+ <Compile Include="Core\Projection.cs" />
<Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
+ <Compile Include="Core\ReflectionUtils.cs" />
<Compile Include="Core\RID.cs" />
- <Compile Include="Core\SignalInfo.cs" />
+ <Compile Include="Core\NativeInterop\NativeFuncs.cs" />
+ <Compile Include="Core\NativeInterop\InteropStructs.cs" />
+ <Compile Include="Core\NativeInterop\Marshaling.cs" />
+ <Compile Include="Core\Signal.cs" />
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />
<Compile Include="Core\StringName.cs" />
<Compile Include="Core\Transform2D.cs" />
<Compile Include="Core\Transform3D.cs" />
- <Compile Include="Core\UnhandledExceptionArgs.cs" />
+ <Compile Include="Core\Variant.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
<Compile Include="Core\Vector3i.cs" />
+ <Compile Include="Core\Vector4.cs" />
+ <Compile Include="Core\Vector4i.cs" />
+ <Compile Include="GlobalUsings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<!--
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
new file mode 100644
index 0000000000..1add6cc77e
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.DotSettings
@@ -0,0 +1,5 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=core/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index a8c4ba96b5..8f623625fc 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -4,12 +4,30 @@
<OutputPath>bin/$(Configuration)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>Godot</RootNamespace>
- <TargetFramework>netstandard2.1</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
<EnableDefaultItems>false</EnableDefaultItems>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <LangVersion>10</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <Description>Godot C# Editor API.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>GodotSharpEditor</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ <IncludeSymbols>true</IncludeSymbols>
+ <SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);GODOT</DefineConstants>
+ <DefineConstants Condition=" '$(GodotFloat64)' == 'true' ">REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\GodotSharp\GodotSharp.csproj">
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
new file mode 100644
index 0000000000..c7ff6fd3ee
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj.DotSettings
@@ -0,0 +1,4 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=generated_005Cgodotobjects/@EntryIndexedValue">True</s:Boolean>
+</wpf:ResourceDictionary>
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
deleted file mode 100644
index 7b9dbc87cf..0000000000
--- a/modules/mono/glue/base_object_glue.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-/*************************************************************************/
-/* base_object_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/object/class_db.h"
-#include "core/object/ref_counted.h"
-#include "core/string/string_name.h"
-
-#include "../csharp_script.h"
-#include "../mono_gd/gd_mono_cache.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_internals.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../mono_gd/gd_mono_utils.h"
-#include "../signal_awaiter_utils.h"
-#include "arguments_vector.h"
-
-Object *godot_icall_Object_Ctor(MonoObject *p_obj) {
- Object *instance = memnew(Object);
- GDMonoInternals::tie_managed_to_unmanaged(p_obj, instance);
- return instance;
-}
-
-void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_ptr == nullptr);
-#endif
-
- if (p_ptr->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
- if (cs_instance) {
- if (!cs_instance->is_destructing_script_instance()) {
- cs_instance->mono_object_disposed(p_obj);
- p_ptr->set_script_instance(nullptr);
- }
- return;
- }
- }
-
- void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
-
- if (data) {
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
- if (script_binding.inited) {
- MonoGCHandleData &gchandle = script_binding.gchandle;
- if (!gchandle.is_released()) {
- CSharpLanguage::release_script_gchandle(p_obj, gchandle);
- }
- }
- }
-}
-
-void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_ptr == nullptr);
- // This is only called with RefCounted derived classes
- CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
-#endif
-
- RefCounted *rc = static_cast<RefCounted *>(p_ptr);
-
- if (rc->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
- if (cs_instance) {
- if (!cs_instance->is_destructing_script_instance()) {
- bool delete_owner;
- bool remove_script_instance;
-
- cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
-
- if (delete_owner) {
- memdelete(rc);
- } else if (remove_script_instance) {
- rc->set_script_instance(nullptr);
- }
- }
- return;
- }
- }
-
- // Unsafe refcount decrement. The managed instance also counts as a reference.
- // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
- CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
- if (rc->unreference()) {
- memdelete(rc);
- } else {
- void *data = CSharpLanguage::get_existing_instance_binding(rc);
-
- if (data) {
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
- if (script_binding.inited) {
- MonoGCHandleData &gchandle = script_binding.gchandle;
- if (!gchandle.is_released()) {
- CSharpLanguage::release_script_gchandle(p_obj, gchandle);
- }
- }
- }
- }
-}
-
-void godot_icall_Object_ConnectEventSignals(Object *p_ptr) {
- CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
- if (csharp_instance) {
- csharp_instance->connect_event_signals();
- }
-}
-
-MethodBind *godot_icall_Object_ClassDB_get_method(StringName *p_type, MonoString *p_method) {
- StringName type = p_type ? *p_type : StringName();
- StringName method(GDMonoMarshal::mono_string_to_godot(p_method));
- return ClassDB::get_method(type, method);
-}
-
-MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
- if (!p_ptr) {
- return nullptr;
- }
-
- Ref<WeakRef> wref;
- RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
-
- if (rc) {
- Ref<RefCounted> r = rc;
- if (!r.is_valid()) {
- return nullptr;
- }
-
- wref.instantiate();
- wref->set_ref(r);
- } else {
- wref.instantiate();
- wref->set_obj(p_ptr);
- }
-
- return GDMonoUtils::unmanaged_get_managed(wref.ptr());
-}
-
-int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
- StringName signal = p_signal ? *p_signal : StringName();
- return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
-}
-
-MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
- List<PropertyInfo> property_list;
- p_ptr->get_property_list(&property_list);
-
- MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size());
-
- int i = 0;
- for (const PropertyInfo &E : property_list) {
- MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E.name);
- mono_array_setref(result, i, boxed);
- i++;
- }
-
- return result;
-}
-
-MonoBoolean godot_icall_DynamicGodotObject_InvokeMember(Object *p_ptr, MonoString *p_name, MonoArray *p_args, MonoObject **r_result) {
- String name = GDMonoMarshal::mono_string_to_godot(p_name);
-
- int argc = mono_array_length(p_args);
-
- ArgumentsVector<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- Callable::CallError error;
- Variant result = p_ptr->callp(StringName(name), args.ptr(), argc, error);
-
- *r_result = GDMonoMarshal::variant_to_mono_object(result);
-
- return error.error == Callable::CallError::CALL_OK;
-}
-
-MonoBoolean godot_icall_DynamicGodotObject_GetMember(Object *p_ptr, MonoString *p_name, MonoObject **r_result) {
- String name = GDMonoMarshal::mono_string_to_godot(p_name);
-
- bool valid;
- Variant value = p_ptr->get(StringName(name), &valid);
-
- if (valid) {
- *r_result = GDMonoMarshal::variant_to_mono_object(value);
- }
-
- return valid;
-}
-
-MonoBoolean godot_icall_DynamicGodotObject_SetMember(Object *p_ptr, MonoString *p_name, MonoObject *p_value) {
- String name = GDMonoMarshal::mono_string_to_godot(p_name);
- Variant value = GDMonoMarshal::mono_object_to_variant(p_value);
-
- bool valid;
- p_ptr->set(StringName(name), value, &valid);
-
- return valid;
-}
-
-MonoString *godot_icall_Object_ToString(Object *p_ptr) {
-#ifdef DEBUG_ENABLED
- // Cannot happen in C#; would get an ObjectDisposedException instead.
- CRASH_COND(p_ptr == nullptr);
-#endif
- // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
- String result = "[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]";
- return GDMonoMarshal::mono_string_from_godot(result);
-}
-
-void godot_register_object_icalls() {
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Ctor", godot_icall_Object_Ctor);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", godot_icall_Object_ClassDB_get_method);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString);
- GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref);
- GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect);
- GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList);
- GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember);
- GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember);
- GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/callable_glue.cpp b/modules/mono/glue/callable_glue.cpp
deleted file mode 100644
index e59b34313c..0000000000
--- a/modules/mono/glue/callable_glue.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* callable_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "../mono_gd/gd_mono_marshal.h"
-#include "arguments_vector.h"
-
-MonoObject *godot_icall_Callable_Call(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
- Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-
- int argc = mono_array_length(p_args);
-
- ArgumentsVector<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- Variant result;
- Callable::CallError error;
- callable.call(args.ptr(), argc, result, error);
-
- return GDMonoMarshal::variant_to_mono_object(result);
-}
-
-void godot_icall_Callable_CallDeferred(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
- Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-
- int argc = mono_array_length(p_args);
-
- ArgumentsVector<Variant> arg_store(argc);
- ArgumentsVector<const Variant *> args(argc);
-
- for (int i = 0; i < argc; i++) {
- MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
- arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
- args.set(i, &arg_store.get(i));
- }
-
- callable.call_deferred(args.ptr(), argc);
-}
-
-void godot_register_callable_icalls() {
- GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_Call", godot_icall_Callable_Call);
- GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_CallDeferred", godot_icall_Callable_CallDeferred);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
deleted file mode 100644
index 8a9f30459c..0000000000
--- a/modules/mono/glue/collections_glue.cpp
+++ /dev/null
@@ -1,374 +0,0 @@
-/*************************************************************************/
-/* collections_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include <mono/metadata/exception.h>
-
-#include "core/variant/array.h"
-
-#include "../mono_gd/gd_mono_cache.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-Array *godot_icall_Array_Ctor() {
- return memnew(Array);
-}
-
-void godot_icall_Array_Dtor(Array *ptr) {
- memdelete(ptr);
-}
-
-MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) {
- if (index < 0 || index >= ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
-}
-
-MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) {
- if (index < 0 || index >= ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class));
-}
-
-void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) {
- if (index < 0 || index >= ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return;
- }
- ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
-}
-
-int32_t godot_icall_Array_Count(Array *ptr) {
- return ptr->size();
-}
-
-int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) {
- ptr->append(GDMonoMarshal::mono_object_to_variant(item));
- return ptr->size();
-}
-
-void godot_icall_Array_Clear(Array *ptr) {
- ptr->clear();
-}
-
-MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
- return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
-}
-
-void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) {
- unsigned int count = ptr->size();
-
- if (mono_array_length(array) < (array_index + count)) {
- MonoException *exc = mono_get_exception_argument("", "Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
- GDMonoUtils::set_pending_exception(exc);
- return;
- }
-
- for (unsigned int i = 0; i < count; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(ptr->operator[](i));
- mono_array_setref(array, array_index, boxed);
- array_index++;
- }
-}
-
-Array *godot_icall_Array_Ctor_MonoArray(MonoArray *mono_array) {
- Array *godot_array = memnew(Array);
- unsigned int count = mono_array_length(mono_array);
- godot_array->resize(count);
- for (unsigned int i = 0; i < count; i++) {
- MonoObject *item = mono_array_get(mono_array, MonoObject *, i);
- godot_icall_Array_SetAt(godot_array, i, item);
- }
- return godot_array;
-}
-
-Array *godot_icall_Array_Duplicate(Array *ptr, MonoBoolean deep) {
- return memnew(Array(ptr->duplicate(deep)));
-}
-
-Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
- int count = left->size() + right->size();
- Array *new_array = memnew(Array(left->duplicate(false)));
- new_array->resize(count);
- for (unsigned int i = 0; i < (unsigned int)right->size(); i++) {
- new_array->operator[](i + left->size()) = right->operator[](i);
- }
- return new_array;
-}
-
-int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
- return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
-}
-
-void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) {
- if (index < 0 || index > ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return;
- }
- ptr->insert(index, GDMonoMarshal::mono_object_to_variant(item));
-}
-
-MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
- int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
- if (idx >= 0) {
- ptr->remove_at(idx);
- return true;
- }
- return false;
-}
-
-void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) {
- if (index < 0 || index >= ptr->size()) {
- GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
- return;
- }
- ptr->remove_at(index);
-}
-
-int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) {
- return (int32_t)ptr->resize(new_size);
-}
-
-void godot_icall_Array_Shuffle(Array *ptr) {
- ptr->shuffle();
-}
-
-void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
- MonoType *elem_type = mono_reflection_type_get_type(refltype);
-
- *type_encoding = mono_type_get_type(elem_type);
- MonoClass *type_class_raw = mono_class_from_mono_type(elem_type);
- *type_class = GDMono::get_singleton()->get_class(type_class_raw);
-}
-
-MonoString *godot_icall_Array_ToString(Array *ptr) {
- return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
-}
-
-Dictionary *godot_icall_Dictionary_Ctor() {
- return memnew(Dictionary);
-}
-
-void godot_icall_Dictionary_Dtor(Dictionary *ptr) {
- memdelete(ptr);
-}
-
-MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
-#ifdef DEBUG_ENABLED
- CRASH_COND(!exc);
-#endif
- GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
- GDMonoUtils::set_pending_exception((MonoException *)exc);
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ret);
-}
-
-MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr());
-#ifdef DEBUG_ENABLED
- CRASH_COND(!exc);
-#endif
- GDMonoUtils::runtime_object_init(exc, CACHED_CLASS(KeyNotFoundException));
- GDMonoUtils::set_pending_exception((MonoException *)exc);
- return nullptr;
- }
- return GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
-}
-
-void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) {
- ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value);
-}
-
-Array *godot_icall_Dictionary_Keys(Dictionary *ptr) {
- return memnew(Array(ptr->keys()));
-}
-
-Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
- return memnew(Array(ptr->values()));
-}
-
-int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
- return ptr->size();
-}
-
-int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
- *keys = godot_icall_Dictionary_Keys(ptr);
- *values = godot_icall_Dictionary_Values(ptr);
- return godot_icall_Dictionary_Count(ptr);
-}
-
-void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
- *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
- *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index));
-}
-
-void godot_icall_Dictionary_KeyValuePairAt_Generic(Dictionary *ptr, int index, MonoObject **key, MonoObject **value, uint32_t value_type_encoding, GDMonoClass *value_type_class) {
- ManagedType type(value_type_encoding, value_type_class);
- *key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
- *value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index), type);
-}
-
-void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
- Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
- Variant *ret = ptr->getptr(varKey);
- if (ret != nullptr) {
- GDMonoUtils::set_pending_exception(mono_get_exception_argument("key", "An element with the same key already exists"));
- return;
- }
- ptr->operator[](varKey) = GDMonoMarshal::mono_object_to_variant(value);
-}
-
-void godot_icall_Dictionary_Clear(Dictionary *ptr) {
- ptr->clear();
-}
-
-MonoBoolean godot_icall_Dictionary_Contains(Dictionary *ptr, MonoObject *key, MonoObject *value) {
- // no dupes
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- return ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value);
-}
-
-MonoBoolean godot_icall_Dictionary_ContainsKey(Dictionary *ptr, MonoObject *key) {
- return ptr->has(GDMonoMarshal::mono_object_to_variant(key));
-}
-
-Dictionary *godot_icall_Dictionary_Duplicate(Dictionary *ptr, MonoBoolean deep) {
- return memnew(Dictionary(ptr->duplicate(deep)));
-}
-
-MonoBoolean godot_icall_Dictionary_RemoveKey(Dictionary *ptr, MonoObject *key) {
- return ptr->erase(GDMonoMarshal::mono_object_to_variant(key));
-}
-
-MonoBoolean godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject *value) {
- Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
-
- // no dupes
- Variant *ret = ptr->getptr(varKey);
- if (ret != nullptr && *ret == GDMonoMarshal::mono_object_to_variant(value)) {
- ptr->erase(varKey);
- return true;
- }
-
- return false;
-}
-
-MonoBoolean godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- *value = nullptr;
- return false;
- }
- *value = GDMonoMarshal::variant_to_mono_object(ret);
- return true;
-}
-
-MonoBoolean godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) {
- Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key));
- if (ret == nullptr) {
- *value = nullptr;
- return false;
- }
- *value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class));
- return true;
-}
-
-void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
- MonoType *value_type = mono_reflection_type_get_type(refltype);
-
- *type_encoding = mono_type_get_type(value_type);
- MonoClass *type_class_raw = mono_class_from_mono_type(value_type);
- *type_class = GDMono::get_singleton()->get_class(type_class_raw);
-}
-
-MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
- return GDMonoMarshal::mono_string_from_godot(Variant(*ptr).operator String());
-}
-
-void godot_register_collections_icalls() {
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo);
- GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString);
-
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt_Generic", godot_icall_Dictionary_KeyValuePairAt_Generic);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo);
- GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
deleted file mode 100644
index 8b1c2b729e..0000000000
--- a/modules/mono/glue/gd_glue.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-/*************************************************************************/
-/* gd_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/io/marshalls.h"
-#include "core/os/os.h"
-#include "core/string/ustring.h"
-#include "core/variant/array.h"
-#include "core/variant/variant.h"
-#include "core/variant/variant_parser.h"
-
-#include "../mono_gd/gd_mono_cache.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-MonoObject *godot_icall_GD_bytes2var(MonoArray *p_bytes, MonoBoolean p_allow_objects) {
- Variant ret;
- PackedByteArray varr = GDMonoMarshal::mono_array_to_PackedByteArray(p_bytes);
- Error err = decode_variant(ret, varr.ptr(), varr.size(), nullptr, p_allow_objects);
- if (err != OK) {
- ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
- }
- return GDMonoMarshal::variant_to_mono_object(ret);
-}
-
-MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
- Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
- const Variant *args[1] = { &what };
- Callable::CallError ce;
- Variant ret;
- Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
- ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
- return GDMonoMarshal::variant_to_mono_object(ret);
-}
-
-int godot_icall_GD_hash(MonoObject *p_var) {
- return GDMonoMarshal::mono_object_to_variant(p_var).hash();
-}
-
-MonoObject *godot_icall_GD_instance_from_id(uint64_t p_instance_id) {
- return GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(ObjectID(p_instance_id)));
-}
-
-void godot_icall_GD_print(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(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);
-
- 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_error(str);
-}
-
-void godot_icall_GD_printraw(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;
- }
-
- OS::get_singleton()->print("%s", str.utf8().get_data());
-}
-
-void godot_icall_GD_prints(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;
- }
-
- if (i) {
- str += " ";
- }
-
- str += elem_str;
- }
-
- print_line(str);
-}
-
-void godot_icall_GD_printt(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;
- }
-
- if (i) {
- str += "\t";
- }
-
- str += elem_str;
- }
-
- print_line(str);
-}
-
-void godot_icall_GD_randomize() {
- Math::randomize();
-}
-
-uint32_t godot_icall_GD_randi() {
- return Math::rand();
-}
-
-float godot_icall_GD_randf() {
- return Math::randf();
-}
-
-int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
- return Math::random(from, to);
-}
-
-double godot_icall_GD_randf_range(double from, double to) {
- return Math::random(from, to);
-}
-
-double godot_icall_GD_randfn(double mean, double deviation) {
- return Math::randfn(mean, deviation);
-}
-
-uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) {
- uint32_t ret = Math::rand_from_seed(&seed);
- *newSeed = seed;
- return ret;
-}
-
-void godot_icall_GD_seed(uint64_t p_seed) {
- Math::seed(p_seed);
-}
-
-MonoString *godot_icall_GD_str(MonoArray *p_what) {
- String str;
- Array what = GDMonoMarshal::mono_array_to_Array(p_what);
-
- for (int i = 0; i < what.size(); i++) {
- String os = what[i].operator String();
-
- if (i == 0) {
- str = os;
- } else {
- str += os;
- }
- }
-
- return GDMonoMarshal::mono_string_from_godot(str);
-}
-
-MonoObject *godot_icall_GD_str2var(MonoString *p_str) {
- Variant ret;
-
- VariantParser::StreamString ss;
- ss.s = GDMonoMarshal::mono_string_to_godot(p_str);
-
- String errs;
- int line;
- Error err = VariantParser::parse(&ss, ret, errs, line);
- if (err != OK) {
- String err_str = "Parse error at line " + itos(line) + ": " + errs + ".";
- ERR_PRINT(err_str);
- ret = err_str;
- }
-
- return GDMonoMarshal::variant_to_mono_object(ret);
-}
-
-MonoBoolean godot_icall_GD_type_exists(StringName *p_type) {
- StringName type = p_type ? *p_type : StringName();
- return ClassDB::class_exists(type);
-}
-
-void godot_icall_GD_pusherror(MonoString *p_str) {
- ERR_PRINT(GDMonoMarshal::mono_string_to_godot(p_str));
-}
-
-void godot_icall_GD_pushwarning(MonoString *p_str) {
- WARN_PRINT(GDMonoMarshal::mono_string_to_godot(p_str));
-}
-
-MonoArray *godot_icall_GD_var2bytes(MonoObject *p_var, MonoBoolean p_full_objects) {
- Variant var = GDMonoMarshal::mono_object_to_variant(p_var);
-
- PackedByteArray barr;
- int len;
- Error err = encode_variant(var, nullptr, len, p_full_objects);
- ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
-
- barr.resize(len);
- encode_variant(var, barr.ptrw(), len, p_full_objects);
-
- return GDMonoMarshal::PackedByteArray_to_mono_array(barr);
-}
-
-MonoString *godot_icall_GD_var2str(MonoObject *p_var) {
- String vars;
- VariantWriter::write_to_string(GDMonoMarshal::mono_object_to_variant(p_var), vars);
- return GDMonoMarshal::mono_string_from_godot(vars);
-}
-
-uint32_t godot_icall_TypeToVariantType(MonoReflectionType *p_refl_type) {
- return (uint32_t)GDMonoMarshal::managed_to_variant_type(ManagedType::from_reftype(p_refl_type));
-}
-
-MonoObject *godot_icall_DefaultGodotTaskScheduler() {
- return GDMonoCache::cached_data.task_scheduler_handle->get_target();
-}
-
-void godot_register_gd_icalls() {
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id);
- 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);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randfn", godot_icall_GD_randfn);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType);
-
- // Dispatcher
- GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
deleted file mode 100644
index f9ad1a9893..0000000000
--- a/modules/mono/glue/glue_header.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* glue_header.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 GLUE_HEADER_H
-#define GLUE_HEADER_H
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-void godot_register_collections_icalls();
-void godot_register_gd_icalls();
-void godot_register_string_name_icalls();
-void godot_register_nodepath_icalls();
-void godot_register_callable_icalls();
-void godot_register_object_icalls();
-void godot_register_rid_icalls();
-void godot_register_string_icalls();
-void godot_register_scene_tree_icalls();
-
-/**
- * Registers internal calls that were not generated. This function is called
- * from the generated GodotSharpBindings::register_generated_icalls() function.
- */
-void godot_register_glue_header_icalls() {
- godot_register_collections_icalls();
- godot_register_gd_icalls();
- godot_register_string_name_icalls();
- godot_register_nodepath_icalls();
- godot_register_callable_icalls();
- godot_register_object_icalls();
- godot_register_rid_icalls();
- godot_register_string_icalls();
- godot_register_scene_tree_icalls();
-}
-
-// Used by the generated glue
-
-#include "core/config/engine.h"
-#include "core/object/class_db.h"
-#include "core/object/method_bind.h"
-#include "core/object/ref_counted.h"
-#include "core/string/node_path.h"
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-#include "core/variant/array.h"
-#include "core/variant/dictionary.h"
-
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_internals.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-#define GODOTSHARP_INSTANCE_OBJECT(m_instance, m_type) \
- static ClassDB::ClassInfo *ci = nullptr; \
- if (!ci) { \
- ci = ClassDB::classes.getptr(m_type); \
- } \
- Object *m_instance = ci->creation_func();
-
-#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
deleted file mode 100644
index 16e1509eb0..0000000000
--- a/modules/mono/glue/nodepath_glue.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*************************************************************************/
-/* nodepath_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/string/node_path.h"
-#include "core/string/ustring.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-NodePath *godot_icall_NodePath_Ctor(MonoString *p_path) {
- return memnew(NodePath(GDMonoMarshal::mono_string_to_godot(p_path)));
-}
-
-void godot_icall_NodePath_Dtor(NodePath *p_ptr) {
- ERR_FAIL_NULL(p_ptr);
- memdelete(p_ptr);
-}
-
-MonoString *godot_icall_NodePath_operator_String(NodePath *p_np) {
- return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
-}
-
-MonoBoolean godot_icall_NodePath_is_absolute(NodePath *p_ptr) {
- return (MonoBoolean)p_ptr->is_absolute();
-}
-
-int32_t godot_icall_NodePath_get_name_count(NodePath *p_ptr) {
- return p_ptr->get_name_count();
-}
-
-MonoString *godot_icall_NodePath_get_name(NodePath *p_ptr, uint32_t p_idx) {
- return GDMonoMarshal::mono_string_from_godot(p_ptr->get_name(p_idx));
-}
-
-int32_t godot_icall_NodePath_get_subname_count(NodePath *p_ptr) {
- return p_ptr->get_subname_count();
-}
-
-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());
-}
-
-NodePath *godot_icall_NodePath_get_as_property_path(NodePath *p_ptr) {
- return memnew(NodePath(p_ptr->get_as_property_path()));
-}
-
-MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) {
- return (MonoBoolean)p_ptr->is_empty();
-}
-
-void godot_register_nodepath_icalls() {
- GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor);
- 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);
- GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname);
- GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count);
- GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute);
- GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp
deleted file mode 100644
index 3e09564539..0000000000
--- a/modules/mono/glue/rid_glue.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************/
-/* rid_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/io/resource.h"
-#include "core/object/class_db.h"
-#include "core/templates/rid.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-RID *godot_icall_RID_Ctor(Object *p_from) {
- Resource *res_from = Object::cast_to<Resource>(p_from);
-
- if (res_from) {
- return memnew(RID(res_from->get_rid()));
- }
-
- return memnew(RID);
-}
-
-void godot_icall_RID_Dtor(RID *p_ptr) {
- ERR_FAIL_NULL(p_ptr);
- memdelete(p_ptr);
-}
-
-uint32_t godot_icall_RID_get_id(RID *p_ptr) {
- return p_ptr->get_id();
-}
-
-void godot_register_rid_icalls() {
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor);
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor);
- GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp
new file mode 100644
index 0000000000..f0ea0313ea
--- /dev/null
+++ b/modules/mono/glue/runtime_interop.cpp
@@ -0,0 +1,1503 @@
+/**************************************************************************/
+/* runtime_interop.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "runtime_interop.h"
+
+#include "core/config/engine.h"
+#include "core/debugger/engine_debugger.h"
+#include "core/debugger/script_debugger.h"
+#include "core/io/marshalls.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/os/os.h"
+#include "core/string/string_name.h"
+
+#include "../interop_types.h"
+
+#include "modules/mono/csharp_script.h"
+#include "modules/mono/managed_callable.h"
+#include "modules/mono/mono_gd/gd_mono_cache.h"
+#include "modules/mono/signal_awaiter_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For ArrayPrivate and DictionaryPrivate
+static_assert(sizeof(SafeRefCount) == sizeof(uint32_t));
+
+typedef Object *(*godotsharp_class_creation_func)();
+
+MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const StringName *p_methodname) {
+ return ClassDB::get_method(*p_classname, *p_methodname);
+}
+
+godotsharp_class_creation_func godotsharp_get_class_constructor(const StringName *p_classname) {
+ ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(*p_classname);
+ if (class_info) {
+ return class_info->creation_func;
+ }
+ return nullptr;
+}
+
+Object *godotsharp_engine_get_singleton(const String *p_name) {
+ return Engine::get_singleton()->get_singleton_object(*p_name);
+}
+
+int32_t godotsharp_stack_info_vector_resize(
+ Vector<ScriptLanguage::StackInfo> *p_stack_info_vector, int p_size) {
+ return (int32_t)p_stack_info_vector->resize(p_size);
+}
+
+void godotsharp_stack_info_vector_destroy(
+ Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
+ p_stack_info_vector->~Vector();
+}
+
+void godotsharp_internal_script_debugger_send_error(const String *p_func,
+ const String *p_file, int32_t p_line, const String *p_err, const String *p_descr,
+ bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) {
+ EngineDebugger::get_script_debugger()->send_error(*p_func, *p_file, p_line, *p_err, *p_descr,
+ true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector);
+}
+
+bool godotsharp_internal_script_debugger_is_active() {
+ return EngineDebugger::is_active();
+}
+
+GCHandleIntPtr godotsharp_internal_object_get_associated_gchandle(Object *p_ptr) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+#endif
+
+ if (p_ptr->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ return cs_instance->get_gchandle_intptr();
+ }
+ return { nullptr };
+ }
+ }
+
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+ return !gchandle.is_released() ? gchandle.get_intptr() : GCHandleIntPtr{ nullptr };
+ }
+ }
+
+ return { nullptr };
+}
+
+void godotsharp_internal_object_disposed(Object *p_ptr, GCHandleIntPtr p_gchandle_to_free) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+#endif
+
+ if (p_ptr->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_ptr->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ cs_instance->mono_object_disposed(p_gchandle_to_free);
+ p_ptr->set_script_instance(nullptr);
+ }
+ return;
+ }
+ }
+
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ if (!script_binding.gchandle.is_released()) {
+ CSharpLanguage::release_binding_gchandle_thread_safe(p_gchandle_to_free, script_binding);
+ }
+ }
+ }
+}
+
+void godotsharp_internal_refcounted_disposed(Object *p_ptr, GCHandleIntPtr p_gchandle_to_free, bool p_is_finalizer) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_ptr == nullptr);
+ // This is only called with RefCounted derived classes
+ CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
+#endif
+
+ RefCounted *rc = static_cast<RefCounted *>(p_ptr);
+
+ if (rc->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
+ if (cs_instance) {
+ if (!cs_instance->is_destructing_script_instance()) {
+ bool delete_owner;
+ bool remove_script_instance;
+
+ cs_instance->mono_object_disposed_baseref(p_gchandle_to_free, p_is_finalizer,
+ delete_owner, remove_script_instance);
+
+ if (delete_owner) {
+ memdelete(rc);
+ } else if (remove_script_instance) {
+ rc->set_script_instance(nullptr);
+ }
+ }
+ return;
+ }
+ }
+
+ // Unsafe refcount decrement. The managed instance also counts as a reference.
+ // See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
+ CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
+ if (rc->unreference()) {
+ memdelete(rc);
+ } else {
+ void *data = CSharpLanguage::get_existing_instance_binding(rc);
+
+ if (data) {
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
+ if (script_binding.inited) {
+ if (!script_binding.gchandle.is_released()) {
+ CSharpLanguage::release_binding_gchandle_thread_safe(p_gchandle_to_free, script_binding);
+ }
+ }
+ }
+ }
+}
+
+int32_t godotsharp_internal_signal_awaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
+ StringName signal = p_signal ? *p_signal : StringName();
+ return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter_handle_ptr);
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_get_script_instance_managed(Object *p_unmanaged, bool *r_has_cs_script_instance) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+ CRASH_COND(!r_has_cs_script_instance);
+#endif
+
+ if (p_unmanaged->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(p_unmanaged->get_script_instance());
+
+ if (cs_instance) {
+ *r_has_cs_script_instance = true;
+ return cs_instance->get_gchandle_intptr();
+ }
+ }
+
+ *r_has_cs_script_instance = false;
+ return { nullptr };
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_get_instance_binding_managed(Object *p_unmanaged) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+#endif
+
+ void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ ERR_FAIL_NULL_V(data, { nullptr });
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
+ ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
+
+ return script_binding.gchandle.get_intptr();
+}
+
+GCHandleIntPtr godotsharp_internal_unmanaged_instance_binding_create_managed(Object *p_unmanaged, GCHandleIntPtr p_old_gchandle) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(!p_unmanaged);
+#endif
+
+ void *data = CSharpLanguage::get_instance_binding(p_unmanaged);
+ ERR_FAIL_NULL_V(data, { nullptr });
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
+ ERR_FAIL_COND_V(!script_binding.inited, { nullptr });
+
+ MonoGCHandleData &gchandle = script_binding.gchandle;
+
+ // TODO: Possible data race?
+ CRASH_COND(gchandle.get_intptr().value != p_old_gchandle.value);
+
+ CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
+ script_binding.inited = false;
+
+ // Create a new one
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(script_binding.type_name == StringName());
+#endif
+
+ bool parent_is_object_class = ClassDB::is_parent_class(p_unmanaged->get_class_name(), script_binding.type_name);
+ ERR_FAIL_COND_V_MSG(!parent_is_object_class, { nullptr },
+ "Type inherits from native type '" + script_binding.type_name + "', so it can't be instantiated in object of type: '" + p_unmanaged->get_class() + "'.");
+
+ GCHandleIntPtr strong_gchandle =
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_CreateManagedForGodotObjectBinding(
+ &script_binding.type_name, p_unmanaged);
+
+ ERR_FAIL_NULL_V(strong_gchandle.value, { nullptr });
+
+ gchandle = MonoGCHandleData(strong_gchandle, gdmono::GCHandleType::STRONG_HANDLE);
+ script_binding.inited = true;
+
+ // Tie managed to unmanaged
+ RefCounted *rc = Object::cast_to<RefCounted>(p_unmanaged);
+
+ if (rc) {
+ // Unsafe refcount increment. The managed instance also counts as a reference.
+ // This way if the unmanaged world has no references to our owner
+ // but the managed instance is alive, the refcount will be 1 instead of 0.
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
+ rc->reference();
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
+ }
+
+ return gchandle.get_intptr();
+}
+
+void godotsharp_internal_tie_native_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, const StringName *p_native_name, bool p_ref_counted) {
+ CSharpLanguage::tie_native_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_native_name, p_ref_counted);
+}
+
+void godotsharp_internal_tie_user_managed_to_unmanaged(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged, Ref<CSharpScript> *p_script, bool p_ref_counted) {
+ CSharpLanguage::tie_user_managed_to_unmanaged(p_gchandle_intptr, p_unmanaged, p_script, p_ref_counted);
+}
+
+void godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup(GCHandleIntPtr p_gchandle_intptr, Object *p_unmanaged) {
+ CSharpLanguage::tie_managed_to_unmanaged_with_pre_setup(p_gchandle_intptr, p_unmanaged);
+}
+
+void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
+ memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
+}
+
+bool godotsharp_internal_script_load(const String *p_path, Ref<CSharpScript> *r_dest) {
+ Ref<Resource> res = ResourceLoader::load(*p_path);
+ if (res.is_valid()) {
+ memnew_placement(r_dest, Ref<CSharpScript>(res));
+ return true;
+ } else {
+ memnew_placement(r_dest, Ref<CSharpScript>());
+ return false;
+ }
+}
+
+void godotsharp_internal_reload_registered_script(CSharpScript *p_script) {
+ CRASH_COND(!p_script);
+ CSharpScript::reload_registered_script(Ref<CSharpScript>(p_script));
+}
+
+void godotsharp_array_filter_godot_objects_by_native(StringName *p_native_name, const Array *p_input, Array *r_output) {
+ memnew_placement(r_output, Array);
+
+ for (int i = 0; i < p_input->size(); ++i) {
+ if (ClassDB::is_parent_class(((Object *)(*p_input)[i])->get_class(), *p_native_name)) {
+ r_output->push_back(p_input[i]);
+ }
+ }
+}
+
+void godotsharp_array_filter_godot_objects_by_non_native(const Array *p_input, Array *r_output) {
+ memnew_placement(r_output, Array);
+
+ for (int i = 0; i < p_input->size(); ++i) {
+ CSharpInstance *si = CAST_CSHARP_INSTANCE(((Object *)(*p_input)[i])->get_script_instance());
+
+ if (si != nullptr) {
+ r_output->push_back(p_input[i]);
+ }
+ }
+}
+
+void godotsharp_ref_new_from_ref_counted_ptr(Ref<RefCounted> *r_dest, RefCounted *p_ref_counted_ptr) {
+ memnew_placement(r_dest, Ref<RefCounted>(p_ref_counted_ptr));
+}
+
+void godotsharp_ref_destroy(Ref<RefCounted> *p_instance) {
+ p_instance->~Ref();
+}
+
+void godotsharp_string_name_new_from_string(StringName *r_dest, const String *p_name) {
+ memnew_placement(r_dest, StringName(*p_name));
+}
+
+void godotsharp_node_path_new_from_string(NodePath *r_dest, const String *p_name) {
+ memnew_placement(r_dest, NodePath(*p_name));
+}
+
+void godotsharp_string_name_as_string(String *r_dest, const StringName *p_name) {
+ memnew_placement(r_dest, String(p_name->operator String()));
+}
+
+void godotsharp_node_path_as_string(String *r_dest, const NodePath *p_np) {
+ memnew_placement(r_dest, String(p_np->operator String()));
+}
+
+godot_packed_array godotsharp_packed_byte_array_new_mem_copy(const uint8_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedByteArray);
+ PackedByteArray *array = reinterpret_cast<PackedByteArray *>(&ret);
+ array->resize(p_length);
+ uint8_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(uint8_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_int32_array_new_mem_copy(const int32_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedInt32Array);
+ PackedInt32Array *array = reinterpret_cast<PackedInt32Array *>(&ret);
+ array->resize(p_length);
+ int32_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(int32_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_int64_array_new_mem_copy(const int64_t *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedInt64Array);
+ PackedInt64Array *array = reinterpret_cast<PackedInt64Array *>(&ret);
+ array->resize(p_length);
+ int64_t *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(int64_t));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_float32_array_new_mem_copy(const float *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedFloat32Array);
+ PackedFloat32Array *array = reinterpret_cast<PackedFloat32Array *>(&ret);
+ array->resize(p_length);
+ float *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(float));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_float64_array_new_mem_copy(const double *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedFloat64Array);
+ PackedFloat64Array *array = reinterpret_cast<PackedFloat64Array *>(&ret);
+ array->resize(p_length);
+ double *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(double));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_vector2_array_new_mem_copy(const Vector2 *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedVector2Array);
+ PackedVector2Array *array = reinterpret_cast<PackedVector2Array *>(&ret);
+ array->resize(p_length);
+ Vector2 *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Vector2));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_vector3_array_new_mem_copy(const Vector3 *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedVector3Array);
+ PackedVector3Array *array = reinterpret_cast<PackedVector3Array *>(&ret);
+ array->resize(p_length);
+ Vector3 *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Vector3));
+ return ret;
+}
+
+godot_packed_array godotsharp_packed_color_array_new_mem_copy(const Color *p_src, int32_t p_length) {
+ godot_packed_array ret;
+ memnew_placement(&ret, PackedColorArray);
+ PackedColorArray *array = reinterpret_cast<PackedColorArray *>(&ret);
+ array->resize(p_length);
+ Color *dst = array->ptrw();
+ memcpy(dst, p_src, p_length * sizeof(Color));
+ return ret;
+}
+
+void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String *p_element) {
+ r_dest->append(*p_element);
+}
+
+void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, void *p_trampoline,
+ const Object *p_object, Callable *r_callable) {
+ // TODO: Use pooling for ManagedCallable instances.
+ ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID();
+ CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, p_trampoline, objid));
+ memnew_placement(r_callable, Callable(managed_callable));
+}
+
+bool godotsharp_callable_get_data_for_marshalling(const Callable *p_callable,
+ GCHandleIntPtr *r_delegate_handle, void **r_trampoline, Object **r_object, StringName *r_name) {
+ if (p_callable->is_custom()) {
+ CallableCustom *custom = p_callable->get_custom();
+ CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
+
+ if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
+ ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
+ *r_delegate_handle = managed_callable->get_delegate();
+ *r_trampoline = managed_callable->get_trampoline();
+ *r_object = nullptr;
+ memnew_placement(r_name, StringName());
+ return true;
+ } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
+ SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
+ *r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
+ *r_object = ObjectDB::get_instance(signal_awaiter_callable->get_object());
+ memnew_placement(r_name, StringName(signal_awaiter_callable->get_signal()));
+ return true;
+ } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
+ EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
+ *r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
+ *r_object = ObjectDB::get_instance(event_signal_callable->get_object());
+ memnew_placement(r_name, StringName(event_signal_callable->get_signal()));
+ return true;
+ }
+
+ // Some other CallableCustom. We only support ManagedCallable.
+ *r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
+ *r_object = nullptr;
+ memnew_placement(r_name, StringName());
+ return false;
+ } else {
+ *r_delegate_handle = { nullptr };
+ *r_trampoline = nullptr;
+ *r_object = ObjectDB::get_instance(p_callable->get_object_id());
+ memnew_placement(r_name, StringName(p_callable->get_method()));
+ return true;
+ }
+}
+
+godot_variant godotsharp_callable_call(Callable *p_callable, const Variant **p_args, const int32_t p_arg_count, Callable::CallError *p_call_error) {
+ godot_variant ret;
+ memnew_placement(&ret, Variant);
+
+ Variant *ret_val = (Variant *)&ret;
+
+ p_callable->callp(p_args, p_arg_count, *ret_val, *p_call_error);
+
+ return ret;
+}
+
+void godotsharp_callable_call_deferred(Callable *p_callable, const Variant **p_args, const int32_t p_arg_count) {
+ p_callable->call_deferredp(p_args, p_arg_count);
+}
+
+godot_color godotsharp_color_from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
+ godot_color ret;
+ Color *dest = (Color *)&ret;
+ memnew_placement(dest, Color(Color::from_ok_hsl(p_h, p_s, p_l, p_alpha)));
+ return ret;
+}
+
+// GDNative functions
+
+// gdnative.h
+
+void godotsharp_method_bind_ptrcall(MethodBind *p_method_bind, Object *p_instance, const void **p_args, void *p_ret) {
+ p_method_bind->ptrcall(p_instance, p_args, p_ret);
+}
+
+godot_variant godotsharp_method_bind_call(MethodBind *p_method_bind, Object *p_instance, const godot_variant **p_args, const int32_t p_arg_count, Callable::CallError *p_call_error) {
+ godot_variant ret;
+ memnew_placement(&ret, Variant());
+
+ Variant *ret_val = (Variant *)&ret;
+
+ *ret_val = p_method_bind->call(p_instance, (const Variant **)p_args, p_arg_count, *p_call_error);
+
+ return ret;
+}
+
+// variant.h
+
+void godotsharp_variant_new_copy(godot_variant *r_dest, const Variant *p_src) {
+ memnew_placement(r_dest, Variant(*p_src));
+}
+
+void godotsharp_variant_new_string_name(godot_variant *r_dest, const StringName *p_s) {
+ memnew_placement(r_dest, Variant(*p_s));
+}
+
+void godotsharp_variant_new_node_path(godot_variant *r_dest, const NodePath *p_np) {
+ memnew_placement(r_dest, Variant(*p_np));
+}
+
+void godotsharp_variant_new_object(godot_variant *r_dest, const Object *p_obj) {
+ memnew_placement(r_dest, Variant(p_obj));
+}
+
+void godotsharp_variant_new_transform2d(godot_variant *r_dest, const Transform2D *p_t2d) {
+ memnew_placement(r_dest, Variant(*p_t2d));
+}
+
+void godotsharp_variant_new_basis(godot_variant *r_dest, const Basis *p_basis) {
+ memnew_placement(r_dest, Variant(*p_basis));
+}
+
+void godotsharp_variant_new_transform3d(godot_variant *r_dest, const Transform3D *p_trans) {
+ memnew_placement(r_dest, Variant(*p_trans));
+}
+
+void godotsharp_variant_new_projection(godot_variant *r_dest, const Projection *p_proj) {
+ memnew_placement(r_dest, Variant(*p_proj));
+}
+
+void godotsharp_variant_new_aabb(godot_variant *r_dest, const AABB *p_aabb) {
+ memnew_placement(r_dest, Variant(*p_aabb));
+}
+
+void godotsharp_variant_new_dictionary(godot_variant *r_dest, const Dictionary *p_dict) {
+ memnew_placement(r_dest, Variant(*p_dict));
+}
+
+void godotsharp_variant_new_array(godot_variant *r_dest, const Array *p_arr) {
+ memnew_placement(r_dest, Variant(*p_arr));
+}
+
+void godotsharp_variant_new_packed_byte_array(godot_variant *r_dest, const PackedByteArray *p_pba) {
+ memnew_placement(r_dest, Variant(*p_pba));
+}
+
+void godotsharp_variant_new_packed_int32_array(godot_variant *r_dest, const PackedInt32Array *p_pia) {
+ memnew_placement(r_dest, Variant(*p_pia));
+}
+
+void godotsharp_variant_new_packed_int64_array(godot_variant *r_dest, const PackedInt64Array *p_pia) {
+ memnew_placement(r_dest, Variant(*p_pia));
+}
+
+void godotsharp_variant_new_packed_float32_array(godot_variant *r_dest, const PackedFloat32Array *p_pra) {
+ memnew_placement(r_dest, Variant(*p_pra));
+}
+
+void godotsharp_variant_new_packed_float64_array(godot_variant *r_dest, const PackedFloat64Array *p_pra) {
+ memnew_placement(r_dest, Variant(*p_pra));
+}
+
+void godotsharp_variant_new_packed_string_array(godot_variant *r_dest, const PackedStringArray *p_psa) {
+ memnew_placement(r_dest, Variant(*p_psa));
+}
+
+void godotsharp_variant_new_packed_vector2_array(godot_variant *r_dest, const PackedVector2Array *p_pv2a) {
+ memnew_placement(r_dest, Variant(*p_pv2a));
+}
+
+void godotsharp_variant_new_packed_vector3_array(godot_variant *r_dest, const PackedVector3Array *p_pv3a) {
+ memnew_placement(r_dest, Variant(*p_pv3a));
+}
+
+void godotsharp_variant_new_packed_color_array(godot_variant *r_dest, const PackedColorArray *p_pca) {
+ memnew_placement(r_dest, Variant(*p_pca));
+}
+
+bool godotsharp_variant_as_bool(const Variant *p_self) {
+ return p_self->operator bool();
+}
+
+int64_t godotsharp_variant_as_int(const Variant *p_self) {
+ return p_self->operator int64_t();
+}
+
+double godotsharp_variant_as_float(const Variant *p_self) {
+ return p_self->operator double();
+}
+
+godot_string godotsharp_variant_as_string(const Variant *p_self) {
+ godot_string raw_dest;
+ String *dest = (String *)&raw_dest;
+ memnew_placement(dest, String(p_self->operator String()));
+ return raw_dest;
+}
+
+godot_vector2 godotsharp_variant_as_vector2(const Variant *p_self) {
+ godot_vector2 raw_dest;
+ Vector2 *dest = (Vector2 *)&raw_dest;
+ memnew_placement(dest, Vector2(p_self->operator Vector2()));
+ return raw_dest;
+}
+
+godot_vector2i godotsharp_variant_as_vector2i(const Variant *p_self) {
+ godot_vector2i raw_dest;
+ Vector2i *dest = (Vector2i *)&raw_dest;
+ memnew_placement(dest, Vector2i(p_self->operator Vector2i()));
+ return raw_dest;
+}
+
+godot_rect2 godotsharp_variant_as_rect2(const Variant *p_self) {
+ godot_rect2 raw_dest;
+ Rect2 *dest = (Rect2 *)&raw_dest;
+ memnew_placement(dest, Rect2(p_self->operator Rect2()));
+ return raw_dest;
+}
+
+godot_rect2i godotsharp_variant_as_rect2i(const Variant *p_self) {
+ godot_rect2i raw_dest;
+ Rect2i *dest = (Rect2i *)&raw_dest;
+ memnew_placement(dest, Rect2i(p_self->operator Rect2i()));
+ return raw_dest;
+}
+
+godot_vector3 godotsharp_variant_as_vector3(const Variant *p_self) {
+ godot_vector3 raw_dest;
+ Vector3 *dest = (Vector3 *)&raw_dest;
+ memnew_placement(dest, Vector3(p_self->operator Vector3()));
+ return raw_dest;
+}
+
+godot_vector3i godotsharp_variant_as_vector3i(const Variant *p_self) {
+ godot_vector3i raw_dest;
+ Vector3i *dest = (Vector3i *)&raw_dest;
+ memnew_placement(dest, Vector3i(p_self->operator Vector3i()));
+ return raw_dest;
+}
+
+godot_transform2d godotsharp_variant_as_transform2d(const Variant *p_self) {
+ godot_transform2d raw_dest;
+ Transform2D *dest = (Transform2D *)&raw_dest;
+ memnew_placement(dest, Transform2D(p_self->operator Transform2D()));
+ return raw_dest;
+}
+
+godot_vector4 godotsharp_variant_as_vector4(const Variant *p_self) {
+ godot_vector4 raw_dest;
+ Vector4 *dest = (Vector4 *)&raw_dest;
+ memnew_placement(dest, Vector4(p_self->operator Vector4()));
+ return raw_dest;
+}
+
+godot_vector4i godotsharp_variant_as_vector4i(const Variant *p_self) {
+ godot_vector4i raw_dest;
+ Vector4i *dest = (Vector4i *)&raw_dest;
+ memnew_placement(dest, Vector4i(p_self->operator Vector4i()));
+ return raw_dest;
+}
+
+godot_plane godotsharp_variant_as_plane(const Variant *p_self) {
+ godot_plane raw_dest;
+ Plane *dest = (Plane *)&raw_dest;
+ memnew_placement(dest, Plane(p_self->operator Plane()));
+ return raw_dest;
+}
+
+godot_quaternion godotsharp_variant_as_quaternion(const Variant *p_self) {
+ godot_quaternion raw_dest;
+ Quaternion *dest = (Quaternion *)&raw_dest;
+ memnew_placement(dest, Quaternion(p_self->operator Quaternion()));
+ return raw_dest;
+}
+
+godot_aabb godotsharp_variant_as_aabb(const Variant *p_self) {
+ godot_aabb raw_dest;
+ AABB *dest = (AABB *)&raw_dest;
+ memnew_placement(dest, AABB(p_self->operator ::AABB()));
+ return raw_dest;
+}
+
+godot_basis godotsharp_variant_as_basis(const Variant *p_self) {
+ godot_basis raw_dest;
+ Basis *dest = (Basis *)&raw_dest;
+ memnew_placement(dest, Basis(p_self->operator Basis()));
+ return raw_dest;
+}
+
+godot_transform3d godotsharp_variant_as_transform3d(const Variant *p_self) {
+ godot_transform3d raw_dest;
+ Transform3D *dest = (Transform3D *)&raw_dest;
+ memnew_placement(dest, Transform3D(p_self->operator Transform3D()));
+ return raw_dest;
+}
+
+godot_projection godotsharp_variant_as_projection(const Variant *p_self) {
+ godot_projection raw_dest;
+ Projection *dest = (Projection *)&raw_dest;
+ memnew_placement(dest, Projection(p_self->operator Projection()));
+ return raw_dest;
+}
+
+godot_color godotsharp_variant_as_color(const Variant *p_self) {
+ godot_color raw_dest;
+ Color *dest = (Color *)&raw_dest;
+ memnew_placement(dest, Color(p_self->operator Color()));
+ return raw_dest;
+}
+
+godot_string_name godotsharp_variant_as_string_name(const Variant *p_self) {
+ godot_string_name raw_dest;
+ StringName *dest = (StringName *)&raw_dest;
+ memnew_placement(dest, StringName(p_self->operator StringName()));
+ return raw_dest;
+}
+
+godot_node_path godotsharp_variant_as_node_path(const Variant *p_self) {
+ godot_node_path raw_dest;
+ NodePath *dest = (NodePath *)&raw_dest;
+ memnew_placement(dest, NodePath(p_self->operator NodePath()));
+ return raw_dest;
+}
+
+godot_rid godotsharp_variant_as_rid(const Variant *p_self) {
+ godot_rid raw_dest;
+ RID *dest = (RID *)&raw_dest;
+ memnew_placement(dest, RID(p_self->operator ::RID()));
+ return raw_dest;
+}
+
+godot_callable godotsharp_variant_as_callable(const Variant *p_self) {
+ godot_callable raw_dest;
+ Callable *dest = (Callable *)&raw_dest;
+ memnew_placement(dest, Callable(p_self->operator Callable()));
+ return raw_dest;
+}
+
+godot_signal godotsharp_variant_as_signal(const Variant *p_self) {
+ godot_signal raw_dest;
+ Signal *dest = (Signal *)&raw_dest;
+ memnew_placement(dest, Signal(p_self->operator Signal()));
+ return raw_dest;
+}
+
+godot_dictionary godotsharp_variant_as_dictionary(const Variant *p_self) {
+ godot_dictionary raw_dest;
+ Dictionary *dest = (Dictionary *)&raw_dest;
+ memnew_placement(dest, Dictionary(p_self->operator Dictionary()));
+ return raw_dest;
+}
+
+godot_array godotsharp_variant_as_array(const Variant *p_self) {
+ godot_array raw_dest;
+ Array *dest = (Array *)&raw_dest;
+ memnew_placement(dest, Array(p_self->operator Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_byte_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedByteArray *dest = (PackedByteArray *)&raw_dest;
+ memnew_placement(dest, PackedByteArray(p_self->operator PackedByteArray()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_int32_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedInt32Array *dest = (PackedInt32Array *)&raw_dest;
+ memnew_placement(dest, PackedInt32Array(p_self->operator PackedInt32Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_int64_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedInt64Array *dest = (PackedInt64Array *)&raw_dest;
+ memnew_placement(dest, PackedInt64Array(p_self->operator PackedInt64Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_float32_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedFloat32Array *dest = (PackedFloat32Array *)&raw_dest;
+ memnew_placement(dest, PackedFloat32Array(p_self->operator PackedFloat32Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_float64_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedFloat64Array *dest = (PackedFloat64Array *)&raw_dest;
+ memnew_placement(dest, PackedFloat64Array(p_self->operator PackedFloat64Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_string_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedStringArray *dest = (PackedStringArray *)&raw_dest;
+ memnew_placement(dest, PackedStringArray(p_self->operator PackedStringArray()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_vector2_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedVector2Array *dest = (PackedVector2Array *)&raw_dest;
+ memnew_placement(dest, PackedVector2Array(p_self->operator PackedVector2Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_vector3_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedVector3Array *dest = (PackedVector3Array *)&raw_dest;
+ memnew_placement(dest, PackedVector3Array(p_self->operator PackedVector3Array()));
+ return raw_dest;
+}
+
+godot_packed_array godotsharp_variant_as_packed_color_array(const Variant *p_self) {
+ godot_packed_array raw_dest;
+ PackedColorArray *dest = (PackedColorArray *)&raw_dest;
+ memnew_placement(dest, PackedColorArray(p_self->operator PackedColorArray()));
+ return raw_dest;
+}
+
+bool godotsharp_variant_equals(const godot_variant *p_a, const godot_variant *p_b) {
+ return *reinterpret_cast<const Variant *>(p_a) == *reinterpret_cast<const Variant *>(p_b);
+}
+
+// string.h
+
+void godotsharp_string_new_with_utf16_chars(String *r_dest, const char16_t *p_contents) {
+ memnew_placement(r_dest, String());
+ r_dest->parse_utf16(p_contents);
+}
+
+// string_name.h
+
+void godotsharp_string_name_new_copy(StringName *r_dest, const StringName *p_src) {
+ memnew_placement(r_dest, StringName(*p_src));
+}
+
+// node_path.h
+
+void godotsharp_node_path_new_copy(NodePath *r_dest, const NodePath *p_src) {
+ memnew_placement(r_dest, NodePath(*p_src));
+}
+
+// array.h
+
+void godotsharp_array_new(Array *r_dest) {
+ memnew_placement(r_dest, Array);
+}
+
+void godotsharp_array_new_copy(Array *r_dest, const Array *p_src) {
+ memnew_placement(r_dest, Array(*p_src));
+}
+
+godot_variant *godotsharp_array_ptrw(godot_array *p_self) {
+ return reinterpret_cast<godot_variant *>(&reinterpret_cast<Array *>(p_self)->operator[](0));
+}
+
+// dictionary.h
+
+void godotsharp_dictionary_new(Dictionary *r_dest) {
+ memnew_placement(r_dest, Dictionary);
+}
+
+void godotsharp_dictionary_new_copy(Dictionary *r_dest, const Dictionary *p_src) {
+ memnew_placement(r_dest, Dictionary(*p_src));
+}
+
+// destroy functions
+
+void godotsharp_packed_byte_array_destroy(PackedByteArray *p_self) {
+ p_self->~PackedByteArray();
+}
+
+void godotsharp_packed_int32_array_destroy(PackedInt32Array *p_self) {
+ p_self->~PackedInt32Array();
+}
+
+void godotsharp_packed_int64_array_destroy(PackedInt64Array *p_self) {
+ p_self->~PackedInt64Array();
+}
+
+void godotsharp_packed_float32_array_destroy(PackedFloat32Array *p_self) {
+ p_self->~PackedFloat32Array();
+}
+
+void godotsharp_packed_float64_array_destroy(PackedFloat64Array *p_self) {
+ p_self->~PackedFloat64Array();
+}
+
+void godotsharp_packed_string_array_destroy(PackedStringArray *p_self) {
+ p_self->~PackedStringArray();
+}
+
+void godotsharp_packed_vector2_array_destroy(PackedVector2Array *p_self) {
+ p_self->~PackedVector2Array();
+}
+
+void godotsharp_packed_vector3_array_destroy(PackedVector3Array *p_self) {
+ p_self->~PackedVector3Array();
+}
+
+void godotsharp_packed_color_array_destroy(PackedColorArray *p_self) {
+ p_self->~PackedColorArray();
+}
+
+void godotsharp_variant_destroy(Variant *p_self) {
+ p_self->~Variant();
+}
+
+void godotsharp_string_destroy(String *p_self) {
+ p_self->~String();
+}
+
+void godotsharp_string_name_destroy(StringName *p_self) {
+ p_self->~StringName();
+}
+
+void godotsharp_node_path_destroy(NodePath *p_self) {
+ p_self->~NodePath();
+}
+
+void godotsharp_signal_destroy(Signal *p_self) {
+ p_self->~Signal();
+}
+
+void godotsharp_callable_destroy(Callable *p_self) {
+ p_self->~Callable();
+}
+
+void godotsharp_array_destroy(Array *p_self) {
+ p_self->~Array();
+}
+
+void godotsharp_dictionary_destroy(Dictionary *p_self) {
+ p_self->~Dictionary();
+}
+
+// Array
+
+int32_t godotsharp_array_add(Array *p_self, const Variant *p_item) {
+ p_self->append(*p_item);
+ return p_self->size();
+}
+
+void godotsharp_array_duplicate(const Array *p_self, bool p_deep, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->duplicate(p_deep)));
+}
+
+int32_t godotsharp_array_index_of(const Array *p_self, const Variant *p_item) {
+ return p_self->find(*p_item);
+}
+
+void godotsharp_array_insert(Array *p_self, int32_t p_index, const Variant *p_item) {
+ p_self->insert(p_index, *p_item);
+}
+
+void godotsharp_array_remove_at(Array *p_self, int32_t p_index) {
+ p_self->remove_at(p_index);
+}
+
+int32_t godotsharp_array_resize(Array *p_self, int32_t p_new_size) {
+ return (int32_t)p_self->resize(p_new_size);
+}
+
+void godotsharp_array_shuffle(Array *p_self) {
+ p_self->shuffle();
+}
+
+void godotsharp_array_to_string(const Array *p_self, String *r_str) {
+ *r_str = Variant(*p_self).operator String();
+}
+
+// Dictionary
+
+bool godotsharp_dictionary_try_get_value(const Dictionary *p_self, const Variant *p_key, Variant *r_value) {
+ const Variant *ret = p_self->getptr(*p_key);
+ if (ret == nullptr) {
+ memnew_placement(r_value, Variant());
+ return false;
+ }
+ memnew_placement(r_value, Variant(*ret));
+ return true;
+}
+
+void godotsharp_dictionary_set_value(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
+ p_self->operator[](*p_key) = *p_value;
+}
+
+void godotsharp_dictionary_keys(const Dictionary *p_self, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->keys()));
+}
+
+void godotsharp_dictionary_values(const Dictionary *p_self, Array *r_dest) {
+ memnew_placement(r_dest, Array(p_self->values()));
+}
+
+int32_t godotsharp_dictionary_count(const Dictionary *p_self) {
+ return p_self->size();
+}
+
+void godotsharp_dictionary_key_value_pair_at(const Dictionary *p_self, int32_t p_index, Variant *r_key, Variant *r_value) {
+ memnew_placement(r_key, Variant(p_self->get_key_at_index(p_index)));
+ memnew_placement(r_value, Variant(p_self->get_value_at_index(p_index)));
+}
+
+void godotsharp_dictionary_add(Dictionary *p_self, const Variant *p_key, const Variant *p_value) {
+ p_self->operator[](*p_key) = *p_value;
+}
+
+void godotsharp_dictionary_clear(Dictionary *p_self) {
+ p_self->clear();
+}
+
+bool godotsharp_dictionary_contains_key(const Dictionary *p_self, const Variant *p_key) {
+ return p_self->has(*p_key);
+}
+
+void godotsharp_dictionary_duplicate(const Dictionary *p_self, bool p_deep, Dictionary *r_dest) {
+ memnew_placement(r_dest, Dictionary(p_self->duplicate(p_deep)));
+}
+
+bool godotsharp_dictionary_remove_key(Dictionary *p_self, const Variant *p_key) {
+ return p_self->erase(*p_key);
+}
+
+void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) {
+ *r_str = Variant(*p_self).operator String();
+}
+
+void godotsharp_string_simplify_path(const String *p_self, String *r_simplified_path) {
+ memnew_placement(r_simplified_path, String(p_self->simplify_path()));
+}
+
+void godotsharp_string_to_camel_case(const String *p_self, String *r_camel_case) {
+ memnew_placement(r_camel_case, String(p_self->to_camel_case()));
+}
+
+void godotsharp_string_to_pascal_case(const String *p_self, String *r_pascal_case) {
+ memnew_placement(r_pascal_case, String(p_self->to_pascal_case()));
+}
+
+void godotsharp_string_to_snake_case(const String *p_self, String *r_snake_case) {
+ memnew_placement(r_snake_case, String(p_self->to_snake_case()));
+}
+
+void godotsharp_node_path_get_as_property_path(const NodePath *p_ptr, NodePath *r_dest) {
+ memnew_placement(r_dest, NodePath(p_ptr->get_as_property_path()));
+}
+
+void godotsharp_node_path_get_concatenated_names(const NodePath *p_self, String *r_subnames) {
+ memnew_placement(r_subnames, String(p_self->get_concatenated_names()));
+}
+
+void godotsharp_node_path_get_concatenated_subnames(const NodePath *p_self, String *r_subnames) {
+ memnew_placement(r_subnames, String(p_self->get_concatenated_subnames()));
+}
+
+void godotsharp_node_path_get_name(const NodePath *p_self, uint32_t p_idx, String *r_name) {
+ memnew_placement(r_name, String(p_self->get_name(p_idx)));
+}
+
+int32_t godotsharp_node_path_get_name_count(const NodePath *p_self) {
+ return p_self->get_name_count();
+}
+
+void godotsharp_node_path_get_subname(const NodePath *p_self, uint32_t p_idx, String *r_subname) {
+ memnew_placement(r_subname, String(p_self->get_subname(p_idx)));
+}
+
+int32_t godotsharp_node_path_get_subname_count(const NodePath *p_self) {
+ return p_self->get_subname_count();
+}
+
+bool godotsharp_node_path_is_absolute(const NodePath *p_self) {
+ return p_self->is_absolute();
+}
+
+void godotsharp_randomize() {
+ Math::randomize();
+}
+
+uint32_t godotsharp_randi() {
+ return Math::rand();
+}
+
+float godotsharp_randf() {
+ return Math::randf();
+}
+
+int32_t godotsharp_randi_range(int32_t p_from, int32_t p_to) {
+ return Math::random(p_from, p_to);
+}
+
+double godotsharp_randf_range(double p_from, double p_to) {
+ return Math::random(p_from, p_to);
+}
+
+double godotsharp_randfn(double p_mean, double p_deviation) {
+ return Math::randfn(p_mean, p_deviation);
+}
+
+void godotsharp_seed(uint64_t p_seed) {
+ Math::seed(p_seed);
+}
+
+uint32_t godotsharp_rand_from_seed(uint64_t p_seed, uint64_t *r_new_seed) {
+ uint32_t ret = Math::rand_from_seed(&p_seed);
+ *r_new_seed = p_seed;
+ return ret;
+}
+
+void godotsharp_weakref(Object *p_ptr, Ref<RefCounted> *r_weak_ref) {
+ if (!p_ptr) {
+ return;
+ }
+
+ Ref<WeakRef> wref;
+ RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
+
+ if (rc) {
+ Ref<RefCounted> r = rc;
+ if (!r.is_valid()) {
+ return;
+ }
+
+ wref.instantiate();
+ wref->set_ref(r);
+ } else {
+ wref.instantiate();
+ wref->set_obj(p_ptr);
+ }
+
+ memnew_placement(r_weak_ref, Ref<RefCounted>(wref));
+}
+
+void godotsharp_str(const godot_array *p_what, godot_string *r_ret) {
+ String &str = *memnew_placement(r_ret, String);
+ const Array &what = *reinterpret_cast<const Array *>(p_what);
+
+ for (int i = 0; i < what.size(); i++) {
+ String os = what[i].operator String();
+
+ if (i == 0) {
+ str = os;
+ } else {
+ str += os;
+ }
+ }
+}
+
+void godotsharp_print(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_print_rich(const godot_string *p_what) {
+ print_line_rich(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printerr(const godot_string *p_what) {
+ print_error(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printt(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_prints(const godot_string *p_what) {
+ print_line(*reinterpret_cast<const String *>(p_what));
+}
+
+void godotsharp_printraw(const godot_string *p_what) {
+ OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data());
+}
+
+void godotsharp_pusherror(const godot_string *p_str) {
+ ERR_PRINT(*reinterpret_cast<const String *>(p_str));
+}
+
+void godotsharp_pushwarning(const godot_string *p_str) {
+ WARN_PRINT(*reinterpret_cast<const String *>(p_str));
+}
+
+void godotsharp_var_to_str(const godot_variant *p_var, godot_string *r_ret) {
+ const Variant &var = *reinterpret_cast<const Variant *>(p_var);
+ String &vars = *memnew_placement(r_ret, String);
+ VariantWriter::write_to_string(var, vars);
+}
+
+void godotsharp_str_to_var(const godot_string *p_str, godot_variant *r_ret) {
+ Variant ret;
+
+ VariantParser::StreamString ss;
+ ss.s = *reinterpret_cast<const String *>(p_str);
+
+ String errs;
+ int line;
+ Error err = VariantParser::parse(&ss, ret, errs, line);
+ if (err != OK) {
+ String err_str = "Parse error at line " + itos(line) + ": " + errs + ".";
+ ERR_PRINT(err_str);
+ ret = err_str;
+ }
+ memnew_placement(r_ret, Variant(ret));
+}
+
+void godotsharp_var_to_bytes(const godot_variant *p_var, bool p_full_objects, godot_packed_array *r_bytes) {
+ const Variant &var = *reinterpret_cast<const Variant *>(p_var);
+ PackedByteArray &bytes = *memnew_placement(r_bytes, PackedByteArray);
+
+ int len;
+ Error err = encode_variant(var, nullptr, len, p_full_objects);
+ ERR_FAIL_COND_MSG(err != OK, "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).");
+
+ bytes.resize(len);
+ encode_variant(var, bytes.ptrw(), len, p_full_objects);
+}
+
+void godotsharp_bytes_to_var(const godot_packed_array *p_bytes, bool p_allow_objects, godot_variant *r_ret) {
+ const PackedByteArray *bytes = reinterpret_cast<const PackedByteArray *>(p_bytes);
+ Variant ret;
+ Error err = decode_variant(ret, bytes->ptr(), bytes->size(), nullptr, p_allow_objects);
+ if (err != OK) {
+ ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
+ }
+ memnew_placement(r_ret, Variant(ret));
+}
+
+int godotsharp_hash(const godot_variant *p_var) {
+ return reinterpret_cast<const Variant *>(p_var)->hash();
+}
+
+void godotsharp_convert(const godot_variant *p_what, int32_t p_type, godot_variant *r_ret) {
+ const Variant *args[1] = { reinterpret_cast<const Variant *>(p_what) };
+ Callable::CallError ce;
+ Variant ret;
+ Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ memnew_placement(r_ret, Variant);
+ ERR_FAIL_MSG("Unable to convert parameter from '" +
+ Variant::get_type_name(reinterpret_cast<const Variant *>(p_what)->get_type()) +
+ "' to '" + Variant::get_type_name(Variant::Type(p_type)) + "'.");
+ }
+ memnew_placement(r_ret, Variant(ret));
+}
+
+Object *godotsharp_instance_from_id(uint64_t p_instance_id) {
+ return ObjectDB::get_instance(ObjectID(p_instance_id));
+}
+
+void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) {
+#ifdef DEBUG_ENABLED
+ // Cannot happen in C#; would get an ObjectDisposedException instead.
+ CRASH_COND(p_ptr == nullptr);
+#endif
+ // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop.
+ memnew_placement(r_str,
+ String("<" + p_ptr->get_class() + "#" + itos(p_ptr->get_instance_id()) + ">"));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+// The order in this array must match the declaration order of
+// the methods in 'GodotSharp/Core/NativeInterop/NativeFuncs.cs'.
+static const void *unmanaged_callbacks[]{
+ (void *)godotsharp_method_bind_get_method,
+ (void *)godotsharp_get_class_constructor,
+ (void *)godotsharp_engine_get_singleton,
+ (void *)godotsharp_stack_info_vector_resize,
+ (void *)godotsharp_stack_info_vector_destroy,
+ (void *)godotsharp_internal_script_debugger_send_error,
+ (void *)godotsharp_internal_script_debugger_is_active,
+ (void *)godotsharp_internal_object_get_associated_gchandle,
+ (void *)godotsharp_internal_object_disposed,
+ (void *)godotsharp_internal_refcounted_disposed,
+ (void *)godotsharp_internal_signal_awaiter_connect,
+ (void *)godotsharp_internal_tie_native_managed_to_unmanaged,
+ (void *)godotsharp_internal_tie_user_managed_to_unmanaged,
+ (void *)godotsharp_internal_tie_managed_to_unmanaged_with_pre_setup,
+ (void *)godotsharp_internal_unmanaged_get_script_instance_managed,
+ (void *)godotsharp_internal_unmanaged_get_instance_binding_managed,
+ (void *)godotsharp_internal_unmanaged_instance_binding_create_managed,
+ (void *)godotsharp_internal_new_csharp_script,
+ (void *)godotsharp_internal_script_load,
+ (void *)godotsharp_internal_reload_registered_script,
+ (void *)godotsharp_array_filter_godot_objects_by_native,
+ (void *)godotsharp_array_filter_godot_objects_by_non_native,
+ (void *)godotsharp_ref_new_from_ref_counted_ptr,
+ (void *)godotsharp_ref_destroy,
+ (void *)godotsharp_string_name_new_from_string,
+ (void *)godotsharp_node_path_new_from_string,
+ (void *)godotsharp_string_name_as_string,
+ (void *)godotsharp_node_path_as_string,
+ (void *)godotsharp_packed_byte_array_new_mem_copy,
+ (void *)godotsharp_packed_int32_array_new_mem_copy,
+ (void *)godotsharp_packed_int64_array_new_mem_copy,
+ (void *)godotsharp_packed_float32_array_new_mem_copy,
+ (void *)godotsharp_packed_float64_array_new_mem_copy,
+ (void *)godotsharp_packed_vector2_array_new_mem_copy,
+ (void *)godotsharp_packed_vector3_array_new_mem_copy,
+ (void *)godotsharp_packed_color_array_new_mem_copy,
+ (void *)godotsharp_packed_string_array_add,
+ (void *)godotsharp_callable_new_with_delegate,
+ (void *)godotsharp_callable_get_data_for_marshalling,
+ (void *)godotsharp_callable_call,
+ (void *)godotsharp_callable_call_deferred,
+ (void *)godotsharp_color_from_ok_hsl,
+ (void *)godotsharp_method_bind_ptrcall,
+ (void *)godotsharp_method_bind_call,
+ (void *)godotsharp_variant_new_string_name,
+ (void *)godotsharp_variant_new_copy,
+ (void *)godotsharp_variant_new_node_path,
+ (void *)godotsharp_variant_new_object,
+ (void *)godotsharp_variant_new_transform2d,
+ (void *)godotsharp_variant_new_basis,
+ (void *)godotsharp_variant_new_transform3d,
+ (void *)godotsharp_variant_new_projection,
+ (void *)godotsharp_variant_new_aabb,
+ (void *)godotsharp_variant_new_dictionary,
+ (void *)godotsharp_variant_new_array,
+ (void *)godotsharp_variant_new_packed_byte_array,
+ (void *)godotsharp_variant_new_packed_int32_array,
+ (void *)godotsharp_variant_new_packed_int64_array,
+ (void *)godotsharp_variant_new_packed_float32_array,
+ (void *)godotsharp_variant_new_packed_float64_array,
+ (void *)godotsharp_variant_new_packed_string_array,
+ (void *)godotsharp_variant_new_packed_vector2_array,
+ (void *)godotsharp_variant_new_packed_vector3_array,
+ (void *)godotsharp_variant_new_packed_color_array,
+ (void *)godotsharp_variant_as_bool,
+ (void *)godotsharp_variant_as_int,
+ (void *)godotsharp_variant_as_float,
+ (void *)godotsharp_variant_as_string,
+ (void *)godotsharp_variant_as_vector2,
+ (void *)godotsharp_variant_as_vector2i,
+ (void *)godotsharp_variant_as_rect2,
+ (void *)godotsharp_variant_as_rect2i,
+ (void *)godotsharp_variant_as_vector3,
+ (void *)godotsharp_variant_as_vector3i,
+ (void *)godotsharp_variant_as_transform2d,
+ (void *)godotsharp_variant_as_vector4,
+ (void *)godotsharp_variant_as_vector4i,
+ (void *)godotsharp_variant_as_plane,
+ (void *)godotsharp_variant_as_quaternion,
+ (void *)godotsharp_variant_as_aabb,
+ (void *)godotsharp_variant_as_basis,
+ (void *)godotsharp_variant_as_transform3d,
+ (void *)godotsharp_variant_as_projection,
+ (void *)godotsharp_variant_as_color,
+ (void *)godotsharp_variant_as_string_name,
+ (void *)godotsharp_variant_as_node_path,
+ (void *)godotsharp_variant_as_rid,
+ (void *)godotsharp_variant_as_callable,
+ (void *)godotsharp_variant_as_signal,
+ (void *)godotsharp_variant_as_dictionary,
+ (void *)godotsharp_variant_as_array,
+ (void *)godotsharp_variant_as_packed_byte_array,
+ (void *)godotsharp_variant_as_packed_int32_array,
+ (void *)godotsharp_variant_as_packed_int64_array,
+ (void *)godotsharp_variant_as_packed_float32_array,
+ (void *)godotsharp_variant_as_packed_float64_array,
+ (void *)godotsharp_variant_as_packed_string_array,
+ (void *)godotsharp_variant_as_packed_vector2_array,
+ (void *)godotsharp_variant_as_packed_vector3_array,
+ (void *)godotsharp_variant_as_packed_color_array,
+ (void *)godotsharp_variant_equals,
+ (void *)godotsharp_string_new_with_utf16_chars,
+ (void *)godotsharp_string_name_new_copy,
+ (void *)godotsharp_node_path_new_copy,
+ (void *)godotsharp_array_new,
+ (void *)godotsharp_array_new_copy,
+ (void *)godotsharp_array_ptrw,
+ (void *)godotsharp_dictionary_new,
+ (void *)godotsharp_dictionary_new_copy,
+ (void *)godotsharp_packed_byte_array_destroy,
+ (void *)godotsharp_packed_int32_array_destroy,
+ (void *)godotsharp_packed_int64_array_destroy,
+ (void *)godotsharp_packed_float32_array_destroy,
+ (void *)godotsharp_packed_float64_array_destroy,
+ (void *)godotsharp_packed_string_array_destroy,
+ (void *)godotsharp_packed_vector2_array_destroy,
+ (void *)godotsharp_packed_vector3_array_destroy,
+ (void *)godotsharp_packed_color_array_destroy,
+ (void *)godotsharp_variant_destroy,
+ (void *)godotsharp_string_destroy,
+ (void *)godotsharp_string_name_destroy,
+ (void *)godotsharp_node_path_destroy,
+ (void *)godotsharp_signal_destroy,
+ (void *)godotsharp_callable_destroy,
+ (void *)godotsharp_array_destroy,
+ (void *)godotsharp_dictionary_destroy,
+ (void *)godotsharp_array_add,
+ (void *)godotsharp_array_duplicate,
+ (void *)godotsharp_array_index_of,
+ (void *)godotsharp_array_insert,
+ (void *)godotsharp_array_remove_at,
+ (void *)godotsharp_array_resize,
+ (void *)godotsharp_array_shuffle,
+ (void *)godotsharp_array_to_string,
+ (void *)godotsharp_dictionary_try_get_value,
+ (void *)godotsharp_dictionary_set_value,
+ (void *)godotsharp_dictionary_keys,
+ (void *)godotsharp_dictionary_values,
+ (void *)godotsharp_dictionary_count,
+ (void *)godotsharp_dictionary_key_value_pair_at,
+ (void *)godotsharp_dictionary_add,
+ (void *)godotsharp_dictionary_clear,
+ (void *)godotsharp_dictionary_contains_key,
+ (void *)godotsharp_dictionary_duplicate,
+ (void *)godotsharp_dictionary_remove_key,
+ (void *)godotsharp_dictionary_to_string,
+ (void *)godotsharp_string_simplify_path,
+ (void *)godotsharp_string_to_camel_case,
+ (void *)godotsharp_string_to_pascal_case,
+ (void *)godotsharp_string_to_snake_case,
+ (void *)godotsharp_node_path_get_as_property_path,
+ (void *)godotsharp_node_path_get_concatenated_names,
+ (void *)godotsharp_node_path_get_concatenated_subnames,
+ (void *)godotsharp_node_path_get_name,
+ (void *)godotsharp_node_path_get_name_count,
+ (void *)godotsharp_node_path_get_subname,
+ (void *)godotsharp_node_path_get_subname_count,
+ (void *)godotsharp_node_path_is_absolute,
+ (void *)godotsharp_bytes_to_var,
+ (void *)godotsharp_convert,
+ (void *)godotsharp_hash,
+ (void *)godotsharp_instance_from_id,
+ (void *)godotsharp_print,
+ (void *)godotsharp_print_rich,
+ (void *)godotsharp_printerr,
+ (void *)godotsharp_printraw,
+ (void *)godotsharp_prints,
+ (void *)godotsharp_printt,
+ (void *)godotsharp_randf,
+ (void *)godotsharp_randi,
+ (void *)godotsharp_randomize,
+ (void *)godotsharp_randf_range,
+ (void *)godotsharp_randfn,
+ (void *)godotsharp_randi_range,
+ (void *)godotsharp_rand_from_seed,
+ (void *)godotsharp_seed,
+ (void *)godotsharp_weakref,
+ (void *)godotsharp_str,
+ (void *)godotsharp_str_to_var,
+ (void *)godotsharp_var_to_bytes,
+ (void *)godotsharp_var_to_str,
+ (void *)godotsharp_pusherror,
+ (void *)godotsharp_pushwarning,
+ (void *)godotsharp_object_to_string,
+};
+
+const void **godotsharp::get_runtime_interop_funcs(int32_t &r_size) {
+ r_size = sizeof(unmanaged_callbacks);
+ return unmanaged_callbacks;
+}
diff --git a/modules/mono/glue/runtime_interop.h b/modules/mono/glue/runtime_interop.h
new file mode 100644
index 0000000000..c1be4db7ab
--- /dev/null
+++ b/modules/mono/glue/runtime_interop.h
@@ -0,0 +1,40 @@
+/**************************************************************************/
+/* runtime_interop.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 RUNTIME_INTEROP_H
+#define RUNTIME_INTEROP_H
+
+#include "core/typedefs.h"
+
+namespace godotsharp {
+const void **get_runtime_interop_funcs(int32_t &r_size);
+}
+
+#endif // RUNTIME_INTEROP_H
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
deleted file mode 100644
index c60e7c4869..0000000000
--- a/modules/mono/glue/scene_tree_glue.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*************************************************************************/
-/* scene_tree_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/object/class_db.h"
-#include "core/string/string_name.h"
-#include "core/variant/array.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-
-#include "../csharp_script.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringName *group, MonoReflectionType *refltype) {
- List<Node *> nodes;
- Array ret;
-
- // Retrieve all the nodes in the group
- ptr->get_nodes_in_group(*group, &nodes);
-
- // No need to bother if the group is empty
- if (!nodes.is_empty()) {
- MonoType *elem_type = mono_reflection_type_get_type(refltype);
- MonoClass *mono_class = mono_class_from_mono_type(elem_type);
- GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
-
- if (klass == GDMonoUtils::get_class_native_base(klass)) {
- // If we're trying to get native objects, just check the inheritance list
- StringName native_class_name = GDMonoUtils::get_native_godot_class_name(klass);
- for (int i = 0; i < nodes.size(); ++i) {
- if (ClassDB::is_parent_class(nodes[i]->get_class(), native_class_name)) {
- ret.push_back(nodes[i]);
- }
- }
- } else {
- // If we're trying to get csharpscript instances, get the mono object and compare the classes
- for (int i = 0; i < nodes.size(); ++i) {
- CSharpInstance *si = CAST_CSHARP_INSTANCE(nodes[i]->get_script_instance());
-
- if (si != nullptr) {
- MonoObject *obj = si->get_mono_object();
- if (obj != nullptr && mono_object_get_class(obj) == mono_class) {
- ret.push_back(nodes[i]);
- }
- }
- }
- }
- }
-
- return memnew(Array(ret));
-}
-
-void godot_register_scene_tree_icalls() {
- GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp
deleted file mode 100644
index fc6b13ceb3..0000000000
--- a/modules/mono/glue/string_glue.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*************************************************************************/
-/* string_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/string/ustring.h"
-#include "core/templates/vector.h"
-#include "core/variant/variant.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-MonoArray *godot_icall_String_md5_buffer(MonoString *p_str) {
- Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_buffer();
- // TODO Check possible Array/Vector<uint8_t> problem?
- return GDMonoMarshal::Array_to_mono_array(Variant(ret));
-}
-
-MonoString *godot_icall_String_md5_text(MonoString *p_str) {
- String ret = GDMonoMarshal::mono_string_to_godot(p_str).md5_text();
- return GDMonoMarshal::mono_string_from_godot(ret);
-}
-
-int godot_icall_String_rfind(MonoString *p_str, MonoString *p_what, int p_from) {
- String what = GDMonoMarshal::mono_string_to_godot(p_what);
- return GDMonoMarshal::mono_string_to_godot(p_str).rfind(what, p_from);
-}
-
-int godot_icall_String_rfindn(MonoString *p_str, MonoString *p_what, int p_from) {
- String what = GDMonoMarshal::mono_string_to_godot(p_what);
- return GDMonoMarshal::mono_string_to_godot(p_str).rfindn(what, p_from);
-}
-
-MonoArray *godot_icall_String_sha256_buffer(MonoString *p_str) {
- Vector<uint8_t> ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_buffer();
- return GDMonoMarshal::Array_to_mono_array(Variant(ret));
-}
-
-MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
- String ret = GDMonoMarshal::mono_string_to_godot(p_str).sha256_text();
- return GDMonoMarshal::mono_string_from_godot(ret);
-}
-
-MonoString *godot_icall_String_simplify_path(MonoString *p_str) {
- String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path();
- return GDMonoMarshal::mono_string_from_godot(ret);
-}
-
-void godot_register_string_icalls() {
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
- GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
deleted file mode 100644
index 46d15316ba..0000000000
--- a/modules/mono/glue/string_name_glue.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*************************************************************************/
-/* string_name_glue.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. */
-/*************************************************************************/
-
-#ifdef MONO_GLUE_ENABLED
-
-#include "core/string/string_name.h"
-#include "core/string/ustring.h"
-
-#include "../mono_gd/gd_mono_marshal.h"
-
-StringName *godot_icall_StringName_Ctor(MonoString *p_path) {
- return memnew(StringName(GDMonoMarshal::mono_string_to_godot(p_path)));
-}
-
-void godot_icall_StringName_Dtor(StringName *p_ptr) {
- ERR_FAIL_NULL(p_ptr);
- memdelete(p_ptr);
-}
-
-MonoString *godot_icall_StringName_operator_String(StringName *p_np) {
- return GDMonoMarshal::mono_string_from_godot(p_np->operator String());
-}
-
-MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
- return (MonoBoolean)(*p_ptr == StringName());
-}
-
-void godot_register_string_name_icalls() {
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String);
- GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty);
-}
-
-#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index e5f1abe8d7..08eeffc3db 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -1,46 +1,42 @@
-/*************************************************************************/
-/* godotsharp_defs.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godotsharp_defs.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GODOTSHARP_DEFS_H
#define GODOTSHARP_DEFS_H
#define BINDINGS_NAMESPACE "Godot"
#define BINDINGS_NAMESPACE_COLLECTIONS BINDINGS_NAMESPACE ".Collections"
-#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
-#define BINDINGS_PTR_FIELD "ptr"
-#define BINDINGS_NATIVE_NAME_FIELD "nativeName"
#define API_SOLUTION_NAME "GodotSharp"
#define CORE_API_ASSEMBLY_NAME "GodotSharp"
#define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
#define TOOLS_ASM_NAME "GodotTools"
-#define TOOLS_PROJECT_EDITOR_ASM_NAME "GodotTools.ProjectEditor"
#define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
#define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index f17b24e399..d08dedcfcb 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godotsharp_dirs.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godotsharp_dirs.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "godotsharp_dirs.h"
@@ -64,7 +64,7 @@ String _get_expected_build_config() {
String _get_mono_user_dir() {
#ifdef TOOLS_ENABLED
if (EditorPaths::get_singleton()) {
- return EditorPaths::get_singleton()->get_data_dir().plus_file("mono");
+ return EditorPaths::get_singleton()->get_data_dir().path_join("mono");
} else {
String settings_path;
@@ -72,167 +72,86 @@ String _get_mono_user_dir() {
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
// On macOS, look outside .app bundle, since .app bundle is read-only.
- if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.plus_file("..").simplify_path().ends_with("Contents")) {
- exe_dir = exe_dir.plus_file("../../..").simplify_path();
+ if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
+ exe_dir = exe_dir.path_join("../../..").simplify_path();
}
Ref<DirAccess> d = DirAccess::create_for_path(exe_dir);
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
// contain yourself
- settings_path = exe_dir.plus_file("editor_data");
+ settings_path = exe_dir.path_join("editor_data");
} else {
- settings_path = OS::get_singleton()->get_data_path().plus_file(OS::get_singleton()->get_godot_dir_name());
+ settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
}
- return settings_path.plus_file("mono");
+ return settings_path.path_join("mono");
}
#else
- return OS::get_singleton()->get_user_data_dir().plus_file("mono");
+ return OS::get_singleton()->get_user_data_dir().path_join("mono");
#endif
}
class _GodotSharpDirs {
public:
- String res_data_dir;
String res_metadata_dir;
- String res_assemblies_base_dir;
- String res_assemblies_dir;
- String res_config_dir;
- String res_temp_dir;
- String res_temp_assemblies_base_dir;
String res_temp_assemblies_dir;
String mono_user_dir;
- String mono_logs_dir;
+ String api_assemblies_dir;
#ifdef TOOLS_ENABLED
- String mono_solutions_dir;
String build_logs_dir;
-
- String sln_filepath;
- String csproj_filepath;
-
String data_editor_tools_dir;
- String data_editor_prebuilt_api_dir;
-#else
- // Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
- // Only defined on export templates. Used when exporting assemblies outside of PCKs.
- String data_game_assemblies_dir;
-#endif
-
- String data_mono_etc_dir;
- String data_mono_lib_dir;
-
-#ifdef WINDOWS_ENABLED
- String data_mono_bin_dir;
#endif
private:
_GodotSharpDirs() {
- res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono");
- res_metadata_dir = res_data_dir.plus_file("metadata");
- res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
- res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
- res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
+ String res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono");
+ res_metadata_dir = res_data_dir.path_join("metadata");
// TODO use paths from csproj
- res_temp_dir = res_data_dir.plus_file("temp");
- res_temp_assemblies_base_dir = res_temp_dir.plus_file("bin");
- res_temp_assemblies_dir = res_temp_assemblies_base_dir.plus_file(_get_expected_build_config());
+ res_temp_assemblies_dir = res_data_dir.path_join("temp").path_join("bin").path_join(_get_expected_build_config());
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
mono_user_dir = "user://";
#else
mono_user_dir = _get_mono_user_dir();
#endif
- mono_logs_dir = mono_user_dir.plus_file("mono_logs");
-
-#ifdef TOOLS_ENABLED
- mono_solutions_dir = mono_user_dir.plus_file("solutions");
- build_logs_dir = mono_user_dir.plus_file("build_logs");
-
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
-
- String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
-
- sln_filepath = base_path.plus_file(appname_safe + ".sln");
- csproj_filepath = base_path.plus_file(appname_safe + ".csproj");
-#endif
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
+ String res_dir = OS::get_singleton()->get_bundle_resource_dir();
#ifdef TOOLS_ENABLED
-
- String data_dir_root = exe_dir.plus_file("GodotSharp");
- data_editor_tools_dir = data_dir_root.plus_file("Tools");
- data_editor_prebuilt_api_dir = data_dir_root.plus_file("Api");
-
- String data_mono_root_dir = data_dir_root.plus_file("Mono");
- data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
-
-#ifdef ANDROID_ENABLED
- data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
-#else
- data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
-#endif
-
-#ifdef WINDOWS_ENABLED
- data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
-#endif
-
+ String data_dir_root = exe_dir.path_join("GodotSharp");
+ data_editor_tools_dir = data_dir_root.path_join("Tools");
+ String api_assemblies_base_dir = data_dir_root.path_join("Api");
+ build_logs_dir = mono_user_dir.path_join("build_logs");
#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
- data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
+ data_editor_tools_dir = res_dir.path_join("GodotSharp").path_join("Tools");
}
-
- if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
- data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
- }
-
- 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");
+ if (!DirAccess::exists(api_assemblies_base_dir)) {
+ api_assemblies_base_dir = res_dir.path_join("GodotSharp").path_join("Api");
}
#endif
-
-#else
-
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
+ api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
+#else // TOOLS_ENABLED
+ String arch = Engine::get_singleton()->get_architecture_name();
+ String appname = GLOBAL_GET("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- String data_dir_root = exe_dir.plus_file("data_" + appname_safe);
+ String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
if (!DirAccess::exists(data_dir_root)) {
- data_dir_root = exe_dir.plus_file("data_Godot");
+ data_dir_root = exe_dir.path_join("data_Godot_" + arch);
}
-
- String data_mono_root_dir = data_dir_root.plus_file("Mono");
- data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
-
-#ifdef ANDROID_ENABLED
- data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
-#else
- data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
- data_game_assemblies_dir = data_dir_root.plus_file("Assemblies");
-#endif
-
-#ifdef WINDOWS_ENABLED
- data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
-#endif
-
#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");
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
}
-
- if (!DirAccess::exists(data_game_assemblies_dir)) {
- data_game_assemblies_dir = exe_dir.plus_file("../Resources/GodotSharp/Assemblies");
+ if (!DirAccess::exists(data_dir_root)) {
+ data_dir_root = res_dir.path_join("data_Godot_" + arch);
}
#endif
-
+ api_assemblies_dir = data_dir_root;
#endif
}
@@ -243,87 +162,30 @@ public:
}
};
-String get_res_data_dir() {
- return _GodotSharpDirs::get_singleton().res_data_dir;
-}
-
String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
-String get_res_assemblies_base_dir() {
- return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
-}
-
-String get_res_assemblies_dir() {
- return _GodotSharpDirs::get_singleton().res_assemblies_dir;
-}
-
-String get_res_config_dir() {
- return _GodotSharpDirs::get_singleton().res_config_dir;
-}
-
-String get_res_temp_dir() {
- return _GodotSharpDirs::get_singleton().res_temp_dir;
-}
-
-String get_res_temp_assemblies_base_dir() {
- return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir;
-}
-
String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
-String get_mono_user_dir() {
- return _GodotSharpDirs::get_singleton().mono_user_dir;
+String get_api_assemblies_dir() {
+ return _GodotSharpDirs::get_singleton().api_assemblies_dir;
}
-String get_mono_logs_dir() {
- return _GodotSharpDirs::get_singleton().mono_logs_dir;
+String get_mono_user_dir() {
+ return _GodotSharpDirs::get_singleton().mono_user_dir;
}
#ifdef TOOLS_ENABLED
-String get_mono_solutions_dir() {
- return _GodotSharpDirs::get_singleton().mono_solutions_dir;
-}
-
String get_build_logs_dir() {
return _GodotSharpDirs::get_singleton().build_logs_dir;
}
-String get_project_sln_path() {
- return _GodotSharpDirs::get_singleton().sln_filepath;
-}
-
-String get_project_csproj_path() {
- return _GodotSharpDirs::get_singleton().csproj_filepath;
-}
-
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
-
-String get_data_editor_prebuilt_api_dir() {
- return _GodotSharpDirs::get_singleton().data_editor_prebuilt_api_dir;
-}
-#else
-String get_data_game_assemblies_dir() {
- return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
-}
#endif
-String get_data_mono_etc_dir() {
- return _GodotSharpDirs::get_singleton().data_mono_etc_dir;
-}
-
-String get_data_mono_lib_dir() {
- return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
-}
-
-#ifdef WINDOWS_ENABLED
-String get_data_mono_bin_dir() {
- return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
-}
-#endif
} // namespace GodotSharpDirs
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index da25e0778f..82847f70dc 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godotsharp_dirs.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godotsharp_dirs.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GODOTSHARP_DIRS_H
#define GODOTSHARP_DIRS_H
@@ -35,37 +35,18 @@
namespace GodotSharpDirs {
-String get_res_data_dir();
String get_res_metadata_dir();
-String get_res_assemblies_base_dir();
-String get_res_assemblies_dir();
-String get_res_config_dir();
-String get_res_temp_dir();
-String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
+String get_api_assemblies_dir();
+
String get_mono_user_dir();
-String get_mono_logs_dir();
#ifdef TOOLS_ENABLED
-String get_mono_solutions_dir();
String get_build_logs_dir();
-
-String get_project_sln_path();
-String get_project_csproj_path();
-
String get_data_editor_tools_dir();
-String get_data_editor_prebuilt_api_dir();
-#else
-String get_data_game_assemblies_dir();
#endif
-String get_data_mono_etc_dir();
-String get_data_mono_lib_dir();
-
-#ifdef WINDOWS_ENABLED
-String get_data_mono_bin_dir();
-#endif
} // namespace GodotSharpDirs
#endif // GODOTSHARP_DIRS_H
diff --git a/modules/mono/interop_types.h b/modules/mono/interop_types.h
new file mode 100644
index 0000000000..811c444140
--- /dev/null
+++ b/modules/mono/interop_types.h
@@ -0,0 +1,208 @@
+/**************************************************************************/
+/* interop_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 INTEROP_TYPES_H
+#define INTEROP_TYPES_H
+
+#include "core/math/math_defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// This is taken from the old GDNative, which was removed.
+
+#define GODOT_VARIANT_SIZE (sizeof(real_t) * 4 + sizeof(int64_t))
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VARIANT_SIZE];
+} godot_variant;
+
+#define GODOT_ARRAY_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_ARRAY_SIZE];
+} godot_array;
+
+#define GODOT_DICTIONARY_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_DICTIONARY_SIZE];
+} godot_dictionary;
+
+#define GODOT_STRING_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_STRING_SIZE];
+} godot_string;
+
+#define GODOT_STRING_NAME_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_STRING_NAME_SIZE];
+} godot_string_name;
+
+#define GODOT_PACKED_ARRAY_SIZE (2 * sizeof(void *))
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_ARRAY_SIZE];
+} godot_packed_array;
+
+#define GODOT_VECTOR2_SIZE (sizeof(real_t) * 2)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR2_SIZE];
+} godot_vector2;
+
+#define GODOT_VECTOR2I_SIZE (sizeof(int32_t) * 2)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR2I_SIZE];
+} godot_vector2i;
+
+#define GODOT_RECT2_SIZE (sizeof(real_t) * 4)
+
+typedef struct godot_rect2 {
+ uint8_t _dont_touch_that[GODOT_RECT2_SIZE];
+} godot_rect2;
+
+#define GODOT_RECT2I_SIZE (sizeof(int32_t) * 4)
+
+typedef struct godot_rect2i {
+ uint8_t _dont_touch_that[GODOT_RECT2I_SIZE];
+} godot_rect2i;
+
+#define GODOT_VECTOR3_SIZE (sizeof(real_t) * 3)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR3_SIZE];
+} godot_vector3;
+
+#define GODOT_VECTOR3I_SIZE (sizeof(int32_t) * 3)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR3I_SIZE];
+} godot_vector3i;
+
+#define GODOT_TRANSFORM2D_SIZE (sizeof(real_t) * 6)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_TRANSFORM2D_SIZE];
+} godot_transform2d;
+
+#define GODOT_VECTOR4_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR4_SIZE];
+} godot_vector4;
+
+#define GODOT_VECTOR4I_SIZE (sizeof(int32_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VECTOR4I_SIZE];
+} godot_vector4i;
+
+#define GODOT_PLANE_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PLANE_SIZE];
+} godot_plane;
+
+#define GODOT_QUATERNION_SIZE (sizeof(real_t) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_QUATERNION_SIZE];
+} godot_quaternion;
+
+#define GODOT_AABB_SIZE (sizeof(real_t) * 6)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_AABB_SIZE];
+} godot_aabb;
+
+#define GODOT_BASIS_SIZE (sizeof(real_t) * 9)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_BASIS_SIZE];
+} godot_basis;
+
+#define GODOT_TRANSFORM3D_SIZE (sizeof(real_t) * 12)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_TRANSFORM3D_SIZE];
+} godot_transform3d;
+
+#define GODOT_PROJECTION_SIZE (sizeof(real_t) * 4 * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PROJECTION_SIZE];
+} godot_projection;
+
+// Colors should always use 32-bit floats, so don't use real_t here.
+#define GODOT_COLOR_SIZE (sizeof(float) * 4)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_COLOR_SIZE];
+} godot_color;
+
+#define GODOT_NODE_PATH_SIZE sizeof(void *)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_NODE_PATH_SIZE];
+} godot_node_path;
+
+#define GODOT_RID_SIZE sizeof(uint64_t)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_RID_SIZE];
+} godot_rid;
+
+// Alignment hardcoded in `core/variant/callable.h`.
+#define GODOT_CALLABLE_SIZE (16)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_CALLABLE_SIZE];
+} godot_callable;
+
+// Alignment hardcoded in `core/variant/callable.h`.
+#define GODOT_SIGNAL_SIZE (16)
+
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE];
+} godot_signal;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // INTEROP_TYPES_H
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index c159bb9eea..3e4eb5a966 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -1,38 +1,37 @@
-/*************************************************************************/
-/* managed_callable.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* managed_callable.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "managed_callable.h"
#include "csharp_script.h"
-#include "mono_gd/gd_mono_marshal.h"
-#include "mono_gd/gd_mono_utils.h"
+#include "mono_gd/gd_mono_cache.h"
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable>::List ManagedCallable::instances;
@@ -44,18 +43,16 @@ bool ManagedCallable::compare_equal(const CallableCustom *p_a, const CallableCus
const ManagedCallable *a = static_cast<const ManagedCallable *>(p_a);
const ManagedCallable *b = static_cast<const ManagedCallable *>(p_b);
- MonoDelegate *delegate_a = (MonoDelegate *)a->delegate_handle.get_target();
- MonoDelegate *delegate_b = (MonoDelegate *)b->delegate_handle.get_target();
-
- if (!delegate_a || !delegate_b) {
- if (!delegate_a && !delegate_b) {
+ if (!a->delegate_handle.value || !b->delegate_handle.value) {
+ if (!a->delegate_handle.value && !b->delegate_handle.value) {
return true;
}
return false;
}
// Call Delegate's 'Equals'
- return GDMonoUtils::mono_delegate_equal(delegate_a, delegate_b);
+ return GDMonoCache::managed_callbacks.DelegateUtils_DelegateEquals(
+ a->delegate_handle, b->delegate_handle);
}
bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -66,8 +63,7 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust
}
uint32_t ManagedCallable::hash() const {
- uint32_t hash = delegate_invoke->get_name().hash();
- return hash_murmur3_one_64(delegate_handle.handle, hash);
+ return hash_murmur3_one_64((uint64_t)delegate_handle.value);
}
String ManagedCallable::get_as_text() const {
@@ -83,7 +79,9 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
}
ObjectID ManagedCallable::get_object() const {
- // TODO: If the delegate target extends Godot.Object, use that instead!
+ if (object_id != ObjectID()) {
+ return object_id;
+ }
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
}
@@ -91,41 +89,25 @@ void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
-#ifdef GD_MONO_HOT_RELOAD
- // Lost during hot-reload
- ERR_FAIL_NULL(delegate_invoke);
- ERR_FAIL_COND(delegate_handle.is_released());
-#endif
-
- ERR_FAIL_COND(delegate_invoke->get_parameters_count() < p_argcount);
-
- MonoObject *delegate = delegate_handle.get_target();
+ ERR_FAIL_COND(delegate_handle.value == nullptr);
- MonoException *exc = nullptr;
- MonoObject *ret = delegate_invoke->invoke(delegate, p_arguments, &exc);
+ GDMonoCache::managed_callbacks.DelegateUtils_InvokeWithVariantArgs(
+ delegate_handle, trampoline, p_arguments, p_argcount, &r_return_value);
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- } else {
- r_return_value = GDMonoMarshal::mono_object_to_variant(ret);
- r_call_error.error = Callable::CallError::CALL_OK;
- }
+ r_call_error.error = Callable::CallError::CALL_OK;
}
-void ManagedCallable::set_delegate(MonoDelegate *p_delegate) {
- delegate_handle = MonoGCHandleData::new_strong_handle((MonoObject *)p_delegate);
- MonoMethod *delegate_invoke_raw = mono_get_delegate_invoke(mono_object_get_class((MonoObject *)p_delegate));
- const StringName &delegate_invoke_name = CSharpLanguage::get_singleton()->get_string_names().delegate_invoke_method_name;
- delegate_invoke = memnew(GDMonoMethod(delegate_invoke_name, delegate_invoke_raw)); // TODO: Use pooling for this GDMonoMethod instances
+void ManagedCallable::release_delegate_handle() {
+ if (delegate_handle.value) {
+ GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(delegate_handle);
+ delegate_handle = { nullptr };
+ }
}
-ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_delegate == nullptr);
-#endif
-
- set_delegate(p_delegate);
-
+// Why you do this clang-format...
+/* clang-format off */
+ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, void *p_trampoline, ObjectID p_object_id) :
+ delegate_handle(p_delegate_handle), trampoline(p_trampoline), object_id(p_object_id) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);
@@ -133,6 +115,7 @@ ManagedCallable::ManagedCallable(MonoDelegate *p_delegate) {
}
#endif
}
+/* clang-format on */
ManagedCallable::~ManagedCallable() {
#ifdef GD_MONO_HOT_RELOAD
@@ -143,5 +126,5 @@ ManagedCallable::~ManagedCallable() {
}
#endif
- delegate_handle.release();
+ release_delegate_handle();
}
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 11bee6cf60..4936e69323 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -1,49 +1,47 @@
-/*************************************************************************/
-/* managed_callable.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* managed_callable.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MANAGED_CALLABLE_H
#define MANAGED_CALLABLE_H
-#include <mono/metadata/object.h>
-
#include "core/os/mutex.h"
#include "core/templates/self_list.h"
#include "core/variant/callable.h"
#include "mono_gc_handle.h"
-#include "mono_gd/gd_mono_method.h"
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
- MonoGCHandleData delegate_handle;
- GDMonoMethod *delegate_invoke = nullptr;
+ GCHandleIntPtr delegate_handle;
+ void *trampoline = nullptr;
+ ObjectID object_id;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@@ -60,9 +58,8 @@ public:
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;
- _FORCE_INLINE_ MonoDelegate *get_delegate() { return (MonoDelegate *)delegate_handle.get_target(); }
-
- void set_delegate(MonoDelegate *p_delegate);
+ _FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
+ _FORCE_INLINE_ void *get_trampoline() const { return trampoline; }
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
@@ -70,7 +67,9 @@ public:
static constexpr CompareEqualFunc compare_equal_func_ptr = &ManagedCallable::compare_equal;
static constexpr CompareEqualFunc compare_less_func_ptr = &ManagedCallable::compare_less;
- ManagedCallable(MonoDelegate *p_delegate);
+ void release_delegate_handle();
+
+ ManagedCallable(GCHandleIntPtr p_delegate_handle, void *p_trampoline, ObjectID p_object_id);
~ManagedCallable();
};
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index f3dafa6ecf..efbcc335d4 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -1,64 +1,50 @@
-/*************************************************************************/
-/* mono_gc_handle.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* mono_gc_handle.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
+#include "mono_gd/gd_mono_cache.h"
void MonoGCHandleData::release() {
#ifdef DEBUG_ENABLED
- CRASH_COND(handle && GDMono::get_singleton() == nullptr);
+ CRASH_COND(handle.value && GDMono::get_singleton() == nullptr);
#endif
- if (handle && GDMono::get_singleton()->is_runtime_initialized()) {
- GDMonoUtils::free_gchandle(handle);
- handle = 0;
+ if (handle.value && GDMonoCache::godot_api_cache_updated &&
+ GDMono::get_singleton()->is_runtime_initialized()) {
+ free_gchandle(handle);
+ handle.value = nullptr;
}
}
-
-MonoGCHandleData MonoGCHandleData::new_strong_handle(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_strong_gchandle(p_object), gdmono::GCHandleType::STRONG_HANDLE);
-}
-
-MonoGCHandleData MonoGCHandleData::new_strong_handle_pinned(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_strong_gchandle_pinned(p_object), gdmono::GCHandleType::STRONG_HANDLE);
-}
-
-MonoGCHandleData MonoGCHandleData::new_weak_handle(MonoObject *p_object) {
- return MonoGCHandleData(GDMonoUtils::new_weak_gchandle(p_object), gdmono::GCHandleType::WEAK_HANDLE);
-}
-
-Ref<MonoGCHandleRef> MonoGCHandleRef::create_strong(MonoObject *p_object) {
- return memnew(MonoGCHandleRef(MonoGCHandleData::new_strong_handle(p_object)));
-}
-
-Ref<MonoGCHandleRef> MonoGCHandleRef::create_weak(MonoObject *p_object) {
- return memnew(MonoGCHandleRef(MonoGCHandleData::new_weak_handle(p_object)));
+void MonoGCHandleData::free_gchandle(GCHandleIntPtr p_gchandle) {
+ CRASH_COND(!GDMonoCache::godot_api_cache_updated);
+ GDMonoCache::managed_callbacks.GCHandleBridge_FreeGCHandle(p_gchandle);
}
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index e2aff1d19d..ff6c68727d 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -1,38 +1,36 @@
-/*************************************************************************/
-/* mono_gc_handle.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* mono_gc_handle.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_GC_HANDLE_H
#define MONO_GC_HANDLE_H
-#include <mono/jit/jit.h>
-
#include "core/object/ref_counted.h"
namespace gdmono {
@@ -44,18 +42,32 @@ enum class GCHandleType : char {
};
}
+extern "C" {
+struct GCHandleIntPtr {
+ void *value;
+
+ _FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
+ _FORCE_INLINE_ bool operator!=(const GCHandleIntPtr &p_other) { return value != p_other.value; }
+
+ GCHandleIntPtr() = delete;
+};
+}
+
+static_assert(sizeof(GCHandleIntPtr) == sizeof(void *));
+
// Manual release of the GC handle must be done when using this struct
struct MonoGCHandleData {
- uint32_t handle = 0;
+ GCHandleIntPtr handle = { nullptr };
gdmono::GCHandleType type = gdmono::GCHandleType::NIL;
- _FORCE_INLINE_ bool is_released() const { return !handle; }
+ _FORCE_INLINE_ bool is_released() const { return !handle.value; }
_FORCE_INLINE_ bool is_weak() const { return type == gdmono::GCHandleType::WEAK_HANDLE; }
-
- _FORCE_INLINE_ MonoObject *get_target() const { return handle ? mono_gchandle_get_target(handle) : nullptr; }
+ _FORCE_INLINE_ GCHandleIntPtr get_intptr() const { return handle; }
void release();
+ static void free_gchandle(GCHandleIntPtr p_gchandle);
+
void operator=(const MonoGCHandleData &p_other) {
#ifdef DEBUG_ENABLED
CRASH_COND(!is_released());
@@ -68,40 +80,10 @@ struct MonoGCHandleData {
MonoGCHandleData() {}
- MonoGCHandleData(uint32_t p_handle, gdmono::GCHandleType p_type) :
+ MonoGCHandleData(GCHandleIntPtr p_handle, gdmono::GCHandleType p_type) :
handle(p_handle),
type(p_type) {
}
-
- static MonoGCHandleData new_strong_handle(MonoObject *p_object);
- static MonoGCHandleData new_strong_handle_pinned(MonoObject *p_object);
- static MonoGCHandleData new_weak_handle(MonoObject *p_object);
-};
-
-class MonoGCHandleRef : public RefCounted {
- GDCLASS(MonoGCHandleRef, RefCounted);
-
- MonoGCHandleData data;
-
-public:
- static Ref<MonoGCHandleRef> create_strong(MonoObject *p_object);
- static Ref<MonoGCHandleRef> create_weak(MonoObject *p_object);
-
- _FORCE_INLINE_ bool is_released() const { return data.is_released(); }
- _FORCE_INLINE_ bool is_weak() const { return data.is_weak(); }
-
- _FORCE_INLINE_ MonoObject *get_target() const { return data.get_target(); }
-
- void release() { data.release(); }
-
- _FORCE_INLINE_ void set_handle(uint32_t p_handle, gdmono::GCHandleType p_handle_type) {
- data = MonoGCHandleData(p_handle, p_handle_type);
- }
-
- MonoGCHandleRef(const MonoGCHandleData &p_gc_handle_data) :
- data(p_gc_handle_data) {
- }
- ~MonoGCHandleRef() { release(); }
};
#endif // MONO_GC_HANDLE_H
diff --git a/modules/mono/mono_gd/android_mono_config.h b/modules/mono/mono_gd/android_mono_config.h
index 9ee4bcf590..bb9c2a5d5b 100644
--- a/modules/mono/mono_gd/android_mono_config.h
+++ b/modules/mono/mono_gd/android_mono_config.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* android_mono_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* android_mono_config.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ANDROID_MONO_CONFIG_H
#define ANDROID_MONO_CONFIG_H
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index d3d3bb2bef..86b0e04206 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -1,42 +1,35 @@
-/*************************************************************************/
-/* gd_mono.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gd_mono.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gd_mono.h"
-#include <mono/metadata/environment.h>
-#include <mono/metadata/exception.h>
-#include <mono/metadata/mono-config.h>
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-gc.h>
-#include <mono/metadata/profiler.h>
-
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/dir_access.h"
@@ -45,1077 +38,478 @@
#include "core/os/thread.h"
#include "../csharp_script.h"
+#include "../glue/runtime_interop.h"
#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
+#include "../thirdparty/coreclr_delegates.h"
+#include "../thirdparty/hostfxr.h"
+
+#ifdef TOOLS_ENABLED
+#include "../editor/hostfxr_resolver.h"
+#endif
+
+#ifdef UNIX_ENABLED
+#include <dlfcn.h>
+#endif
+
+// TODO mobile
+#if 0
#ifdef ANDROID_ENABLED
-#include "android_mono_config.h"
#include "support/android_support.h"
#elif defined(IOS_ENABLED)
#include "support/ios_support.h"
#endif
-
-#if defined(TOOL_ENABLED) && defined(GD_MONO_SINGLE_APPDOMAIN)
-// This will no longer be the case if we replace appdomains with AssemblyLoadContext
-#error "Editor build requires support for multiple appdomains"
-#endif
-
-#if defined(GD_MONO_HOT_RELOAD) && defined(GD_MONO_SINGLE_APPDOMAIN)
-#error "Hot reloading requires multiple appdomains"
#endif
-// TODO:
-// This has turned into a gigantic mess. There's too much going on here. Too much #ifdef as well.
-// It's just painful to read... It needs to be re-structured. Please, clean this up, future me.
-
GDMono *GDMono::singleton = nullptr;
namespace {
-
-#if defined(JAVASCRIPT_ENABLED)
-extern "C" {
-void mono_wasm_load_runtime(const char *managed_path, int enable_debugging);
-}
-#endif
-
-#if !defined(JAVASCRIPT_ENABLED)
-
-void gd_mono_setup_runtime_main_args() {
- CharString execpath = OS::get_singleton()->get_executable_path().utf8();
-
- List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-
- List<CharString> cmdline_args_utf8;
- Vector<char *> main_args;
- main_args.resize(cmdline_args.size() + 1);
-
- main_args.write[0] = execpath.ptrw();
-
- int i = 1;
- for (const String &E : cmdline_args) {
- CharString &stored = cmdline_args_utf8.push_back(E.utf8())->get();
- main_args.write[i] = stored.ptrw();
- i++;
- }
-
- mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
-}
-
-void gd_mono_profiler_init() {
- String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
- bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false);
- if (profiler_enabled) {
- mono_profiler_load(profiler_args.utf8());
- return;
- }
-
- const String env_var_name = "MONO_ENV_OPTIONS";
- if (OS::get_singleton()->has_environment(env_var_name)) {
- const String mono_env_ops = OS::get_singleton()->get_environment(env_var_name);
- // Usually MONO_ENV_OPTIONS looks like: --profile=jb:prof=timeline,ctl=remote,host=127.0.0.1:55467
- const String prefix = "--profile=";
- if (mono_env_ops.begins_with(prefix)) {
- const String ops = mono_env_ops.substr(prefix.length(), mono_env_ops.length());
- mono_profiler_load(ops.utf8());
- }
- }
-}
-
-void gd_mono_debug_init() {
- CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
-
- if (da_args.length()) {
- OS::get_singleton()->set_environment("GODOT_MONO_DEBUGGER_AGENT", String());
- }
-
-#ifdef TOOLS_ENABLED
- int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
- bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
- int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
-
- if (Engine::get_singleton()->is_editor_hint() ||
- ProjectSettings::get_singleton()->get_resource_path().is_empty() ||
- Engine::get_singleton()->is_project_manager_hint()) {
- if (da_args.size() == 0) {
- return;
- }
- }
-
- if (da_args.length() == 0) {
- da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
- ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
- .utf8();
- }
+hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line = nullptr;
+hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
+hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
+hostfxr_close_fn hostfxr_close = nullptr;
+
+#ifdef _WIN32
+static_assert(sizeof(char_t) == sizeof(char16_t));
+using HostFxrCharString = Char16String;
+#define HOSTFXR_STR(m_str) L##m_str
#else
- if (da_args.length() == 0) {
- return; // Exported games don't use the project settings to setup the debugger agent
- }
+static_assert(sizeof(char_t) == sizeof(char));
+using HostFxrCharString = CharString;
+#define HOSTFXR_STR(m_str) m_str
#endif
- // Debugging enabled
-
- mono_debug_init(MONO_DEBUG_FORMAT_MONO);
-
- // --debugger-agent=help
- const char *options[] = {
- "--soft-breakpoints",
- da_args.get_data()
- };
- mono_jit_parse_options(2, (char **)options);
-}
-
-#endif // !defined(JAVASCRIPT_ENABLED)
-
-#if defined(JAVASCRIPT_ENABLED)
-MonoDomain *gd_initialize_mono_runtime() {
- const char *vfs_prefix = "managed";
- int enable_debugging = 0;
-
- // TODO: Provide a way to enable debugging on WASM release builds.
-#ifdef DEBUG_ENABLED
- enable_debugging = 1;
-#endif
-
- mono_wasm_load_runtime(vfs_prefix, enable_debugging);
-
- return mono_get_root_domain();
-}
+HostFxrCharString str_to_hostfxr(const String &p_str) {
+#ifdef _WIN32
+ return p_str.utf16();
#else
-MonoDomain *gd_initialize_mono_runtime() {
- gd_mono_debug_init();
-
-#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED)
- // I don't know whether this actually matters or not
- const char *runtime_version = "mobile";
-#else
- const char *runtime_version = "v4.0.30319";
+ return p_str.utf8();
#endif
-
- return mono_jit_init_version("GodotEngine.RootDomain", runtime_version);
}
-#endif
-} // namespace
-
-void GDMono::add_mono_shared_libs_dir_to_path() {
- // TODO: Replace this with a mono_dl_fallback
-
- // By default Mono seems to search shared libraries in the following directories:
- // Current working directory, @executable_path@ and PATH
- // The parent directory of the image file (assembly where the dllimport method is declared)
- // @executable_path@/../lib
- // @executable_path@/../Libraries (__MACH__ only)
- // This does not work when embedding Mono unless we use the same directory structure.
- // To fix this we append the directory containing our shared libraries to PATH.
-
-#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED)
- String path_var("PATH");
- String path_value = OS::get_singleton()->get_environment(path_var);
-
-#ifdef WINDOWS_ENABLED
- path_value += ';';
-
- String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir();
-#ifdef TOOLS_ENABLED
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- } else {
- path_value += mono_reg_info.bin_dir;
- }
-#else
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- }
-#endif // TOOLS_ENABLED
-
-#else
- path_value += ':';
-
- String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir();
- if (DirAccess::exists(bundled_lib_dir)) {
- path_value += bundled_lib_dir;
- } else {
- // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms?
- }
-#endif // WINDOWS_ENABLED
-
- OS::get_singleton()->set_environment(path_var, path_value);
-#endif // WINDOWS_ENABLED || UNIX_ENABLED
+const char_t *get_data(const HostFxrCharString &p_char_str) {
+ return (const char_t *)p_char_str.get_data();
}
-void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) {
- String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
- String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
-
+String find_hostfxr() {
#ifdef TOOLS_ENABLED
-
-#if defined(WINDOWS_ENABLED)
- mono_reg_info = MonoRegUtils::find_mono();
-
- if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
- r_assembly_rootdir = mono_reg_info.assembly_dir;
- }
-
- if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
- r_config_dir = mono_reg_info.config_dir;
- }
-#elif defined(MACOS_ENABLED)
- const char *c_assembly_rootdir = mono_assembly_getrootdir();
- const char *c_config_dir = mono_get_config_dir();
-
- if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
- Vector<const char *> locations;
- locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
- locations.push_back("/usr/local/var/homebrew/linked/mono/");
-
- for (int i = 0; i < locations.size(); i++) {
- String hint_assembly_rootdir = path::join(locations[i], "lib");
- String hint_mscorlib_path = path::join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
- String hint_config_dir = path::join(locations[i], "etc");
-
- if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
- r_assembly_rootdir = hint_assembly_rootdir;
- r_config_dir = hint_config_dir;
- break;
- }
+ String dotnet_root;
+ String fxr_path;
+ if (godotsharp::hostfxr_resolver::try_get_path(dotnet_root, fxr_path)) {
+ return fxr_path;
+ }
+
+ // hostfxr_resolver doesn't look for dotnet in `PATH`. If it fails, we try to find the dotnet
+ // executable in `PATH` here and pass its location as `dotnet_root` to `get_hostfxr_path`.
+ String dotnet_exe = path::find_executable("dotnet");
+
+ if (!dotnet_exe.is_empty()) {
+ // The file found in PATH may be a symlink
+ dotnet_exe = path::abspath(path::realpath(dotnet_exe));
+
+ // TODO:
+ // Sometimes, the symlink may not point to the dotnet executable in the dotnet root.
+ // That's the case with snaps. The snap install should have been found with the
+ // previous `get_hostfxr_path`, but it would still be better to do this properly
+ // and use something like `dotnet --list-sdks/runtimes` to find the actual location.
+ // This way we could also check if the proper sdk or runtime is installed. This would
+ // allow us to fail gracefully and show some helpful information in the editor.
+
+ dotnet_root = dotnet_exe.get_base_dir();
+ if (godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(dotnet_root, fxr_path)) {
+ return fxr_path;
}
}
-#endif
- if (DirAccess::exists(bundled_assembly_rootdir)) {
- r_assembly_rootdir = bundled_assembly_rootdir;
- }
-
- if (DirAccess::exists(bundled_config_dir)) {
- r_config_dir = bundled_config_dir;
- }
-
-#ifdef WINDOWS_ENABLED
- if (r_assembly_rootdir.is_empty() || r_config_dir.is_empty()) {
- ERR_PRINT("Cannot find Mono in the registry.");
- // Assertion: if they are not set, then they weren't found in the registry
- CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
- }
-#endif // WINDOWS_ENABLED
+ ERR_PRINT(String() + ".NET: One of the dependent libraries is missing. " +
+ "Typically when the `hostfxr`, `hostpolicy` or `coreclr` dynamic " +
+ "libraries are not present in the expected locations.");
+ return String();
#else
- // Export templates always use the bundled directories
- r_assembly_rootdir = bundled_assembly_rootdir;
- r_config_dir = bundled_config_dir;
-#endif
-}
-
-void GDMono::initialize() {
- ERR_FAIL_NULL(Engine::get_singleton());
- print_verbose("Mono: Initializing module...");
-
- char *runtime_build_info = mono_get_runtime_build_info();
- print_verbose("Mono JIT compiler version " + String(runtime_build_info));
- mono_free(runtime_build_info);
-
- _init_godot_api_hashes();
- _init_exception_policy();
-
- GDMonoLog::get_singleton()->initialize();
-
-#if !defined(JAVASCRIPT_ENABLED)
- String assembly_rootdir;
- String config_dir;
- determine_mono_dirs(assembly_rootdir, config_dir);
-
- // Leak if we call mono_set_dirs more than once
- mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr,
- config_dir.length() ? config_dir.utf8().get_data() : nullptr);
-
- add_mono_shared_libs_dir_to_path();
-#endif
-
-#ifdef ANDROID_ENABLED
- mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
+#if defined(WINDOWS_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .path_join("hostfxr.dll");
+#elif defined(MACOS_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .path_join("libhostfxr.dylib");
+#elif defined(UNIX_ENABLED)
+ String probe_path = GodotSharpDirs::get_api_assemblies_dir()
+ .path_join("libhostfxr.so");
#else
- mono_config_parse(nullptr);
-#endif
-
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::initialize();
-#elif defined(IOS_ENABLED)
- gdmono::ios::support::initialize();
+#error "Platform not supported (yet?)"
#endif
- GDMonoAssembly::initialize();
-
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_profiler_init();
-#endif
-
- mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr);
-
-#ifndef TOOLS_ENABLED
- // Exported games that don't use C# must still work. They likely don't ship with mscorlib.
- // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
- if (GDMonoAssembly::find_assembly("mscorlib.dll").is_empty()) {
- print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
- return;
+ if (FileAccess::exists(probe_path)) {
+ return probe_path;
}
-#endif
-#if !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND)
- // FIXME: Temporary workaround. See: https://github.com/godotengine/godot/issues/29812
- if (!OS::get_singleton()->has_environment("MONO_THREADS_SUSPEND")) {
- OS::get_singleton()->set_environment("MONO_THREADS_SUSPEND", "preemptive");
- }
-#endif
+ return String();
- // NOTE: Internal calls must be registered after the Mono runtime initialization.
- // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'.
-
- root_domain = gd_initialize_mono_runtime();
- ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
-
- GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
-
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
#endif
+}
- runtime_initialized = true;
-
- print_verbose("Mono: Runtime initialized");
+bool load_hostfxr(void *&r_hostfxr_dll_handle) {
+ String hostfxr_path = find_hostfxr();
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::register_internal_calls();
-#endif
+ if (hostfxr_path.is_empty()) {
+ return false;
+ }
- // mscorlib assembly MUST be present at initialization
- bool corlib_loaded = _load_corlib_assembly();
- ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly.");
+ print_verbose("Found hostfxr: " + hostfxr_path);
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- Error domain_load_err = _load_scripts_domain();
- ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
-#else
- scripts_domain = root_domain;
-#endif
+ Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, r_hostfxr_dll_handle);
- _register_internal_calls();
+ if (err != OK) {
+ return false;
+ }
- print_verbose("Mono: INITIALIZED");
-}
+ void *lib = r_hostfxr_dll_handle;
-void GDMono::initialize_load_assemblies() {
-#ifndef MONO_GLUE_ENABLED
- CRASH_NOW_MSG("Mono: This binary was built with 'mono_glue=no'; cannot load assemblies.");
-#endif
+ void *symbol = nullptr;
- // Load assemblies. The API and tools assemblies are required,
- // the application is aborted if these assemblies cannot be loaded.
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_dotnet_command_line", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)symbol;
- _load_api_assemblies();
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol;
-#if defined(TOOLS_ENABLED)
- bool tool_assemblies_loaded = _load_tools_assemblies();
- CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol;
- if (Engine::get_singleton()->is_project_manager_hint()) {
- return;
- }
-#endif
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_close = (hostfxr_close_fn)symbol;
- // Load the project's main assembly. This doesn't necessarily need to succeed.
- // The game may not be using .NET at all, or if the project does use .NET and
- // we're running in the editor, it may just happen to be it wasn't built yet.
- if (!_load_project_assembly()) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load project assembly");
- }
- }
+ return (hostfxr_initialize_for_runtime_config &&
+ hostfxr_get_runtime_delegate &&
+ hostfxr_close);
}
-bool GDMono::_are_api_assemblies_out_of_sync() {
- bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoCache::cached_data.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
- if (!out_of_sync) {
- out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
+load_assembly_and_get_function_pointer_fn initialize_hostfxr_for_config(const char_t *p_config_path) {
+ hostfxr_handle cxt = nullptr;
+ int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr) {
+ hostfxr_close(cxt);
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed with code: " + itos(rc));
}
-#endif
- return out_of_sync;
-}
-namespace GodotSharpBindings {
-#ifdef MONO_GLUE_ENABLED
+ void *load_assembly_and_get_function_pointer = nullptr;
-uint64_t get_core_api_hash();
-#ifdef TOOLS_ENABLED
-uint64_t get_editor_api_hash();
-#endif
-uint32_t get_bindings_version();
-uint32_t get_cs_glue_version();
-
-void register_generated_icalls();
-
-#else
+ rc = hostfxr_get_runtime_delegate(cxt,
+ hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
+ }
-uint64_t get_core_api_hash() {
- GD_UNREACHABLE();
-}
-#ifdef TOOLS_ENABLED
-uint64_t get_editor_api_hash() {
- GD_UNREACHABLE();
-}
-#endif
-uint32_t get_bindings_version() {
- GD_UNREACHABLE();
-}
+ hostfxr_close(cxt);
-uint32_t get_cs_glue_version() {
- GD_UNREACHABLE();
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
+#else
+load_assembly_and_get_function_pointer_fn initialize_hostfxr_self_contained(
+ const char_t *p_main_assembly_path) {
+ hostfxr_handle cxt = nullptr;
-void register_generated_icalls() {
- /* Fine, just do nothing */
-}
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-#endif // MONO_GLUE_ENABLED
-} // namespace GodotSharpBindings
+ List<HostFxrCharString> argv_store;
+ Vector<const char_t *> argv;
+ argv.resize(cmdline_args.size() + 1);
-void GDMono::_register_internal_calls() {
- GodotSharpBindings::register_generated_icalls();
-}
+ argv.write[0] = p_main_assembly_path;
-void GDMono::_init_godot_api_hashes() {
-#if defined(MONO_GLUE_ENABLED) && defined(DEBUG_METHODS_ENABLED)
- if (get_api_core_hash() != GodotSharpBindings::get_core_api_hash()) {
- ERR_PRINT("Mono: Core API hash mismatch.");
+ int i = 1;
+ for (const String &E : cmdline_args) {
+ HostFxrCharString &stored = argv_store.push_back(str_to_hostfxr(E))->get();
+ argv.write[i] = get_data(stored);
+ i++;
}
-#ifdef TOOLS_ENABLED
- if (get_api_editor_hash() != GodotSharpBindings::get_editor_api_hash()) {
- ERR_PRINT("Mono: Editor API hash mismatch.");
+ int rc = hostfxr_initialize_for_dotnet_command_line(argv.size(), argv.ptrw(), nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr) {
+ hostfxr_close(cxt);
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_dotnet_command_line failed with code: " + itos(rc));
}
-#endif // TOOLS_ENABLED
-#endif // MONO_GLUE_ENABLED && DEBUG_METHODS_ENABLED
-}
-void GDMono::_init_exception_policy() {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/unhandled_exception_policy", PROPERTY_HINT_ENUM,
- vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
- unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
- ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
+ void *load_assembly_and_get_function_pointer = nullptr;
- if (Engine::get_singleton()->is_editor_hint()) {
- // Unhandled exceptions should not terminate the editor
- unhandled_exception_policy = POLICY_LOG_ERROR;
+ rc = hostfxr_get_runtime_delegate(cxt,
+ hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed with code: " + itos(rc));
}
-}
-void GDMono::add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly) {
- assemblies[p_domain_id][p_assembly->get_name()] = p_assembly;
-}
+ hostfxr_close(cxt);
-GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) {
- if (p_name == "mscorlib" && corlib_assembly) {
- return corlib_assembly;
- }
-
- MonoDomain *domain = mono_domain_get();
- int32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
- GDMonoAssembly **result = assemblies[domain_id].getptr(p_name);
- return result ? *result : nullptr;
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
-
-bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
#endif
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- bool result = load_assembly(p_name, aname, r_assembly, p_refonly);
- mono_assembly_name_free(aname);
- mono_free(aname);
-
- return result;
-}
-
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
+#ifdef TOOLS_ENABLED
+using godot_plugins_initialize_fn = bool (*)(void *, bool, gdmono::PluginCallbacks *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
+#else
+using godot_plugins_initialize_fn = bool (*)(void *, GDMonoCache::ManagedCallbacks *, const void **, int32_t);
#endif
- return load_assembly(p_name, p_aname, r_assembly, p_refonly, GDMonoAssembly::get_default_search_dirs());
-}
+#ifdef TOOLS_ENABLED
+godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
-#endif
+ HostFxrCharString godot_plugins_path = str_to_hostfxr(
+ GodotSharpDirs::get_api_assemblies_dir().path_join("GodotPlugins.dll"));
- print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
+ HostFxrCharString config_path = str_to_hostfxr(
+ GodotSharpDirs::get_api_assemblies_dir().path_join("GodotPlugins.runtimeconfig.json"));
- GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, p_refonly, p_search_dirs);
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
+ initialize_hostfxr_for_config(get_data(config_path));
+ ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
- if (!assembly) {
- return false;
- }
+ r_runtime_initialized = true;
- *r_assembly = assembly;
+ print_verbose(".NET: hostfxr initialized");
- print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
+ int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path),
+ HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"),
+ HOSTFXR_STR("InitializeFromEngine"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void **)&godot_plugins_initialize);
+ ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
- return true;
+ return godot_plugins_initialize;
}
+#else
+static String get_assembly_name() {
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
-bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly) {
- CRASH_COND(!r_assembly);
-
- print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "...");
-
- GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, p_refonly);
-
- if (!assembly) {
- return false;
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
}
- *r_assembly = assembly;
-
- print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path());
-
- return true;
+ return assembly_name;
}
-ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) {
- ApiAssemblyInfo::Version api_assembly_version;
+godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
- const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE
- ? BINDINGS_CLASS_NATIVECALLS
- : BINDINGS_CLASS_NATIVECALLS_EDITOR;
+ String assembly_name = get_assembly_name();
- GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
+ HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
+ .path_join(assembly_name + ".dll"));
- if (nativecalls_klass) {
- GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash");
- if (api_hash_field) {
- api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr));
- }
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
+ initialize_hostfxr_self_contained(get_data(assembly_path));
+ ERR_FAIL_NULL_V(load_assembly_and_get_function_pointer, nullptr);
- GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version");
- if (binds_ver_field) {
- api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr));
- }
+ r_runtime_initialized = true;
- GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version");
- if (cs_glue_ver_field) {
- api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr));
- }
- }
+ print_verbose(".NET: hostfxr initialized");
- return api_assembly_version;
-}
+ int rc = load_assembly_and_get_function_pointer(get_data(assembly_path),
+ get_data(str_to_hostfxr("GodotPlugins.Game.Main, " + assembly_name)),
+ HOSTFXR_STR("InitializeFromGameProject"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void **)&godot_plugins_initialize);
+ ERR_FAIL_COND_V_MSG(rc != 0, nullptr, ".NET: Failed to get GodotPlugins initialization function pointer");
-String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) {
- return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR";
+ return godot_plugins_initialize;
}
-bool GDMono::_load_corlib_assembly() {
- if (corlib_assembly) {
- return true;
- }
-
- bool success = load_assembly("mscorlib", &corlib_assembly);
+godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
+ String assembly_name = get_assembly_name();
- if (success) {
- GDMonoCache::update_corlib_cache();
- }
-
- return success;
-}
-
-#ifdef TOOLS_ENABLED
-bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) {
- String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
- String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
-
- String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+#if defined(WINDOWS_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
+#elif defined(MACOS_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
+#elif defined(UNIX_ENABLED)
+ String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
+#else
+#error "Platform not supported (yet?)"
+#endif
- // Create destination directory if needed
- if (!DirAccess::exists(dst_dir)) {
- Ref<DirAccess> da = DirAccess::create_for_path(dst_dir);
- Error err = da->make_dir_recursive(dst_dir);
+ if (FileAccess::exists(native_aot_so_path)) {
+ Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
if (err != OK) {
- ERR_PRINT("Failed to create destination directory for the API assemblies. Error: " + itos(err) + ".");
- return false;
+ return nullptr;
}
- }
-
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- String xml_file = assembly_name + ".xml";
- if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK) {
- WARN_PRINT("Failed to copy '" + xml_file + "'.");
- }
+ void *lib = r_aot_dll_handle;
- String pdb_file = assembly_name + ".pdb";
- if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK) {
- WARN_PRINT("Failed to copy '" + pdb_file + "'.");
- }
+ void *symbol = nullptr;
- String assembly_file = assembly_name + ".dll";
- if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) {
- ERR_PRINT("Failed to copy '" + assembly_file + "'.");
- return false;
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
+ ERR_FAIL_COND_V(err != OK, nullptr);
+ return (godot_plugins_initialize_fn)symbol;
}
- return true;
+ return nullptr;
}
+#endif
-static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) {
- String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path)) {
- return false;
- }
-
- String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
+} // namespace
- if (!FileAccess::exists(cached_api_hash_path)) {
+static bool _on_core_api_assembly_loaded() {
+ if (!GDMonoCache::godot_api_cache_updated) {
return false;
}
- Ref<ConfigFile> cfg;
- cfg.instantiate();
- Error cfg_err = cfg->load(cached_api_hash_path);
- ERR_FAIL_COND_V(cfg_err != OK, false);
-
- // Checking the modified time is good enough
- if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") ||
- FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) {
- return false;
- }
+ bool debug;
+#ifdef DEBUG_ENABLED
+ debug = true;
+#else
+ debug = false;
+#endif
- r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") ||
- GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") ||
- GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") ||
- GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") ||
- GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") ||
- GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash");
+ GDMonoCache::managed_callbacks.GD_OnCoreApiAssemblyLoaded(debug);
return true;
}
-static void create_cached_api_hash_for(const String &p_api_assemblies_dir) {
- String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
- String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
-
- Ref<ConfigFile> cfg;
- cfg.instantiate();
-
- cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path));
- cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path));
-
- cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version());
- cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version());
- cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version());
- cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version());
-
- // This assumes the prebuilt api assemblies we copied to the project are not out of sync
- cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash());
- cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash());
-
- Error err = cfg->save(cached_api_hash_path);
- ERR_FAIL_COND(err != OK);
-}
-
-bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) {
- MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies");
- ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies");
- _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
-
- _GDMONO_SCOPE_DOMAIN_(temp_domain);
-
- GDMono::LoadedApiAssembly temp_core_api_assembly;
- GDMono::LoadedApiAssembly temp_editor_api_assembly;
-
- if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly,
- p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) {
- return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync;
- }
-
- return true; // Failed to load, assume they're outdated assemblies
-}
-
-String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {
-#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
- ( \
- (m_out_of_sync ? String("The assembly is invalidated ") : String("The assembly was not found ")) + \
- (m_prebuilt_exists ? String("and the prebuilt assemblies are missing.") : String("and we failed to copy the prebuilt assemblies.")))
+void GDMono::initialize() {
+ print_verbose(".NET: Initializing module...");
- String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
+ _init_godot_api_hashes();
- String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
- bool api_assemblies_out_of_sync = false;
+ if (!load_hostfxr(hostfxr_dll_handle)) {
+#if !defined(TOOLS_ENABLED)
+ godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle);
- if (p_core_api_out_of_sync && p_editor_api_out_of_sync) {
- api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync;
- } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
- // Determine if they're out of sync
- if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) {
- api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config);
+ if (godot_plugins_initialize != nullptr) {
+ is_native_aot = true;
+ } else {
+ ERR_FAIL_MSG(".NET: Failed to load hostfxr");
}
+#else
+ ERR_FAIL_MSG(".NET: Failed to load hostfxr");
+#endif
}
- // Note: Even if only one of the assemblies if missing or out of sync, we update both
-
- if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
- return String(); // No update needed
- }
-
- print_verbose("Updating '" + p_config + "' API assemblies");
-
- String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
- String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) {
- return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false);
+ if (!is_native_aot) {
+ godot_plugins_initialize = initialize_hostfxr_and_godot_plugins(runtime_initialized);
+ ERR_FAIL_NULL(godot_plugins_initialize);
}
- // Copy the prebuilt Api
- if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) ||
- !copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) {
- return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true);
- }
+ int32_t interop_funcs_size = 0;
+ const void **interop_funcs = godotsharp::get_runtime_interop_funcs(interop_funcs_size);
- // Cache the api hash of the assemblies we just copied
- create_cached_api_hash_for(dst_assemblies_dir);
+ GDMonoCache::ManagedCallbacks managed_callbacks{};
- return String(); // Updated successfully
+ void *godot_dll_handle = nullptr;
-#undef FAIL_REASON
-}
+#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(IOS_ENABLED)
+ // Managed code can access it on its own on other platforms
+ godot_dll_handle = dlopen(nullptr, RTLD_NOW);
#endif
-bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (r_loaded_api_assembly.assembly) {
- return true;
- }
-
#ifdef TOOLS_ENABLED
- // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
-
- // If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Engine::get_singleton()->is_project_manager_hint()
- ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
- : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
-
- String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
-
- bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
+ gdmono::PluginCallbacks plugin_callbacks_res;
+ bool init_ok = godot_plugins_initialize(godot_dll_handle,
+ Engine::get_singleton()->is_editor_hint(),
+ &plugin_callbacks_res, &managed_callbacks,
+ interop_funcs, interop_funcs_size);
+ ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
+
+ plugin_callbacks = plugin_callbacks_res;
#else
- bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly);
+ bool init_ok = godot_plugins_initialize(godot_dll_handle, &managed_callbacks,
+ interop_funcs, interop_funcs_size);
+ ERR_FAIL_COND_MSG(!init_ok, ".NET: GodotPlugins initialization failed");
#endif
- if (success) {
- ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE);
- r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
- } else {
- r_loaded_api_assembly.out_of_sync = false;
- }
-
- return success;
-}
-
-#ifdef TOOLS_ENABLED
-bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (r_loaded_api_assembly.assembly) {
- return true;
- }
+ GDMonoCache::update_godot_api_cache(managed_callbacks);
- // For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
+ print_verbose(".NET: GodotPlugins initialized");
- // If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Engine::get_singleton()->is_project_manager_hint()
- ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
- : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
-
- String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
- bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
-
- if (success) {
- ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR);
- r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
- } else {
- r_loaded_api_assembly.out_of_sync = false;
- }
-
- return success;
+ _on_core_api_assembly_loaded();
}
-#endif
-bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
- const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) {
- if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Core API assembly");
- }
- return false;
+#ifdef TOOLS_ENABLED
+void GDMono::initialize_load_assemblies() {
+ if (Engine::get_singleton()->is_project_manager_hint()) {
+ return;
}
-#ifdef TOOLS_ENABLED
- if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {
+ // Load the project's main assembly. This doesn't necessarily need to succeed.
+ // The game may not be using .NET at all, or if the project does use .NET and
+ // we're running in the editor, it may just happen to be it wasn't built yet.
+ if (!_load_project_assembly()) {
if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Editor API assembly");
+ print_error(".NET: Failed to load project assembly");
}
- return false;
}
-
- if (r_editor_api_assembly.out_of_sync) {
- return false;
- }
-#endif
-
- // Check if the core API assembly is out of sync only after trying to load the
- // editor API assembly. Otherwise, if both assemblies are out of sync, we would
- // only update the former as we won't know the latter also needs to be updated.
- if (r_core_api_assembly.out_of_sync) {
- return false;
- }
-
- if (p_callback) {
- return p_callback();
- }
-
- return true;
-}
-
-bool GDMono::_on_core_api_assembly_loaded() {
- GDMonoCache::update_godot_api_cache();
-
- if (!GDMonoCache::cached_data.godot_api_cache_updated) {
- return false;
- }
-
- get_singleton()->_install_trace_listener();
-
- return true;
-}
-
-bool GDMono::_try_load_api_assemblies_preset() {
- return _try_load_api_assemblies(core_api_assembly, editor_api_assembly,
- get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded);
}
-
-void GDMono::_load_api_assemblies() {
- bool api_assemblies_loaded = _try_load_api_assemblies_preset();
-
-#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN)
- if (!api_assemblies_loaded) {
- // The API assemblies are out of sync or some other error happened. Fine, try one more time, but
- // this time update them from the prebuilt assemblies directory before trying to load them again.
-
- // Shouldn't happen. The project manager loads the prebuilt API assemblies
- CRASH_COND_MSG(Engine::get_singleton()->is_project_manager_hint(), "Failed to load one of the prebuilt API assemblies.");
-
- // 1. Unload the scripts domain
- Error domain_unload_err = _unload_scripts_domain();
- CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain.");
-
- // 2. Update the API assemblies
- String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);
- CRASH_COND_MSG(!update_error.is_empty(), update_error);
-
- // 3. Load the scripts domain again
- Error domain_load_err = _load_scripts_domain();
- CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
-
- // 4. Try loading the updated assemblies
- api_assemblies_loaded = _try_load_api_assemblies_preset();
- }
#endif
- if (!api_assemblies_loaded) {
- // welp... too bad
-
- if (_are_api_assemblies_out_of_sync()) {
- if (core_api_assembly.out_of_sync) {
- ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");
- } else if (!GDMonoCache::cached_data.godot_api_cache_updated) {
- ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");
- }
+void GDMono::_init_godot_api_hashes() {
+#ifdef DEBUG_METHODS_ENABLED
+ get_api_core_hash();
#ifdef TOOLS_ENABLED
- if (editor_api_assembly.out_of_sync) {
- ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync.");
- }
-#endif
-
- CRASH_NOW();
- } else {
- CRASH_NOW_MSG("Failed to load one of the API assemblies.");
- }
- }
+ get_api_editor_hash();
+#endif // TOOLS_ENABLED
+#endif // DEBUG_METHODS_ENABLED
}
#ifdef TOOLS_ENABLED
-bool GDMono::_load_tools_assemblies() {
- if (tools_assembly && tools_project_editor_assembly) {
- return true;
- }
-
- bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
- load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
-
- return success;
-}
-#endif
-
bool GDMono::_load_project_assembly() {
- if (project_assembly) {
- return true;
- }
-
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
-
- bool success = load_assembly(appname_safe, &project_assembly);
+ String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
- if (success) {
- mono_assembly_set_main(project_assembly->get_assembly());
- CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly);
- }
-
- return success;
-}
-
-void GDMono::_install_trace_listener() {
-#ifdef DEBUG_ENABLED
- // Install the trace listener now before the project assembly is loaded
- GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
- GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener");
-
- MonoException *exc = nullptr;
- install_func->invoke_raw(nullptr, nullptr, &exc);
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
+ if (assembly_name.is_empty()) {
+ assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
}
-#endif
-}
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::_load_scripts_domain() {
- ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG);
-
- print_verbose("Mono: Loading scripts domain...");
-
- scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts");
- ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain.");
-
- mono_domain_set(scripts_domain, true);
-
- return OK;
-}
+ String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
+ .path_join(assembly_name + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
-Error GDMono::_unload_scripts_domain() {
- ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
-
- print_verbose("Mono: Finalizing scripts domain...");
-
- if (mono_domain_get() != root_domain) {
- mono_domain_set(root_domain, true);
- }
-
- finalizing_scripts_domain = true;
-
- if (!mono_domain_finalize(scripts_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
+ if (!FileAccess::exists(assembly_path)) {
+ return false;
}
- finalizing_scripts_domain = false;
-
- mono_gc_collect(mono_gc_max_generation());
-
- GDMonoCache::clear_godot_api_cache();
-
- _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
-
- core_api_assembly.assembly = nullptr;
-#ifdef TOOLS_ENABLED
- editor_api_assembly.assembly = nullptr;
-#endif
-
- project_assembly = nullptr;
-#ifdef TOOLS_ENABLED
- tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
-#endif
-
- MonoDomain *domain = scripts_domain;
- scripts_domain = nullptr;
-
- print_verbose("Mono: Unloading scripts domain...");
+ String loaded_assembly_path;
+ bool success = plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16(), &loaded_assembly_path);
- MonoException *exc = nullptr;
- mono_domain_try_unload(domain, (MonoObject **)&exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown when unloading scripts domain.");
- GDMonoUtils::debug_unhandled_exception(exc);
- return FAILED;
+ if (success) {
+ project_assembly_path = loaded_assembly_path.simplify_path();
+ project_assembly_modified_time = FileAccess::get_modified_time(loaded_assembly_path);
}
- return OK;
+ return success;
}
#endif
#ifdef GD_MONO_HOT_RELOAD
-Error GDMono::reload_scripts_domain() {
+Error GDMono::reload_project_assemblies() {
ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
- if (scripts_domain) {
- Error domain_unload_err = _unload_scripts_domain();
- ERR_FAIL_COND_V_MSG(domain_unload_err != OK, domain_unload_err, "Mono: Failed to unload scripts domain.");
- }
-
- CSharpLanguage::get_singleton()->_on_scripts_domain_unloaded();
-
- Error domain_load_err = _load_scripts_domain();
- ERR_FAIL_COND_V_MSG(domain_load_err != OK, domain_load_err, "Mono: Failed to load scripts domain.");
+ finalizing_scripts_domain = true;
- // Load assemblies. The API and tools assemblies are required,
- // the application is aborted if these assemblies cannot be loaded.
+ CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload();
- _load_api_assemblies();
+ if (!get_plugin_callbacks().UnloadProjectPluginCallback()) {
+ ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies.");
+ }
-#if defined(TOOLS_ENABLED)
- bool tools_assemblies_loaded = _load_tools_assemblies();
- CRASH_COND_MSG(!tools_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-#endif
+ finalizing_scripts_domain = false;
// Load the project's main assembly. Here, during hot-reloading, we do
// consider failing to load the project's main assembly to be an error.
- // However, unlike the API and tools assemblies, the application can continue working.
if (!_load_project_assembly()) {
- print_error("Mono: Failed to load project assembly");
+ print_error(".NET: Failed to load project assembly.");
return ERR_CANT_OPEN;
}
@@ -1123,204 +517,38 @@ Error GDMono::reload_scripts_domain() {
}
#endif
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
- CRASH_COND(p_domain == nullptr);
- CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead
-
- String domain_name = mono_domain_get_friendly_name(p_domain);
-
- print_verbose("Mono: Unloading domain '" + domain_name + "'...");
-
- if (mono_domain_get() == p_domain) {
- mono_domain_set(root_domain, true);
- }
-
- if (!mono_domain_finalize(p_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
- }
-
- mono_gc_collect(mono_gc_max_generation());
-
- _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
-
- MonoException *exc = nullptr;
- mono_domain_try_unload(p_domain, (MonoObject **)&exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown when unloading domain '" + domain_name + "'.");
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return FAILED;
- }
-
- return OK;
-}
-#endif
-
-GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
- MonoImage *image = mono_class_get_image(p_raw_class);
-
- if (image == corlib_assembly->get_image()) {
- return corlib_assembly->get_class(p_raw_class);
- }
-
- int32_t domain_id = mono_domain_get_id(mono_domain_get());
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
-
- 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) {
- return klass;
- }
- }
- }
-
- return nullptr;
-}
-
-GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
- GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name);
- if (klass) {
- return klass;
- }
-
- int32_t domain_id = mono_domain_get_id(mono_domain_get());
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- GDMonoAssembly *assembly = E.value;
- klass = assembly->get_class(p_namespace, p_name);
- if (klass) {
- return klass;
- }
- }
-
- return nullptr;
-}
-
-void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- memdelete(E.value);
- }
-
- assemblies.erase(p_domain_id);
-}
-
-void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
- // This method will be called by the runtime when a thrown exception is not handled.
- // It won't be called when we manually treat a thrown exception as unhandled.
- // We assume the exception was already printed before calling this hook.
-
-#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
- if (EngineDebugger::is_active()) {
- EngineDebugger::get_singleton()->poll_events(false);
- }
-#endif
-
- exit(mono_environment_exitcode_get());
-
- GD_UNREACHABLE();
-}
-
GDMono::GDMono() {
singleton = this;
- gdmono_log = memnew(GDMonoLog);
-
runtime_initialized = false;
finalizing_scripts_domain = false;
- root_domain = nullptr;
- scripts_domain = nullptr;
-
- corlib_assembly = nullptr;
- project_assembly = nullptr;
-#ifdef TOOLS_ENABLED
- tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
-#endif
-
api_core_hash = 0;
#ifdef TOOLS_ENABLED
api_editor_hash = 0;
#endif
-
- unhandled_exception_policy = POLICY_TERMINATE_APP;
}
GDMono::~GDMono() {
- if (is_runtime_initialized()) {
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- if (scripts_domain) {
- Error err = _unload_scripts_domain();
- if (err != OK) {
- ERR_PRINT("Mono: Failed to unload scripts domain.");
- }
- }
-#else
- CRASH_COND(scripts_domain != root_domain);
-
- print_verbose("Mono: Finalizing scripts domain...");
-
- if (mono_domain_get() != root_domain) {
- mono_domain_set(root_domain, true);
- }
-
- finalizing_scripts_domain = true;
-
- if (!mono_domain_finalize(root_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
- }
-
- finalizing_scripts_domain = false;
-
- mono_gc_collect(mono_gc_max_generation());
-
- GDMonoCache::clear_godot_api_cache();
-
- _domain_assemblies_cleanup(mono_domain_get_id(root_domain));
-
- core_api_assembly.assembly = nullptr;
-
- project_assembly = nullptr;
-
- root_domain = nullptr;
- scripts_domain = nullptr;
-
- // Leave the rest to 'mono_jit_cleanup'
-#endif
-
- for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) {
- const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value;
+ finalizing_scripts_domain = true;
- for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) {
- memdelete(F.value);
- }
+ if (is_runtime_initialized()) {
+ if (GDMonoCache::godot_api_cache_updated) {
+ GDMonoCache::managed_callbacks.DisposablesTracker_OnGodotShuttingDown();
}
- assemblies.clear();
-
- print_verbose("Mono: Runtime cleanup...");
-
- mono_jit_cleanup(root_domain);
-
- print_verbose("Mono: Finalized");
+ }
- runtime_initialized = false;
+ if (hostfxr_dll_handle) {
+ OS::get_singleton()->close_dynamic_library(hostfxr_dll_handle);
}
+ finalizing_scripts_domain = false;
+ runtime_initialized = false;
+
#if defined(ANDROID_ENABLED)
gdmono::android::support::cleanup();
#endif
- if (gdmono_log) {
- memdelete(gdmono_log);
- }
-
singleton = nullptr;
}
@@ -1328,62 +556,7 @@ namespace mono_bind {
GodotSharp *GodotSharp::singleton = nullptr;
-void GodotSharp::attach_thread() {
- GDMonoUtils::attach_current_thread();
-}
-
-void GodotSharp::detach_thread() {
- GDMonoUtils::detach_current_thread();
-}
-
-int32_t GodotSharp::get_domain_id() {
- MonoDomain *domain = mono_domain_get();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-int32_t GodotSharp::get_scripts_domain_id() {
- ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(),
- -1, "The Mono runtime is not initialized");
- MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-bool GodotSharp::is_scripts_domain_loaded() {
- return GDMono::get_singleton() != nullptr &&
- GDMono::get_singleton()->is_runtime_initialized() &&
- GDMono::get_singleton()->get_scripts_domain() != nullptr;
-}
-
-bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
- return is_domain_finalizing_for_unload(p_domain_id);
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
- return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id));
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
- GDMono *gd_mono = GDMono::get_singleton();
-
- ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(),
- false, "The Mono runtime is not initialized");
-
- ERR_FAIL_NULL_V(p_domain, true);
-
- if (p_domain == gd_mono->get_scripts_domain() && gd_mono->is_finalizing_scripts_domain()) {
- return true;
- }
-
- return mono_domain_is_unloading(p_domain);
-}
-
-bool GodotSharp::is_runtime_shutting_down() {
- return mono_runtime_is_shutting_down();
-}
-
-bool GodotSharp::is_runtime_initialized() {
+bool GodotSharp::_is_runtime_initialized() {
return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
}
@@ -1399,16 +572,7 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) {
}
void GodotSharp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread);
- ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread);
-
- ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id);
- ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id);
- ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded);
- ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload);
-
- ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &GodotSharp::is_runtime_shutting_down);
- ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::is_runtime_initialized);
+ ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized);
ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
}
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 51fd0f8483..080b2c4669 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* gd_mono.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gd_mono.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GD_MONO_H
#define GD_MONO_H
@@ -34,131 +34,54 @@
#include "core/io/config_file.h"
#include "../godotsharp_defs.h"
-#include "gd_mono_assembly.h"
-#include "gd_mono_log.h"
-#ifdef WINDOWS_ENABLED
-#include "../utils/mono_reg_utils.h"
+#ifndef GD_CLR_STDCALL
+#ifdef WIN32
+#define GD_CLR_STDCALL __stdcall
+#else
+#define GD_CLR_STDCALL
+#endif
#endif
-namespace ApiAssemblyInfo {
-enum Type {
- API_CORE,
- API_EDITOR
-};
-
-struct Version {
- uint64_t godot_api_hash = 0;
- uint32_t bindings_version = 0;
- uint32_t cs_glue_version = 0;
-
- bool operator==(const Version &p_other) const {
- return godot_api_hash == p_other.godot_api_hash &&
- bindings_version == p_other.bindings_version &&
- cs_glue_version == p_other.cs_glue_version;
- }
-
- Version() {}
-
- Version(uint64_t p_godot_api_hash,
- uint32_t p_bindings_version,
- uint32_t p_cs_glue_version) :
- godot_api_hash(p_godot_api_hash),
- bindings_version(p_bindings_version),
- cs_glue_version(p_cs_glue_version) {
- }
+namespace gdmono {
- static Version get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, Type p_api_type);
+#ifdef TOOLS_ENABLED
+struct PluginCallbacks {
+ using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *, String *);
+ using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *, const void **, int32_t);
+ using FuncUnloadProjectPluginCallback = bool(GD_CLR_STDCALL *)();
+ FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr;
+ FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr;
+ FuncUnloadProjectPluginCallback UnloadProjectPluginCallback = nullptr;
};
+#endif
-String to_string(Type p_type);
-} // namespace ApiAssemblyInfo
+} // namespace gdmono
class GDMono {
-public:
- enum UnhandledExceptionPolicy {
- POLICY_TERMINATE_APP,
- POLICY_LOG_ERROR
- };
-
- struct LoadedApiAssembly {
- GDMonoAssembly *assembly = nullptr;
- bool out_of_sync = false;
-
- LoadedApiAssembly() {}
- };
-
-private:
bool runtime_initialized;
bool finalizing_scripts_domain;
- UnhandledExceptionPolicy unhandled_exception_policy;
+ void *hostfxr_dll_handle = nullptr;
+ bool is_native_aot = false;
- MonoDomain *root_domain = nullptr;
- MonoDomain *scripts_domain = nullptr;
+ String project_assembly_path;
+ uint64_t project_assembly_modified_time = 0;
- HashMap<int32_t, HashMap<String, GDMonoAssembly *>> assemblies;
-
- GDMonoAssembly *corlib_assembly = nullptr;
- GDMonoAssembly *project_assembly = nullptr;
#ifdef TOOLS_ENABLED
- GDMonoAssembly *tools_assembly = nullptr;
- GDMonoAssembly *tools_project_editor_assembly = nullptr;
-#endif
-
- LoadedApiAssembly core_api_assembly;
- LoadedApiAssembly editor_api_assembly;
-
- typedef bool (*CoreApiAssemblyLoadedCallback)();
-
- bool _are_api_assemblies_out_of_sync();
- bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config);
-
- bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
-#ifdef TOOLS_ENABLED
- bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
-#endif
-
- static bool _on_core_api_assembly_loaded();
-
- bool _load_corlib_assembly();
-#ifdef TOOLS_ENABLED
- bool _load_tools_assemblies();
-#endif
bool _load_project_assembly();
-
- bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
- const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback);
- bool _try_load_api_assemblies_preset();
- void _load_api_assemblies();
-
- void _install_trace_listener();
-
- void _register_internal_calls();
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- Error _load_scripts_domain();
- Error _unload_scripts_domain();
#endif
- void _domain_assemblies_cleanup(int32_t p_domain_id);
-
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
#endif
void _init_godot_api_hashes();
- void _init_exception_policy();
-
- GDMonoLog *gdmono_log = nullptr;
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- MonoRegInfo mono_reg_info;
+#ifdef TOOLS_ENABLED
+ gdmono::PluginCallbacks plugin_callbacks;
#endif
- void add_mono_shared_libs_dir_to_path();
- void determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir);
-
protected:
static GDMono *singleton;
@@ -192,107 +115,43 @@ public:
#endif
}
-#ifdef TOOLS_ENABLED
- bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
- String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = nullptr, const bool *p_editor_api_out_of_sync = nullptr);
-#endif
-
- static GDMono *get_singleton() { return singleton; }
-
- [[noreturn]] static void unhandled_exception_hook(MonoObject *p_exc, void *p_user_data);
-
- UnhandledExceptionPolicy get_unhandled_exception_policy() const { return unhandled_exception_policy; }
-
- // Do not use these, unless you know what you're doing
- void add_assembly(int32_t p_domain_id, GDMonoAssembly *p_assembly);
- GDMonoAssembly *get_loaded_assembly(const String &p_name);
-
- _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized && !mono_runtime_is_shutting_down() /* stays true after shutdown finished */; }
+ static GDMono *get_singleton() {
+ return singleton;
+ }
- _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
+ _FORCE_INLINE_ bool is_runtime_initialized() const {
+ return runtime_initialized;
+ }
+ _FORCE_INLINE_ bool is_finalizing_scripts_domain() {
+ return finalizing_scripts_domain;
+ }
- _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
+ _FORCE_INLINE_ const String &get_project_assembly_path() const {
+ return project_assembly_path;
+ }
+ _FORCE_INLINE_ uint64_t get_project_assembly_modified_time() const {
+ return project_assembly_modified_time;
+ }
- _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
- _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
-#endif
-
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- const MonoRegInfo &get_mono_reg_info() { return mono_reg_info; }
+ const gdmono::PluginCallbacks &get_plugin_callbacks() {
+ return plugin_callbacks;
+ }
#endif
- GDMonoClass *get_class(MonoClass *p_raw_class);
- GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
-
#ifdef GD_MONO_HOT_RELOAD
- Error reload_scripts_domain();
+ Error reload_project_assemblies();
#endif
- bool load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly = false);
- bool load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, bool p_refonly, const Vector<String> &p_search_dirs);
- bool load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly, bool p_refonly = false);
-
- Error finalize_and_unload_domain(MonoDomain *p_domain);
-
void initialize();
+#ifdef TOOLS_ENABLED
void initialize_load_assemblies();
+#endif
GDMono();
~GDMono();
};
-namespace gdmono {
-
-class ScopeDomain {
- MonoDomain *prev_domain = nullptr;
-
-public:
- ScopeDomain(MonoDomain *p_domain) {
- prev_domain = mono_domain_get();
- if (prev_domain != p_domain) {
- mono_domain_set(p_domain, false);
- } else {
- prev_domain = nullptr;
- }
- }
-
- ~ScopeDomain() {
- if (prev_domain) {
- mono_domain_set(prev_domain, false);
- }
- }
-};
-
-class ScopeExitDomainUnload {
- MonoDomain *domain = nullptr;
-
-public:
- ScopeExitDomainUnload(MonoDomain *p_domain) :
- domain(p_domain) {
- }
-
- ~ScopeExitDomainUnload() {
- if (domain) {
- GDMono::get_singleton()->finalize_and_unload_domain(domain);
- }
- }
-};
-} // namespace gdmono
-
-#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
- gdmono::ScopeDomain __gdmono__scope__domain__(m_mono_domain); \
- (void)__gdmono__scope__domain__;
-
-#define _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(m_mono_domain) \
- gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
- (void)__gdmono__scope__exit__domain__unload__;
-
namespace mono_bind {
class GodotSharp : public Object {
@@ -300,9 +159,8 @@ class GodotSharp : public Object {
friend class GDMono;
- bool _is_domain_finalizing_for_unload(int32_t p_domain_id);
-
void _reload_assemblies(bool p_soft_reload);
+ bool _is_runtime_initialized();
protected:
static GodotSharp *singleton;
@@ -311,20 +169,6 @@ protected:
public:
static GodotSharp *get_singleton() { return singleton; }
- void attach_thread();
- void detach_thread();
-
- int32_t get_domain_id();
- int32_t get_scripts_domain_id();
-
- bool is_scripts_domain_loaded();
-
- bool is_domain_finalizing_for_unload(int32_t p_domain_id);
- bool is_domain_finalizing_for_unload(MonoDomain *p_domain);
-
- bool is_runtime_shutting_down();
- bool is_runtime_initialized();
-
GodotSharp();
~GodotSharp();
};
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
deleted file mode 100644
index 42c6b6305f..0000000000
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ /dev/null
@@ -1,482 +0,0 @@
-/*************************************************************************/
-/* gd_mono_assembly.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 "gd_mono_assembly.h"
-
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/tokentype.h>
-
-#include "core/config/project_settings.h"
-#include "core/io/file_access.h"
-#include "core/io/file_access_pack.h"
-#include "core/os/os.h"
-#include "core/templates/list.h"
-
-#include "../godotsharp_dirs.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-
-Vector<String> GDMonoAssembly::search_dirs;
-
-void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
- String framework_dir;
-
- if (!p_custom_bcl_dir.is_empty()) {
- framework_dir = p_custom_bcl_dir;
- } else if (mono_assembly_getrootdir()) {
- framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
- }
-
- if (!framework_dir.is_empty()) {
- r_search_dirs.push_back(framework_dir);
- r_search_dirs.push_back(framework_dir.plus_file("Facades"));
- }
-
-#if !defined(TOOLS_ENABLED)
- String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
- if (!data_game_assemblies_dir.is_empty()) {
- r_search_dirs.push_back(data_game_assemblies_dir);
- }
-#endif
-
- if (p_custom_config.length()) {
- r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(p_custom_config));
- } else {
- r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
- }
-
- if (p_custom_config.is_empty()) {
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
- } else {
- String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
- }
-
- r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir());
- r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
- r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
-
-#ifdef TOOLS_ENABLED
- r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
-
- // For GodotTools to find the api assemblies
- r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
-#endif
-}
-
-// This is how these assembly loading hooks work:
-//
-// - The 'search' hook checks if the assembly has already been loaded, to avoid loading again.
-// - The 'preload' hook does the actual loading and is only called if the
-// 'search' hook didn't find the assembly in the list of loaded assemblies.
-// - The 'load' hook is called after the assembly has been loaded. Its job is to add the
-// assembly to the list of loaded assemblies so that the 'search' hook can look it up.
-
-void GDMonoAssembly::assembly_load_hook(MonoAssembly *assembly, [[maybe_unused]] void *user_data) {
- String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
-
- MonoImage *image = mono_assembly_get_image(assembly);
-
- GDMonoAssembly *gdassembly = memnew(GDMonoAssembly(name, image, assembly));
-
-#ifdef GD_MONO_HOT_RELOAD
- String path = String::utf8(mono_image_get_filename(image));
- if (FileAccess::exists(path)) {
- gdassembly->modified_time = FileAccess::get_modified_time(path);
- }
-#endif
-
- MonoDomain *domain = mono_domain_get();
- GDMono::get_singleton()->add_assembly(domain ? mono_domain_get_id(domain) : 0, gdassembly);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_search_hook(MonoAssemblyName *aname, void *user_data) {
- return GDMonoAssembly::_search_hook(aname, user_data, false);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data) {
- return GDMonoAssembly::_search_hook(aname, user_data, true);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
- return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, false);
-}
-
-MonoAssembly *GDMonoAssembly::assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data) {
- return GDMonoAssembly::_preload_hook(aname, assemblies_path, user_data, true);
-}
-
-MonoAssembly *GDMonoAssembly::_search_hook(MonoAssemblyName *aname, [[maybe_unused]] void *user_data, bool refonly) {
- String name = String::utf8(mono_assembly_name_get_name(aname));
- bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
- if (loaded_asm) {
- return loaded_asm->get_assembly();
- }
-
- return nullptr;
-}
-
-MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **, [[maybe_unused]] void *user_data, bool refonly) {
- String name = String::utf8(mono_assembly_name_get_name(aname));
- return _load_assembly_search(name, aname, refonly, search_dirs);
-}
-
-MonoAssembly *GDMonoAssembly::_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
- MonoAssembly *res = nullptr;
- String path;
-
- bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
-
- for (int i = 0; i < p_search_dirs.size(); i++) {
- const String &search_dir = p_search_dirs[i];
-
- if (has_extension) {
- path = search_dir.plus_file(p_name);
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
- } else {
- path = search_dir.plus_file(p_name + ".dll");
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
-
- path = search_dir.plus_file(p_name + ".exe");
- if (FileAccess::exists(path)) {
- res = _real_load_assembly_from(path, p_refonly, p_aname);
- if (res != nullptr) {
- return res;
- }
- }
- }
- }
-
- return nullptr;
-}
-
-String GDMonoAssembly::find_assembly(const String &p_name) {
- String path;
-
- bool has_extension = p_name.ends_with(".dll") || p_name.ends_with(".exe");
-
- for (int i = 0; i < search_dirs.size(); i++) {
- const String &search_dir = search_dirs[i];
-
- if (has_extension) {
- path = search_dir.plus_file(p_name);
- if (FileAccess::exists(path)) {
- return path;
- }
- } else {
- path = search_dir.plus_file(p_name + ".dll");
- if (FileAccess::exists(path)) {
- return path;
- }
-
- path = search_dir.plus_file(p_name + ".exe");
- if (FileAccess::exists(path)) {
- return path;
- }
- }
- }
-
- return String();
-}
-
-void GDMonoAssembly::initialize() {
- fill_search_dirs(search_dirs);
-
- mono_install_assembly_search_hook(&assembly_search_hook, nullptr);
- mono_install_assembly_refonly_search_hook(&assembly_refonly_search_hook, nullptr);
- mono_install_assembly_preload_hook(&assembly_preload_hook, nullptr);
- mono_install_assembly_refonly_preload_hook(&assembly_refonly_preload_hook, nullptr);
- mono_install_assembly_load_hook(&assembly_load_hook, nullptr);
-}
-
-MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
- Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
- ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location");
-
- String image_filename;
-
-#ifdef ANDROID_ENABLED
- if (p_path.begins_with("res://")) {
- image_filename = p_path.substr(6, p_path.length());
- } else {
- image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
- }
-#else
- // FIXME: globalize_path does not work on exported games
- image_filename = ProjectSettings::get_singleton()->globalize_path(p_path);
-#endif
-
- MonoImageOpenStatus status = MONO_IMAGE_OK;
-
- MonoImage *image = mono_image_open_from_data_with_name(
- (char *)&data[0], data.size(),
- true, &status, p_refonly,
- image_filename.utf8());
-
- ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !image, nullptr, "Failed to open assembly image from memory: '" + p_path + "'.");
-
- if (p_aname != nullptr) {
- // Check assembly version
- const MonoTableInfo *table = mono_image_get_table_info(image, MONO_TABLE_ASSEMBLY);
-
- ERR_FAIL_NULL_V(table, nullptr);
-
- if (mono_table_info_get_rows(table)) {
- uint32_t cols[MONO_ASSEMBLY_SIZE];
- mono_metadata_decode_row(table, 0, cols, MONO_ASSEMBLY_SIZE);
-
- // Not sure about .NET's policy. We will only ensure major and minor are equal, and ignore build and revision.
- uint16_t major = cols[MONO_ASSEMBLY_MAJOR_VERSION];
- uint16_t minor = cols[MONO_ASSEMBLY_MINOR_VERSION];
-
- uint16_t required_minor;
- uint16_t required_major = mono_assembly_name_get_version(p_aname, &required_minor, nullptr, nullptr);
-
- if (required_major != 0) {
- if (major != required_major && minor != required_minor) {
- mono_image_close(image);
- return nullptr;
- }
- }
- }
- }
-
-#ifdef DEBUG_ENABLED
- Vector<uint8_t> pdb_data;
- String pdb_path(p_path + ".pdb");
-
- if (!FileAccess::exists(pdb_path)) {
- pdb_path = p_path.get_basename() + ".pdb"; // without .dll
-
- if (!FileAccess::exists(pdb_path)) {
- goto no_pdb;
- }
- }
-
- pdb_data = FileAccess::get_file_as_array(pdb_path);
-
- // mono_debug_close_image doesn't seem to be needed
- mono_debug_open_image_from_memory(image, &pdb_data[0], pdb_data.size());
-
-no_pdb:
-
-#endif
-
- bool need_manual_load_hook = mono_image_get_assembly(image) != nullptr; // Re-using an existing image with an assembly loaded
-
- status = MONO_IMAGE_OK;
-
- MonoAssembly *assembly = mono_assembly_load_from_full(image, image_filename.utf8().get_data(), &status, p_refonly);
-
- ERR_FAIL_COND_V_MSG(status != MONO_IMAGE_OK || !assembly, nullptr, "Failed to load assembly for image");
-
- if (need_manual_load_hook) {
- // For some reason if an assembly survived domain reloading (maybe because it's referenced somewhere else),
- // the mono internal search hook don't detect it, yet mono_image_open_from_data_with_name re-uses the image
- // and assembly, and mono_assembly_load_from_full doesn't call the load hook. We need to call it manually.
- String name = String::utf8(mono_assembly_name_get_name(mono_assembly_get_name(assembly)));
- bool has_extension = name.ends_with(".dll") || name.ends_with(".exe");
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(has_extension ? name.get_basename() : name);
- if (!loaded_asm) {
- assembly_load_hook(assembly, nullptr);
- }
- }
-
- // Decrement refcount which was previously incremented by mono_image_open_from_data_with_name
- mono_image_close(image);
-
- return assembly;
-}
-
-void GDMonoAssembly::unload() {
- ERR_FAIL_NULL(image); // Should not be called if already unloaded
-
- for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) {
- memdelete(E.value);
- }
-
- cached_classes.clear();
- cached_raw.clear();
-
- assembly = nullptr;
- image = nullptr;
-}
-
-String GDMonoAssembly::get_path() const {
- return String::utf8(mono_image_get_filename(image));
-}
-
-bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, false);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoAssembly::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
-
- attributes = mono_custom_attrs_from_assembly(assembly);
- attrs_fetched = true;
-}
-
-GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
- ERR_FAIL_NULL_V(image, nullptr);
-
- ClassKey key(p_namespace, p_name);
-
- GDMonoClass **match = cached_classes.getptr(key);
-
- if (match) {
- return *match;
- }
-
- MonoClass *mono_class = mono_class_from_name(image, String(p_namespace).utf8(), String(p_name).utf8());
-
- if (!mono_class) {
- return nullptr;
- }
-
- GDMonoClass *wrapped_class = memnew(GDMonoClass(p_namespace, p_name, mono_class, this));
-
- cached_classes[key] = wrapped_class;
- cached_raw[mono_class] = wrapped_class;
-
- return wrapped_class;
-}
-
-GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
- ERR_FAIL_NULL_V(image, nullptr);
-
- HashMap<MonoClass *, GDMonoClass *>::Iterator match = cached_raw.find(p_mono_class);
-
- if (match) {
- return match->value;
- }
-
- StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
- StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
-
- GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
-
- cached_classes[ClassKey(namespace_name, class_name)] = wrapped_class;
- cached_raw[p_mono_class] = wrapped_class;
-
- return wrapped_class;
-}
-
-GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
- if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) {
- return GDMono::get_singleton()->get_corlib_assembly();
- }
-
- // We need to manually call the search hook in this case, as it won't be called in the next step
- MonoAssembly *assembly = mono_assembly_invoke_search_hook(p_aname);
-
- if (!assembly) {
- assembly = _load_assembly_search(p_name, p_aname, p_refonly, p_search_dirs);
- if (!assembly) {
- return nullptr;
- }
- }
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
- ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
- ERR_FAIL_COND_V(loaded_asm->get_assembly() != assembly, nullptr);
-
- return loaded_asm;
-}
-
-GDMonoAssembly *GDMonoAssembly::load_from(const String &p_name, const String &p_path, bool p_refonly) {
- if (p_name == "mscorlib" || p_name == "mscorlib.dll") {
- return GDMono::get_singleton()->get_corlib_assembly();
- }
-
- // We need to manually call the search hook in this case, as it won't be called in the next step
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- MonoAssembly *assembly = mono_assembly_invoke_search_hook(aname);
- mono_assembly_name_free(aname);
- mono_free(aname);
-
- if (!assembly) {
- assembly = _real_load_assembly_from(p_path, p_refonly);
- if (!assembly) {
- return nullptr;
- }
- }
-
- GDMonoAssembly *loaded_asm = GDMono::get_singleton()->get_loaded_assembly(p_name);
- ERR_FAIL_NULL_V_MSG(loaded_asm, nullptr, "Loaded assembly missing from table. Did we not receive the load hook?");
-
- return loaded_asm;
-}
-
-GDMonoAssembly::~GDMonoAssembly() {
- if (image) {
- unload();
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
deleted file mode 100644
index 0a3ae6c4fe..0000000000
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*************************************************************************/
-/* gd_mono_assembly.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 GD_MONO_ASSEMBLY_H
-#define GD_MONO_ASSEMBLY_H
-
-#include <mono/jit/jit.h>
-#include <mono/metadata/assembly.h>
-
-#include "core/string/ustring.h"
-#include "core/templates/hash_map.h"
-#include "core/templates/rb_map.h"
-#include "gd_mono_utils.h"
-
-class GDMonoAssembly {
- struct ClassKey {
- struct Hasher {
- static _FORCE_INLINE_ uint32_t hash(const ClassKey &p_key) {
- uint32_t hash = 0;
-
- GDMonoUtils::hash_combine(hash, p_key.namespace_name.hash());
- GDMonoUtils::hash_combine(hash, p_key.class_name.hash());
-
- return hash;
- }
- };
-
- _FORCE_INLINE_ bool operator==(const ClassKey &p_a) const {
- return p_a.class_name == class_name && p_a.namespace_name == namespace_name;
- }
-
- ClassKey() {}
-
- ClassKey(const StringName &p_namespace_name, const StringName &p_class_name) {
- namespace_name = p_namespace_name;
- class_name = p_class_name;
- }
-
- StringName namespace_name;
- StringName class_name;
- };
-
- String name;
- MonoImage *image = nullptr;
- MonoAssembly *assembly = nullptr;
-
- bool attrs_fetched = false;
- MonoCustomAttrInfo *attributes = nullptr;
-
-#ifdef GD_MONO_HOT_RELOAD
- uint64_t modified_time = 0;
-#endif
-
- HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
- HashMap<MonoClass *, GDMonoClass *> cached_raw;
-
- static Vector<String> search_dirs;
-
- static void assembly_load_hook(MonoAssembly *assembly, void *user_data);
- static MonoAssembly *assembly_search_hook(MonoAssemblyName *aname, void *user_data);
- static MonoAssembly *assembly_refonly_search_hook(MonoAssemblyName *aname, void *user_data);
- static MonoAssembly *assembly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
- static MonoAssembly *assembly_refonly_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data);
-
- static MonoAssembly *_search_hook(MonoAssemblyName *aname, void *user_data, bool refonly);
- static MonoAssembly *_preload_hook(MonoAssemblyName *aname, char **assemblies_path, void *user_data, bool refonly);
-
- static MonoAssembly *_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname = nullptr);
- static MonoAssembly *_load_assembly_search(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
-
- friend class GDMono;
- static void initialize();
-
-public:
- void unload();
-
- _FORCE_INLINE_ MonoImage *get_image() const { return image; }
- _FORCE_INLINE_ MonoAssembly *get_assembly() const { return assembly; }
- _FORCE_INLINE_ String get_name() const { return name; }
-
-#ifdef GD_MONO_HOT_RELOAD
- _FORCE_INLINE_ uint64_t get_modified_time() const { return modified_time; }
-#endif
-
- String get_path() const;
-
- bool has_attribute(GDMonoClass *p_attr_class);
- MonoObject *get_attribute(GDMonoClass *p_attr_class);
-
- void fetch_attributes();
-
- GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
- GDMonoClass *get_class(MonoClass *p_mono_class);
-
- static String find_assembly(const String &p_name);
-
- static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
- static const Vector<String> &get_default_search_dirs() { return search_dirs; }
-
- static GDMonoAssembly *load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs);
- static GDMonoAssembly *load_from(const String &p_name, const String &p_path, bool p_refonly);
-
- GDMonoAssembly(const String &p_name, MonoImage *p_image, MonoAssembly *p_assembly) :
- name(p_name),
- image(p_image),
- assembly(p_assembly) {
- }
- ~GDMonoAssembly();
-};
-
-#endif // GD_MONO_ASSEMBLY_H
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 69d8c7edc9..3ef7554a3f 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -1,339 +1,95 @@
-/*************************************************************************/
-/* gd_mono_cache.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gd_mono_cache.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gd_mono_cache.h"
-#include "gd_mono.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_method.h"
-#include "gd_mono_utils.h"
+#include "core/error/error_macros.h"
namespace GDMonoCache {
-CachedData cached_data;
+ManagedCallbacks managed_callbacks;
+bool godot_api_cache_updated = false;
-#define CACHE_AND_CHECK(m_var, m_val) \
- { \
- CRASH_COND(m_var != nullptr); \
- m_var = m_val; \
- ERR_FAIL_COND_MSG(m_var == nullptr, "Mono Cache: Member " #m_var " is null."); \
- }
-
-#define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_class, m_val)
-#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(cached_data.class_##m_ns##_##m_class, m_val)
-#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(cached_data.rawclass_##m_class, m_val)
-#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(cached_data.field_##m_class##_##m_field, m_val)
-#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(cached_data.method_##m_class##_##m_method, m_val)
-#define CACHE_PROPERTY_AND_CHECK(m_class, m_property, m_val) CACHE_AND_CHECK(cached_data.property_##m_class##_##m_property, m_val)
+void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
+ int checked_count = 0;
-#define CACHE_METHOD_THUNK_AND_CHECK_IMPL(m_var, m_val) \
- { \
- CRASH_COND(!m_var.is_null()); \
- ERR_FAIL_COND_MSG(m_val == nullptr, "Mono Cache: Method for member " #m_var " is null."); \
- m_var.set_from_method(m_val); \
- ERR_FAIL_COND_MSG(m_var.is_null(), "Mono Cache: Member " #m_var " is null."); \
+#define CHECK_CALLBACK_NOT_NULL_IMPL(m_var, m_class, m_method) \
+ { \
+ ERR_FAIL_COND_MSG(m_var == nullptr, \
+ "Mono Cache: Managed callback for '" #m_class "_" #m_method "' is null."); \
+ checked_count += 1; \
}
-#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_METHOD_THUNK_AND_CHECK_IMPL(cached_data.methodthunk_##m_class##_##m_method, m_val)
-
-void CachedData::clear_corlib_cache() {
- corlib_cache_updated = false;
-
- class_MonoObject = nullptr;
- class_bool = nullptr;
- class_int8_t = nullptr;
- class_int16_t = nullptr;
- class_int32_t = nullptr;
- class_int64_t = nullptr;
- class_uint8_t = nullptr;
- class_uint16_t = nullptr;
- class_uint32_t = nullptr;
- class_uint64_t = nullptr;
- class_float = nullptr;
- class_double = nullptr;
- class_String = nullptr;
- class_IntPtr = nullptr;
-
- class_System_Collections_IEnumerable = nullptr;
- class_System_Collections_ICollection = nullptr;
- class_System_Collections_IDictionary = nullptr;
-
-#ifdef DEBUG_ENABLED
- class_System_Diagnostics_StackTrace = nullptr;
- methodthunk_System_Diagnostics_StackTrace_GetFrames.nullify();
- method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
- method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
-#endif
-
- class_KeyNotFoundException = nullptr;
-}
-
-void CachedData::clear_godot_api_cache() {
- godot_api_cache_updated = false;
-
- rawclass_Dictionary = nullptr;
-
- class_Vector2 = nullptr;
- class_Vector2i = nullptr;
- class_Rect2 = nullptr;
- class_Rect2i = nullptr;
- 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;
- class_StringName = nullptr;
- class_NodePath = nullptr;
- class_RID = nullptr;
- class_GodotObject = nullptr;
- class_GodotResource = nullptr;
- class_Node = nullptr;
- class_Control = nullptr;
- class_Node3D = nullptr;
- class_WeakRef = nullptr;
- class_Callable = nullptr;
- class_SignalInfo = nullptr;
- class_Array = nullptr;
- class_Dictionary = nullptr;
- class_MarshalUtils = nullptr;
- class_ISerializationListener = nullptr;
-
-#ifdef DEBUG_ENABLED
- class_DebuggingUtils = nullptr;
- methodthunk_DebuggingUtils_GetStackFrameInfo.nullify();
-#endif
-
- class_ExportAttribute = nullptr;
- field_ExportAttribute_hint = nullptr;
- field_ExportAttribute_hintString = nullptr;
- class_SignalAttribute = nullptr;
- class_ToolAttribute = 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;
- field_ScriptPathAttribute_path = nullptr;
- class_AssemblyHasScriptsAttribute = nullptr;
- field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
- field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
-
- field_GodotObject_ptr = nullptr;
- field_StringName_ptr = nullptr;
- field_NodePath_ptr = nullptr;
- field_Image_ptr = nullptr;
- field_RID_ptr = nullptr;
-
- methodthunk_GodotObject_Dispose.nullify();
- methodthunk_Array_GetPtr.nullify();
- methodthunk_Dictionary_GetPtr.nullify();
- methodthunk_SignalAwaiter_SignalCallback.nullify();
- methodthunk_GodotTaskScheduler_Activate.nullify();
-
- methodthunk_Delegate_Equals.nullify();
-
- methodthunk_DelegateUtils_TrySerializeDelegate.nullify();
- methodthunk_DelegateUtils_TryDeserializeDelegate.nullify();
-
- // Start of MarshalUtils methods
-
- methodthunk_MarshalUtils_TypeIsGenericArray.nullify();
- methodthunk_MarshalUtils_TypeIsGenericDictionary.nullify();
- methodthunk_MarshalUtils_TypeIsSystemGenericList.nullify();
- methodthunk_MarshalUtils_TypeIsSystemGenericDictionary.nullify();
- methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
- methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
- methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
- methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
-
- methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
-
- methodthunk_MarshalUtils_ArrayGetElementType.nullify();
- methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify();
-
- methodthunk_MarshalUtils_MakeGenericArrayType.nullify();
- methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify();
-
- // End of MarshalUtils methods
-
- task_scheduler_handle = Ref<MonoGCHandleRef>();
-}
-
-#define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
-#define GODOT_API_NS_CLASS(m_ns, m_class) (GDMono::get_singleton()->get_core_api_assembly()->get_class(m_ns, #m_class))
-
-void update_corlib_cache() {
- CACHE_CLASS_AND_CHECK(MonoObject, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_object_class()));
- CACHE_CLASS_AND_CHECK(bool, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_boolean_class()));
- CACHE_CLASS_AND_CHECK(int8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_sbyte_class()));
- CACHE_CLASS_AND_CHECK(int16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int16_class()));
- CACHE_CLASS_AND_CHECK(int32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int32_class()));
- CACHE_CLASS_AND_CHECK(int64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_int64_class()));
- CACHE_CLASS_AND_CHECK(uint8_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_byte_class()));
- CACHE_CLASS_AND_CHECK(uint16_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint16_class()));
- CACHE_CLASS_AND_CHECK(uint32_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint32_class()));
- CACHE_CLASS_AND_CHECK(uint64_t, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_uint64_class()));
- CACHE_CLASS_AND_CHECK(float, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_single_class()));
- CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
- CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
- CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
-
- CACHE_CLASS_AND_CHECK(System_Collections_IEnumerable, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IEnumerable"));
- CACHE_CLASS_AND_CHECK(System_Collections_ICollection, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "ICollection"));
- CACHE_CLASS_AND_CHECK(System_Collections_IDictionary, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections", "IDictionary"));
-
-#ifdef DEBUG_ENABLED
- CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
- CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames"));
- CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
- CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
-#endif
-
- CACHE_METHOD_THUNK_AND_CHECK(Delegate, Equals, GDMono::get_singleton()->get_corlib_assembly()->get_class("System", "Delegate")->get_method_with_desc("System.Delegate:Equals(object)", true));
-
- CACHE_CLASS_AND_CHECK(KeyNotFoundException, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Collections.Generic", "KeyNotFoundException"));
-
- cached_data.corlib_cache_updated = true;
-}
-
-void update_godot_api_cache() {
- CACHE_CLASS_AND_CHECK(Vector2, GODOT_API_CLASS(Vector2));
- CACHE_CLASS_AND_CHECK(Vector2i, GODOT_API_CLASS(Vector2i));
- CACHE_CLASS_AND_CHECK(Rect2, GODOT_API_CLASS(Rect2));
- CACHE_CLASS_AND_CHECK(Rect2i, GODOT_API_CLASS(Rect2i));
- 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));
- CACHE_CLASS_AND_CHECK(StringName, GODOT_API_CLASS(StringName));
- CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath));
- CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID));
- CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object));
- CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource));
- CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node));
- CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control));
- CACHE_CLASS_AND_CHECK(Node3D, GODOT_API_CLASS(Node3D));
- CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
- CACHE_CLASS_AND_CHECK(Callable, GODOT_API_CLASS(Callable));
- CACHE_CLASS_AND_CHECK(SignalInfo, GODOT_API_CLASS(SignalInfo));
- CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
- CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
- CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
- CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
-
-#ifdef DEBUG_ENABLED
- CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
-#endif
-
- // Attributes
- CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
- CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
- 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(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));
- CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path"));
- CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute));
- CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup"));
- CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes"));
-
- CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
- CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD));
- CACHE_FIELD_AND_CHECK(NodePath, ptr, CACHED_CLASS(NodePath)->get_field(BINDINGS_PTR_FIELD));
- CACHE_FIELD_AND_CHECK(RID, ptr, CACHED_CLASS(RID)->get_field(BINDINGS_PTR_FIELD));
-
- CACHE_METHOD_THUNK_AND_CHECK(GodotObject, Dispose, CACHED_CLASS(GodotObject)->get_method("Dispose", 0));
- CACHE_METHOD_THUNK_AND_CHECK(Array, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Array)->get_method("GetPtr", 0));
- CACHE_METHOD_THUNK_AND_CHECK(Dictionary, GetPtr, GODOT_API_NS_CLASS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)->get_method("GetPtr", 0));
- CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, SignalCallback, GODOT_API_CLASS(SignalAwaiter)->get_method("SignalCallback", 1));
- CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0));
-
- CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TrySerializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TrySerializeDelegate", 2));
- CACHE_METHOD_THUNK_AND_CHECK(DelegateUtils, TryDeserializeDelegate, GODOT_API_CLASS(DelegateUtils)->get_method("TryDeserializeDelegate", 2));
-
- // Start of MarshalUtils methods
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericArray", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericDictionary", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericList, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericList", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsSystemGenericDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsSystemGenericDictionary", 1));
- 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));
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3));
-
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericArrayType", 1));
- CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, GODOT_API_CLASS(MarshalUtils)->get_method("MakeGenericDictionaryType", 2));
-
- // End of MarshalUtils methods
-
-#ifdef DEBUG_ENABLED
- CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4));
-#endif
-
- // TODO Move to CSharpLanguage::init() and do handle disposal
- MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
- GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
- cached_data.task_scheduler_handle = MonoGCHandleRef::create_strong(task_scheduler);
+#define CHECK_CALLBACK_NOT_NULL(m_class, m_method) CHECK_CALLBACK_NOT_NULL_IMPL(p_managed_callbacks.m_class##_##m_method, m_class, m_method)
+
+ CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetScriptNativeName);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SetGodotObjectPtr);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RaiseEventSignal);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, ScriptIsOrInherits);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, TryReloadRegisteredScriptWithClass);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyInfoList);
+ CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyDefaultValues);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Call);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Set);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, Get);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallDispose);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallToString);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, HasMethodUnknownParams);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState);
+ CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState);
+ CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle);
+ CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo);
+ CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown);
+ CHECK_CALLBACK_NOT_NULL(GD, OnCoreApiAssemblyLoaded);
+
+ managed_callbacks = p_managed_callbacks;
+
+ // It's easy to forget to add new callbacks here, so this should help
+ if (checked_count * sizeof(void *) != sizeof(ManagedCallbacks)) {
+ int missing_count = (sizeof(ManagedCallbacks) / sizeof(void *)) - checked_count;
+ WARN_PRINT("The presence of " + itos(missing_count) + " callback(s) was not validated");
+ }
- cached_data.godot_api_cache_updated = true;
+ godot_api_cache_updated = true;
}
} // namespace GDMonoCache
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index e9cc26899e..2554dfff97 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -1,204 +1,150 @@
-/*************************************************************************/
-/* gd_mono_cache.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* gd_mono_cache.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GD_MONO_CACHE_H
#define GD_MONO_CACHE_H
-#include "gd_mono_header.h"
-#include "gd_mono_method_thunk.h"
+#include <stdint.h>
-namespace GDMonoCache {
-
-struct CachedData {
- // -----------------------------------------------
- // corlib classes
+#include "../csharp_script.h"
+#include "../interop_types.h"
+#include "../mono_gc_handle.h"
+#include "core/object/object.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
+#include "core/variant/callable.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/variant.h"
- // Let's use the no-namespace format for these too
- GDMonoClass *class_MonoObject = nullptr; // object
- GDMonoClass *class_bool = nullptr; // bool
- GDMonoClass *class_int8_t = nullptr; // sbyte
- GDMonoClass *class_int16_t = nullptr; // short
- GDMonoClass *class_int32_t = nullptr; // int
- GDMonoClass *class_int64_t = nullptr; // long
- GDMonoClass *class_uint8_t = nullptr; // byte
- GDMonoClass *class_uint16_t = nullptr; // ushort
- GDMonoClass *class_uint32_t = nullptr; // uint
- GDMonoClass *class_uint64_t = nullptr; // ulong
- GDMonoClass *class_float = nullptr; // float
- GDMonoClass *class_double = nullptr; // double
- GDMonoClass *class_String = nullptr; // string
- GDMonoClass *class_IntPtr = nullptr; // System.IntPtr
+class CSharpScript;
- GDMonoClass *class_System_Collections_IEnumerable = nullptr;
- GDMonoClass *class_System_Collections_ICollection = nullptr;
- GDMonoClass *class_System_Collections_IDictionary = nullptr;
+namespace GDMonoCache {
-#ifdef DEBUG_ENABLED
- GDMonoClass *class_System_Diagnostics_StackTrace = nullptr;
- GDMonoMethodThunkR<MonoArray *, MonoObject *> methodthunk_System_Diagnostics_StackTrace_GetFrames;
- GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool = nullptr;
- GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool = nullptr;
+#ifndef GD_CLR_STDCALL
+#ifdef WIN32
+#define GD_CLR_STDCALL __stdcall
+#else
+#define GD_CLR_STDCALL
#endif
-
- GDMonoClass *class_KeyNotFoundException = nullptr;
-
- MonoClass *rawclass_Dictionary = nullptr;
- // -----------------------------------------------
-
- GDMonoClass *class_Vector2 = nullptr;
- GDMonoClass *class_Vector2i = nullptr;
- GDMonoClass *class_Rect2 = nullptr;
- GDMonoClass *class_Rect2i = nullptr;
- 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;
- GDMonoClass *class_StringName = nullptr;
- GDMonoClass *class_NodePath = nullptr;
- GDMonoClass *class_RID = nullptr;
- GDMonoClass *class_GodotObject = nullptr;
- GDMonoClass *class_GodotResource = nullptr;
- GDMonoClass *class_Node = nullptr;
- GDMonoClass *class_Control = nullptr;
- GDMonoClass *class_Node3D = nullptr;
- GDMonoClass *class_WeakRef = nullptr;
- GDMonoClass *class_Callable = nullptr;
- GDMonoClass *class_SignalInfo = nullptr;
- GDMonoClass *class_Array = nullptr;
- GDMonoClass *class_Dictionary = nullptr;
- GDMonoClass *class_MarshalUtils = nullptr;
- GDMonoClass *class_ISerializationListener = nullptr;
-
-#ifdef DEBUG_ENABLED
- GDMonoClass *class_DebuggingUtils = nullptr;
- GDMonoMethodThunk<MonoObject *, MonoString **, int *, MonoString **> methodthunk_DebuggingUtils_GetStackFrameInfo;
#endif
- GDMonoClass *class_ExportAttribute = nullptr;
- GDMonoField *field_ExportAttribute_hint = nullptr;
- GDMonoField *field_ExportAttribute_hintString = nullptr;
- GDMonoClass *class_SignalAttribute = nullptr;
- GDMonoClass *class_ToolAttribute = 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;
- GDMonoField *field_ScriptPathAttribute_path = nullptr;
- GDMonoClass *class_AssemblyHasScriptsAttribute = nullptr;
- GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
- GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
-
- GDMonoField *field_GodotObject_ptr = nullptr;
- GDMonoField *field_StringName_ptr = nullptr;
- GDMonoField *field_NodePath_ptr = nullptr;
- GDMonoField *field_Image_ptr = nullptr;
- GDMonoField *field_RID_ptr = nullptr;
-
- GDMonoMethodThunk<MonoObject *> methodthunk_GodotObject_Dispose;
- GDMonoMethodThunkR<Array *, MonoObject *> methodthunk_Array_GetPtr;
- GDMonoMethodThunkR<Dictionary *, MonoObject *> methodthunk_Dictionary_GetPtr;
- GDMonoMethodThunk<MonoObject *, MonoArray *> methodthunk_SignalAwaiter_SignalCallback;
- GDMonoMethodThunk<MonoObject *> methodthunk_GodotTaskScheduler_Activate;
-
- GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoObject *> methodthunk_Delegate_Equals;
-
- GDMonoMethodThunkR<MonoBoolean, MonoDelegate *, MonoObject *> methodthunk_DelegateUtils_TrySerializeDelegate;
- GDMonoMethodThunkR<MonoBoolean, MonoObject *, MonoDelegate **> methodthunk_DelegateUtils_TryDeserializeDelegate;
-
- // Start of MarshalUtils methods
-
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericArray;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericDictionary;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericList;
- GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsSystemGenericDictionary;
- 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;
-
- GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType;
- GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
-
- GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType;
- GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType;
-
- // End of MarshalUtils methods
-
- Ref<MonoGCHandleRef> task_scheduler_handle;
-
- bool corlib_cache_updated;
- bool godot_api_cache_updated;
-
- void clear_corlib_cache();
- void clear_godot_api_cache();
+struct godotsharp_property_info {
+ godot_string_name name; // Not owned
+ godot_string hint_string;
+ Variant::Type type;
+ PropertyHint hint;
+ PropertyUsageFlags usage;
+ bool exported;
+};
- CachedData() {
- clear_corlib_cache();
- clear_godot_api_cache();
- }
+struct godotsharp_property_def_val_pair {
+ godot_string_name name; // Not owned
+ godot_variant value;
};
-extern CachedData cached_data;
+struct ManagedCallbacks {
+ using Callback_ScriptManagerBridge_GetPropertyInfoList_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, const String *, void *p_props, int32_t p_count);
+ using Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add = void(GD_CLR_STDCALL *)(CSharpScript *p_script, void *p_def_vals, int32_t p_count);
+
+ using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *);
+ using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *);
+ using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
+ using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
+ using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
+ using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
+ using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *);
+ using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t);
+ using FuncScriptManagerBridge_GetScriptNativeName = void(GD_CLR_STDCALL *)(const CSharpScript *, StringName *);
+ using FuncScriptManagerBridge_SetGodotObjectPtr = void(GD_CLR_STDCALL *)(GCHandleIntPtr, Object *);
+ using FuncScriptManagerBridge_RaiseEventSignal = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, bool *);
+ using FuncScriptManagerBridge_ScriptIsOrInherits = bool(GD_CLR_STDCALL *)(const CSharpScript *, const CSharpScript *);
+ using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *);
+ using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
+ using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
+ using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
+ using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
+ using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
+ using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
+ using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
+ using FuncCSharpInstanceBridge_Call = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant **, int32_t, Callable::CallError *, Variant *);
+ using FuncCSharpInstanceBridge_Set = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, const Variant *);
+ using FuncCSharpInstanceBridge_Get = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *, Variant *);
+ using FuncCSharpInstanceBridge_CallDispose = void(GD_CLR_STDCALL *)(GCHandleIntPtr, bool);
+ using FuncCSharpInstanceBridge_CallToString = void(GD_CLR_STDCALL *)(GCHandleIntPtr, String *, bool *);
+ using FuncCSharpInstanceBridge_HasMethodUnknownParams = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *);
+ using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
+ using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
+ using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr);
+ using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *);
+ using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)();
+ using FuncGD_OnCoreApiAssemblyLoaded = void(GD_CLR_STDCALL *)(bool);
+
+ FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback;
+ FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
+ FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals;
+ FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
+ FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
+ FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
+ FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding;
+ FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance;
+ FuncScriptManagerBridge_GetScriptNativeName ScriptManagerBridge_GetScriptNativeName;
+ FuncScriptManagerBridge_SetGodotObjectPtr ScriptManagerBridge_SetGodotObjectPtr;
+ FuncScriptManagerBridge_RaiseEventSignal ScriptManagerBridge_RaiseEventSignal;
+ FuncScriptManagerBridge_ScriptIsOrInherits ScriptManagerBridge_ScriptIsOrInherits;
+ FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge;
+ FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
+ FuncScriptManagerBridge_RemoveScriptBridge ScriptManagerBridge_RemoveScriptBridge;
+ FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
+ FuncScriptManagerBridge_UpdateScriptClassInfo ScriptManagerBridge_UpdateScriptClassInfo;
+ FuncScriptManagerBridge_SwapGCHandleForType ScriptManagerBridge_SwapGCHandleForType;
+ FuncScriptManagerBridge_GetPropertyInfoList ScriptManagerBridge_GetPropertyInfoList;
+ FuncScriptManagerBridge_GetPropertyDefaultValues ScriptManagerBridge_GetPropertyDefaultValues;
+ FuncCSharpInstanceBridge_Call CSharpInstanceBridge_Call;
+ FuncCSharpInstanceBridge_Set CSharpInstanceBridge_Set;
+ FuncCSharpInstanceBridge_Get CSharpInstanceBridge_Get;
+ FuncCSharpInstanceBridge_CallDispose CSharpInstanceBridge_CallDispose;
+ FuncCSharpInstanceBridge_CallToString CSharpInstanceBridge_CallToString;
+ FuncCSharpInstanceBridge_HasMethodUnknownParams CSharpInstanceBridge_HasMethodUnknownParams;
+ FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState;
+ FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState;
+ FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle;
+ FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo;
+ FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown;
+ FuncGD_OnCoreApiAssemblyLoaded GD_OnCoreApiAssemblyLoaded;
+};
-void update_corlib_cache();
-void update_godot_api_cache();
+extern ManagedCallbacks managed_callbacks;
+extern bool godot_api_cache_updated;
-inline void clear_corlib_cache() {
- cached_data.clear_corlib_cache();
-}
+void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks);
-inline void clear_godot_api_cache() {
- cached_data.clear_godot_api_cache();
-}
} // namespace GDMonoCache
-#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)
-#define CACHED_CLASS_RAW(m_class) (GDMonoCache::cached_data.class_##m_class->get_mono_ptr())
-#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoCache::cached_data.rawclass_##m_class)
-#define CACHED_FIELD(m_class, m_field) (GDMonoCache::cached_data.field_##m_class##_##m_field)
-#define CACHED_METHOD(m_class, m_method) (GDMonoCache::cached_data.method_##m_class##_##m_method)
-#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoCache::cached_data.methodthunk_##m_class##_##m_method)
-#define CACHED_PROPERTY(m_class, m_property) (GDMonoCache::cached_data.property_##m_class##_##m_property)
-
#endif // GD_MONO_CACHE_H
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
deleted file mode 100644
index 51c5aa3542..0000000000
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-/*************************************************************************/
-/* gd_mono_class.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 "gd_mono_class.h"
-
-#include <mono/metadata/attrdefs.h>
-#include <mono/metadata/debug-helpers.h>
-
-#include "gd_mono_assembly.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_marshal.h"
-
-String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
- // mono_type_get_full_name is not exposed to embedders, but this seems to do the job
- MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class));
-
- MonoException *exc = nullptr;
- MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return GDMonoMarshal::mono_string_to_godot(str);
-}
-
-MonoType *GDMonoClass::get_mono_type(MonoClass *p_mono_class) {
- return mono_class_get_type(p_mono_class);
-}
-
-String GDMonoClass::get_full_name() const {
- return get_full_name(mono_class);
-}
-
-String GDMonoClass::get_type_desc() const {
- return GDMonoUtils::get_type_desc(get_mono_type());
-}
-
-MonoType *GDMonoClass::get_mono_type() const {
- // Careful, you cannot compare two MonoType*.
- // There is mono_metadata_type_equal, how is this different from comparing two MonoClass*?
- return get_mono_type(mono_class);
-}
-
-uint32_t GDMonoClass::get_flags() const {
- return mono_class_get_flags(mono_class);
-}
-
-bool GDMonoClass::is_static() const {
- uint32_t static_class_flags = MONO_TYPE_ATTR_ABSTRACT | MONO_TYPE_ATTR_SEALED;
- return (get_flags() & static_class_flags) == static_class_flags;
-}
-
-bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
- return mono_class_is_assignable_from(mono_class, p_from->mono_class);
-}
-
-StringName GDMonoClass::get_namespace() const {
- GDMonoClass *nesting_class = get_nesting_class();
- if (!nesting_class) {
- return namespace_name;
- }
- return nesting_class->get_namespace();
-}
-
-String GDMonoClass::get_name_for_lookup() const {
- GDMonoClass *nesting_class = get_nesting_class();
- if (!nesting_class) {
- return class_name;
- }
- return nesting_class->get_name_for_lookup() + "/" + class_name;
-}
-
-GDMonoClass *GDMonoClass::get_parent_class() const {
- MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
- return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : nullptr;
-}
-
-GDMonoClass *GDMonoClass::get_nesting_class() const {
- MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
- return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : nullptr;
-}
-
-#ifdef TOOLS_ENABLED
-Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
- bool class_is_enum = mono_class_is_enum(mono_class);
- ERR_FAIL_COND_V(!class_is_enum, Vector<MonoClassField *>());
-
- Vector<MonoClassField *> enum_fields;
-
- void *iter = nullptr;
- MonoClassField *raw_field = nullptr;
- while ((raw_field = mono_class_get_fields(get_mono_ptr(), &iter)) != nullptr) {
- uint32_t field_flags = mono_field_get_flags(raw_field);
-
- // Enums have an instance field named value__ which holds the value of the enum.
- // Enum constants are static, so we will use this to ignore the value__ field.
- if (field_flags & MONO_FIELD_ATTR_PUBLIC && field_flags & MONO_FIELD_ATTR_STATIC) {
- enum_fields.push_back(raw_field);
- }
- }
-
- return enum_fields;
-}
-#endif
-
-bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, false);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoClass::get_attribute(GDMonoClass *p_attr_class) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-#endif
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoClass::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
-
- attributes = mono_custom_attrs_from_class(get_mono_ptr());
- attrs_fetched = true;
-}
-
-void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) {
- CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this));
-
- if (methods_fetched) {
- return;
- }
-
- void *iter = nullptr;
- MonoMethod *raw_method = nullptr;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- StringName name = String::utf8(mono_method_get_name(raw_method));
-
- // get_method implicitly fetches methods and adds them to this->methods
- GDMonoMethod *method = get_method(raw_method, name);
- ERR_CONTINUE(!method);
-
- if (method->get_name() != name) {
-#ifdef DEBUG_ENABLED
- String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
- WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" +
- method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'.");
-#endif
- continue;
- }
-
-#ifdef DEBUG_ENABLED
- // For debug builds, we also fetched from native base classes as well before if this is not a native base class.
- // This allows us to warn the user here if they are using snake_case by mistake.
-
- if (p_native_base != this) {
- GDMonoClass *native_top = p_native_base;
- while (native_top) {
- GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count());
-
- if (m && m->get_name() != name) {
- // found
- String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
- WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() +
- "'. In class '" + namespace_name + "." + class_name + "'.");
- break;
- }
-
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- native_top = native_top->get_parent_class();
- }
- }
-#endif
-
- uint32_t flags = mono_method_get_flags(method->mono_method, nullptr);
-
- if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) {
- continue;
- }
-
- // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute
-
- GDMonoClass *top = p_native_base;
-
- while (top) {
- GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count());
-
- if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) {
- // Found base method with GodotMethod attribute.
- // We get the original API method name from this attribute.
- // This name must point to the virtual method.
-
- MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute));
-
- StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr);
-#ifdef DEBUG_ENABLED
- CRASH_COND(godot_method_name == StringName());
-#endif
- MethodKey key = MethodKey(godot_method_name, method->get_parameters_count());
- GDMonoMethod **existing_method = methods.getptr(key);
- if (existing_method) {
- memdelete(*existing_method); // Must delete old one
- }
- methods.insert(key, method);
-
- break;
- }
-
- if (top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- top = top->get_parent_class();
- }
- }
-
- methods_fetched = true;
-}
-
-GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
- ERR_FAIL_COND_V(!methods_fetched, nullptr);
-
- for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
- if (E.key.name == p_name) {
- return E.value;
- }
- }
-
- return nullptr;
-}
-
-bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
- return get_fetched_method_unknown_params(p_name) != nullptr;
-}
-
-bool GDMonoClass::implements_interface(GDMonoClass *p_interface) {
- return mono_class_implements_interface(mono_class, p_interface->get_mono_ptr());
-}
-
-bool GDMonoClass::has_public_parameterless_ctor() {
- GDMonoMethod *ctor = get_method(".ctor", 0);
- return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
-}
-
-GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) {
- MethodKey key = MethodKey(p_name, p_params_count);
-
- GDMonoMethod **match = methods.getptr(key);
-
- if (match) {
- return *match;
- }
-
- if (methods_fetched) {
- return nullptr;
- }
-
- MonoMethod *raw_method = mono_class_get_method_from_name(mono_class, String(p_name).utf8().get_data(), p_params_count);
-
- if (raw_method) {
- GDMonoMethod *method = memnew(GDMonoMethod(p_name, raw_method));
- methods.insert(key, method);
-
- return method;
- }
-
- return nullptr;
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
- MonoMethodSignature *sig = mono_method_signature(p_raw_method);
-
- int params_count = mono_signature_get_param_count(sig);
- StringName method_name = String::utf8(mono_method_get_name(p_raw_method));
-
- return get_method(p_raw_method, method_name, params_count);
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name) {
- MonoMethodSignature *sig = mono_method_signature(p_raw_method);
- int params_count = mono_signature_get_param_count(sig);
- return get_method(p_raw_method, p_name, params_count);
-}
-
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) {
- ERR_FAIL_NULL_V(p_raw_method, nullptr);
-
- MethodKey key = MethodKey(p_name, p_params_count);
-
- GDMonoMethod **match = methods.getptr(key);
-
- if (match) {
- return *match;
- }
-
- GDMonoMethod *method = memnew(GDMonoMethod(p_name, p_raw_method));
- methods.insert(key, method);
-
- return method;
-}
-
-GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, bool p_include_namespace) {
- MonoMethodDesc *desc = mono_method_desc_new(p_description.utf8().get_data(), p_include_namespace);
- MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
- mono_method_desc_free(desc);
-
- if (!method) {
- return nullptr;
- }
-
- ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, nullptr);
-
- return get_method(method);
-}
-
-GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
- HashMap<StringName, GDMonoField *>::Iterator result = fields.find(p_name);
-
- if (result) {
- return result->value;
- }
-
- if (fields_fetched) {
- return nullptr;
- }
-
- MonoClassField *raw_field = mono_class_get_field_from_name(mono_class, String(p_name).utf8().get_data());
-
- if (raw_field) {
- GDMonoField *field = memnew(GDMonoField(raw_field, this));
- fields.insert(p_name, field);
-
- return field;
- }
-
- return nullptr;
-}
-
-const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
- if (fields_fetched) {
- return fields_list;
- }
-
- void *iter = nullptr;
- MonoClassField *raw_field = nullptr;
- while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
- StringName name = String::utf8(mono_field_get_name(raw_field));
-
- HashMap<StringName, GDMonoField *>::Iterator match = fields.find(name);
-
- if (match) {
- fields_list.push_back(match->value);
- } else {
- GDMonoField *field = memnew(GDMonoField(raw_field, this));
- fields.insert(name, field);
- fields_list.push_back(field);
- }
- }
-
- fields_fetched = true;
-
- return fields_list;
-}
-
-GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
- HashMap<StringName, GDMonoProperty *>::Iterator result = properties.find(p_name);
-
- if (result) {
- return result->value;
- }
-
- if (properties_fetched) {
- return nullptr;
- }
-
- MonoProperty *raw_property = mono_class_get_property_from_name(mono_class, String(p_name).utf8().get_data());
-
- if (raw_property) {
- GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
- properties.insert(p_name, property);
-
- return property;
- }
-
- return nullptr;
-}
-
-const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
- if (properties_fetched) {
- return properties_list;
- }
-
- void *iter = nullptr;
- MonoProperty *raw_property = nullptr;
- while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
- StringName name = String::utf8(mono_property_get_name(raw_property));
-
- HashMap<StringName, GDMonoProperty *>::Iterator match = properties.find(name);
-
- if (match) {
- properties_list.push_back(match->value);
- } else {
- GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
- properties.insert(name, property);
- properties_list.push_back(property);
- }
- }
-
- properties_fetched = true;
-
- return properties_list;
-}
-
-const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
- if (delegates_fetched) {
- return delegates_list;
- }
-
- // If the class is generic we must use the generic type definition.
- MonoClass *klass = mono_class;
- if (mono_type_get_type(get_mono_type()) == MONO_TYPE_GENERICINST) {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), get_mono_type());
- GDMonoUtils::Marshal::get_generic_type_definition(reftype, &reftype);
- MonoType *type = mono_reflection_type_get_type(reftype);
- klass = mono_class_from_mono_type(type);
- }
-
- void *iter = nullptr;
- MonoClass *raw_class = nullptr;
- while ((raw_class = mono_class_get_nested_types(klass, &iter)) != nullptr) {
- if (mono_class_is_delegate(raw_class)) {
- StringName name = String::utf8(mono_class_get_name(raw_class));
-
- HashMap<StringName, GDMonoClass *>::Iterator match = delegates.find(name);
-
- if (match) {
- 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);
- delegates_list.push_back(delegate);
- }
- }
- }
-
- delegates_fetched = true;
-
- return delegates_list;
-}
-
-const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
- if (!method_list_fetched) {
- void *iter = nullptr;
- MonoMethod *raw_method = nullptr;
- while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- method_list.push_back(memnew(GDMonoMethod(String::utf8(mono_method_get_name(raw_method)), raw_method)));
- }
-
- method_list_fetched = true;
- }
-
- return method_list;
-}
-
-GDMonoClass::GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly) {
- namespace_name = p_namespace;
- class_name = p_name;
- mono_class = p_class;
- assembly = p_assembly;
-
- attrs_fetched = false;
- attributes = nullptr;
-
- methods_fetched = false;
- method_list_fetched = false;
- fields_fetched = false;
- properties_fetched = false;
- delegates_fetched = false;
-}
-
-GDMonoClass::~GDMonoClass() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-
- for (const KeyValue<StringName, GDMonoField *> &E : fields) {
- memdelete(E.value);
- }
-
- for (const KeyValue<StringName, GDMonoProperty *> &E : properties) {
- memdelete(E.value);
- }
-
- {
- // Ugly workaround...
- // We may have duplicated values, because we redirect snake_case methods to PascalCasel (only Godot API methods).
- // This way, we end with both the snake_case name and the PascalCasel name paired with the same method.
- // Therefore, we must avoid deleting the same pointer twice.
-
- int offset = 0;
- Vector<GDMonoMethod *> deleted_methods;
- deleted_methods.resize(methods.size());
-
- for (const KeyValue<MethodKey, GDMonoMethod *> &E : methods) {
- GDMonoMethod *method = E.value;
-
- if (method) {
- for (int i = 0; i < offset; i++) {
- if (deleted_methods[i] == method) {
- // Already deleted
- goto already_deleted;
- }
- }
-
- deleted_methods.write[offset] = method;
- ++offset;
-
- memdelete(method);
- }
-
- already_deleted:;
- }
-
- methods.clear();
- }
-
- for (int i = 0; i < method_list.size(); ++i) {
- memdelete(method_list[i]);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
deleted file mode 100644
index 6b35da30f9..0000000000
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*************************************************************************/
-/* gd_mono_class.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 GD_MONO_CLASS_H
-#define GD_MONO_CLASS_H
-
-#include "core/string/ustring.h"
-#include "core/templates/rb_map.h"
-
-#include "gd_mono_field.h"
-#include "gd_mono_header.h"
-#include "gd_mono_method.h"
-#include "gd_mono_property.h"
-#include "gd_mono_utils.h"
-
-class GDMonoClass {
- struct MethodKey {
- struct Hasher {
- static _FORCE_INLINE_ uint32_t hash(const MethodKey &p_key) {
- uint32_t hash = 0;
-
- GDMonoUtils::hash_combine(hash, p_key.name.hash());
- GDMonoUtils::hash_combine(hash, HashMapHasherDefault::hash(p_key.params_count));
-
- return hash;
- }
- };
-
- _FORCE_INLINE_ bool operator==(const MethodKey &p_a) const {
- return p_a.params_count == params_count && p_a.name == name;
- }
-
- MethodKey() {}
-
- MethodKey(const StringName &p_name, uint16_t p_params_count) :
- name(p_name), params_count(p_params_count) {
- }
-
- StringName name;
- uint16_t params_count = 0;
- };
-
- StringName namespace_name;
- StringName class_name;
-
- MonoClass *mono_class = nullptr;
- GDMonoAssembly *assembly = nullptr;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
- // This contains both the original method names and remapped method names from the native Godot identifiers to the C# functions.
- // Most method-related functions refer to this and it's possible this is unintuitive for outside users; this may be a prime location for refactoring or renaming.
- bool methods_fetched;
- HashMap<MethodKey, GDMonoMethod *, MethodKey::Hasher> methods;
-
- bool method_list_fetched;
- Vector<GDMonoMethod *> method_list;
-
- bool fields_fetched;
- HashMap<StringName, GDMonoField *> fields;
- Vector<GDMonoField *> fields_list;
-
- bool properties_fetched;
- HashMap<StringName, GDMonoProperty *> properties;
- Vector<GDMonoProperty *> properties_list;
-
- bool delegates_fetched;
- HashMap<StringName, GDMonoClass *> delegates;
- Vector<GDMonoClass *> delegates_list;
-
- friend class GDMonoAssembly;
- GDMonoClass(const StringName &p_namespace, const StringName &p_name, MonoClass *p_class, GDMonoAssembly *p_assembly);
-
-public:
- static String get_full_name(MonoClass *p_mono_class);
- static MonoType *get_mono_type(MonoClass *p_mono_class);
-
- String get_full_name() const;
- String get_type_desc() const;
- MonoType *get_mono_type() const;
-
- uint32_t get_flags() const;
- bool is_static() const;
-
- bool is_assignable_from(GDMonoClass *p_from) const;
-
- StringName get_namespace() const;
- _FORCE_INLINE_ StringName get_name() const { return class_name; }
- String get_name_for_lookup() const;
-
- _FORCE_INLINE_ MonoClass *get_mono_ptr() const { return mono_class; }
- _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
-
- GDMonoClass *get_parent_class() const;
- GDMonoClass *get_nesting_class() const;
-
-#ifdef TOOLS_ENABLED
- Vector<MonoClassField *> get_enum_fields();
-#endif
-
- GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name);
- bool has_fetched_method_unknown_params(const StringName &p_name);
-
- bool has_attribute(GDMonoClass *p_attr_class);
- MonoObject *get_attribute(GDMonoClass *p_attr_class);
-
- void fetch_attributes();
- void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
-
- bool implements_interface(GDMonoClass *p_interface);
- bool has_public_parameterless_ctor();
-
- GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0);
- GDMonoMethod *get_method(MonoMethod *p_raw_method);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count);
- GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
-
- GDMonoField *get_field(const StringName &p_name);
- const Vector<GDMonoField *> &get_all_fields();
-
- GDMonoProperty *get_property(const StringName &p_name);
- const Vector<GDMonoProperty *> &get_all_properties();
-
- const Vector<GDMonoClass *> &get_all_delegates();
-
- const Vector<GDMonoMethod *> &get_all_methods();
-
- ~GDMonoClass();
-};
-
-#endif // GD_MONO_CLASS_H
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
deleted file mode 100644
index cb025fc67a..0000000000
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ /dev/null
@@ -1,556 +0,0 @@
-/*************************************************************************/
-/* gd_mono_field.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 "gd_mono_field.h"
-
-#include <mono/metadata/attrdefs.h>
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-void GDMonoField::set_value(MonoObject *p_object, MonoObject *p_value) {
- mono_field_set_value(p_object, mono_field, p_value);
-}
-
-void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
- mono_field_set_value(p_object, mono_field, &p_ptr);
-}
-
-void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) {
- switch (type.type_encoding) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_value.operator bool();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_CHAR: {
- int16_t val = p_value.operator unsigned short();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_I1: {
- int8_t val = p_value.operator signed char();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_I2: {
- int16_t val = p_value.operator signed short();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_I4: {
- int32_t val = p_value.operator signed int();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_I8: {
- int64_t val = p_value.operator int64_t();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_U1: {
- uint8_t val = p_value.operator unsigned char();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_U2: {
- uint16_t val = p_value.operator unsigned short();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_U4: {
- uint32_t val = p_value.operator unsigned int();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_U8: {
- uint64_t val = p_value.operator uint64_t();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_R4: {
- float val = p_value.operator float();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_R8: {
- double val = p_value.operator double();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *tclass = type.type_class;
-
- if (tclass == CACHED_CLASS(Vector2)) {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Vector2i)) {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Rect2)) {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Rect2i)) {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Transform2D)) {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Vector3)) {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Vector3i)) {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
- mono_field_set_value(p_object, mono_field, &from);
- 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);
- break;
- }
-
- if (tclass == CACHED_CLASS(Quaternion)) {
- GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Transform3D)) {
- GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D());
- mono_field_set_value(p_object, mono_field, &from);
- 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);
- break;
- }
-
- if (tclass == CACHED_CLASS(Color)) {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Plane)) {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
- mono_field_set_value(p_object, mono_field, &from);
- break;
- }
-
- if (tclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
-
- if (tclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
-
- if (mono_class_is_enum(tclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(tclass->get_mono_ptr());
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_value.operator bool();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_value.operator unsigned short();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_I1: {
- int8_t val = p_value.operator signed char();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_I2: {
- int16_t val = p_value.operator signed short();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_I4: {
- int32_t val = p_value.operator signed int();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_I8: {
- int64_t val = p_value.operator int64_t();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_value.operator unsigned char();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_value.operator unsigned short();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_value.operator unsigned int();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_value.operator uint64_t();
- mono_field_set_value(p_object, mono_field, &val);
- break;
- }
- default: {
- ERR_FAIL_MSG("Attempted to convert Variant to a managed enum value of unmarshallable base type.");
- }
- }
-
- break;
- }
-
- ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'.");
- } break;
- case MONO_TYPE_STRING: {
- if (p_value.get_type() == Variant::NIL) {
- // Otherwise, Variant -> String would return the string "Null"
- MonoString *mono_string = nullptr;
- mono_field_set_value(p_object, mono_field, mono_string);
- } else {
- MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
- mono_field_set_value(p_object, mono_field, mono_string);
- }
- } break;
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class);
- if (likely(managed != nullptr)) {
- mono_field_set_value(p_object, mono_field, managed);
- }
- } break;
- case MONO_TYPE_CLASS: {
- MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class);
- if (likely(managed != nullptr)) {
- mono_field_set_value(p_object, mono_field, managed);
- }
- } break;
- case MONO_TYPE_GENERICINST: {
- MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class);
- if (likely(managed != nullptr)) {
- mono_field_set_value(p_object, mono_field, managed);
- }
- } break;
- case MONO_TYPE_OBJECT: {
- // Variant
- switch (p_value.get_type()) {
- case Variant::BOOL: {
- MonoBoolean val = p_value.operator bool();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case Variant::INT: {
- int32_t val = p_value.operator signed int();
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case Variant::FLOAT: {
-#ifdef REAL_T_IS_DOUBLE
- double val = p_value.operator double();
- mono_field_set_value(p_object, mono_field, &val);
-#else
- float val = p_value.operator float();
- mono_field_set_value(p_object, mono_field, &val);
-#endif
- } break;
- case Variant::STRING: {
- MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
- mono_field_set_value(p_object, mono_field, mono_string);
- } break;
- case Variant::VECTOR2: {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::VECTOR2I: {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::RECT2: {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::RECT2I: {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::VECTOR3: {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::VECTOR3I: {
- 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);
- } break;
- case Variant::PLANE: {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::QUATERNION: {
- GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::AABB: {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::BASIS: {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
- mono_field_set_value(p_object, mono_field, &from);
- } break;
- case Variant::TRANSFORM3D: {
- 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);
- } break;
- case Variant::STRING_NAME: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::NODE_PATH: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::RID: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::OBJECT: {
- MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::CALLABLE: {
- GDMonoMarshal::M_Callable val = GDMonoMarshal::callable_to_managed(p_value.operator Callable());
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case Variant::SIGNAL: {
- GDMonoMarshal::M_SignalInfo val = GDMonoMarshal::signal_info_to_managed(p_value.operator Signal());
- mono_field_set_value(p_object, mono_field, &val);
- } break;
- case Variant::DICTIONARY: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::ARRAY: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_INT32_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_INT64_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_FLOAT64_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_STRING_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_VECTOR2_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_VECTOR3_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- case Variant::PACKED_COLOR_ARRAY: {
- MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray());
- mono_field_set_value(p_object, mono_field, managed);
- } break;
- default:
- break;
- }
- } break;
- default: {
- ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + ".");
- } break;
- }
-}
-
-MonoObject *GDMonoField::get_value(MonoObject *p_object) {
- return mono_field_get_value_object(mono_domain_get(), mono_field, p_object);
-}
-
-bool GDMonoField::get_bool_value(MonoObject *p_object) {
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
-}
-
-int GDMonoField::get_int_value(MonoObject *p_object) {
- return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
-}
-
-String GDMonoField::get_string_value(MonoObject *p_object) {
- MonoObject *val = get_value(p_object);
- return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
-}
-
-bool GDMonoField::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoField::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoField::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_field(owner->get_mono_ptr(), mono_field);
- attrs_fetched = true;
-}
-
-bool GDMonoField::is_static() {
- return mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoField::get_visibility() {
- switch (mono_field_get_flags(mono_field) & MONO_FIELD_ATTR_FIELD_ACCESS_MASK) {
- case MONO_FIELD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_FIELD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_FIELD_ATTR_ASSEMBLY:
- return IMonoClassMember::INTERNAL;
- case MONO_FIELD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_FIELD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) {
- owner = p_owner;
- mono_field = p_mono_field;
- name = String::utf8(mono_field_get_name(mono_field));
- MonoType *field_type = mono_field_get_type(mono_field);
- type.type_encoding = mono_type_get_type(field_type);
- MonoClass *field_type_class = mono_class_from_mono_type(field_type);
- type.type_class = GDMono::get_singleton()->get_class(field_type_class);
-
- attrs_fetched = false;
- attributes = nullptr;
-}
-
-GDMonoField::~GDMonoField() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
deleted file mode 100644
index 1d30f7a369..0000000000
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*************************************************************************/
-/* gd_mono_field.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 GD_MONO_FIELD_H
-#define GD_MONO_FIELD_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoField : public IMonoClassMember {
- GDMonoClass *owner = nullptr;
- MonoClassField *mono_field = nullptr;
-
- StringName name;
- ManagedType type;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final { return owner; }
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_FIELD; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- _FORCE_INLINE_ ManagedType get_type() const { return type; }
-
- void set_value(MonoObject *p_object, MonoObject *p_value);
- void set_value_raw(MonoObject *p_object, void *p_ptr);
- void set_value_from_variant(MonoObject *p_object, const Variant &p_value);
-
- MonoObject *get_value(MonoObject *p_object);
-
- bool get_bool_value(MonoObject *p_object);
- int get_int_value(MonoObject *p_object);
- String get_string_value(MonoObject *p_object);
-
- GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner);
- ~GDMonoField();
-};
-
-#endif // GD_MONO_FIELD_H
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
deleted file mode 100644
index d206b0dfc3..0000000000
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*************************************************************************/
-/* gd_mono_internals.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 "gd_mono_internals.h"
-
-#include "../csharp_script.h"
-#include "../mono_gc_handle.h"
-#include "../utils/macros.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-#include "core/debugger/engine_debugger.h"
-#include "core/debugger/script_debugger.h"
-
-#include <mono/metadata/exception.h>
-
-namespace GDMonoInternals {
-void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
- // This method should not fail
-
- CRASH_COND(!unmanaged);
-
- // All mono objects created from the managed world (e.g.: 'new Player()')
- // need to have a CSharpScript in order for their methods to be callable from the unmanaged side
-
- RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
-
- GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
-
- CRASH_COND(!klass);
-
- GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);
-
- CRASH_COND(native == nullptr);
-
- if (native == klass) {
- // If it's just a wrapper Godot class and not a custom inheriting class, then attach a
- // script binding instead. One of the advantages of this is that if a script is attached
- // later and it's not a C# script, then the managed object won't have to be disposed.
- // Another reason for doing this is that this instance could outlive CSharpLanguage, which would
- // be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621
-
- CSharpScriptBinding script_binding;
-
- script_binding.inited = true;
- script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
- script_binding.wrapper_class = klass;
- script_binding.gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
- script_binding.owner = unmanaged;
-
- if (rc) {
- // Unsafe refcount increment. The managed instance also counts as a reference.
- // This way if the unmanaged world has no references to our owner
- // but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
-
- // May not me referenced yet, so we must use init_ref() instead of reference()
- if (rc->init_ref()) {
- CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
- }
- }
-
- // The object was just created, no script instance binding should have been attached
- CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged));
-
- void *data;
- {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
- }
-
- // Should be thread safe because the object was just created and nothing else should be referencing it
- CSharpLanguage::set_instance_binding(unmanaged, data);
-
- return;
- }
-
- MonoGCHandleData gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
-
- Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
-
- CRASH_COND(script.is_null());
-
- CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
-
- unmanaged->set_script_and_instance(script, csharp_instance);
-}
-
-void unhandled_exception(MonoException *p_exc) {
- mono_print_unhandled_exception((MonoObject *)p_exc);
- gd_unhandled_exception_event(p_exc);
-
- if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
- // Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
- mono_unhandled_exception((MonoObject *)p_exc);
- GDMono::unhandled_exception_hook((MonoObject *)p_exc, nullptr);
- GD_UNREACHABLE();
- } else {
-#ifdef DEBUG_ENABLED
- GDMonoUtils::debug_send_unhandled_exception_error(p_exc);
- if (EngineDebugger::is_active()) {
- EngineDebugger::get_singleton()->poll_events(false);
- }
-#endif
- }
-}
-
-void gd_unhandled_exception_event(MonoException *p_exc) {
- MonoImage *mono_image = GDMono::get_singleton()->get_core_api_assembly()->get_image();
-
- MonoClass *gd_klass = mono_class_from_name(mono_image, "Godot", "GD");
- MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", -1);
- void *args[1];
- args[0] = p_exc;
- mono_runtime_invoke(unhandled_exception_method, nullptr, (void **)args, nullptr);
-}
-} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
deleted file mode 100644
index 6ea3c5539e..0000000000
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*************************************************************************/
-/* gd_mono_log.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 "gd_mono_log.h"
-
-#include <stdlib.h> // abort
-
-#include "core/io/dir_access.h"
-#include "core/os/os.h"
-
-#include "../godotsharp_dirs.h"
-#include "../utils/string_utils.h"
-
-static CharString get_default_log_level() {
-#ifdef DEBUG_ENABLED
- return String("info").utf8();
-#else
- return String("warning").utf8();
-#endif
-}
-
-GDMonoLog *GDMonoLog::singleton = nullptr;
-
-#ifdef GD_MONO_LOG_ENABLED
-
-static int get_log_level_id(const char *p_log_level) {
- const char *valid_log_levels[] = { "error", "critical", "warning", "message", "info", "debug", nullptr };
-
- int i = 0;
- while (valid_log_levels[i]) {
- if (!strcmp(valid_log_levels[i], p_log_level)) {
- return i;
- }
- i++;
- }
-
- return -1;
-}
-
-static String make_text(const char *log_domain, const char *log_level, const char *message) {
- String text(message);
- text += " (in domain ";
- text += log_domain;
- if (log_level) {
- text += ", ";
- text += log_level;
- }
- text += ")";
- return text;
-}
-
-void GDMonoLog::mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *) {
- if (GDMonoLog::get_singleton()->log_level_id >= get_log_level_id(log_level)) {
- String text = make_text(log_domain, log_level, message);
- text += "\n";
-
- GDMonoLog::get_singleton()->log_file->seek_end();
- GDMonoLog::get_singleton()->log_file->store_string(text);
- }
-
- if (fatal) {
- String text = make_text(log_domain, log_level, message);
- ERR_PRINT("Mono: FATAL ERROR '" + text + "', ABORTING! Logfile: '" + GDMonoLog::get_singleton()->log_file_path + "'.");
- // Make sure to flush before aborting
- GDMonoLog::get_singleton()->log_file->flush();
- GDMonoLog::get_singleton()->log_file.unref();
-
- abort();
- }
-}
-
-bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) {
- if (!DirAccess::exists(p_logs_dir)) {
- Ref<DirAccess> diraccess = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V(diraccess.is_null(), false);
- Error logs_mkdir_err = diraccess->make_dir_recursive(p_logs_dir);
- ERR_FAIL_COND_V_MSG(logs_mkdir_err != OK, false, "Failed to create mono logs directory.");
- }
-
- return true;
-}
-
-void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
- static const uint64_t MAX_SECS = 5 * 86400; // 5 days
-
- Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND(da.is_null());
-
- Error err = da->change_dir(p_logs_dir);
- ERR_FAIL_COND_MSG(err != OK, "Cannot change directory to '" + p_logs_dir + "'.");
-
- ERR_FAIL_COND(da->list_dir_begin() != OK);
-
- String current = da->get_next();
- while (!current.is_empty()) {
- if (da->current_is_dir() || !current.ends_with(".txt")) {
- current = da->get_next();
- continue;
- }
-
- uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current));
-
- if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) {
- da->remove(current);
- }
- current = da->get_next();
- }
-
- da->list_dir_end();
-}
-
-void GDMonoLog::initialize() {
- CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8();
-
- if (log_level.length() != 0 && get_log_level_id(log_level.get_data()) == -1) {
- ERR_PRINT(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): '" + log_level.get_data() + "'.");
- log_level = CharString();
- }
-
- if (log_level.length() == 0) {
- log_level = get_default_log_level();
- }
-
- String logs_dir = GodotSharpDirs::get_mono_logs_dir();
-
- if (_try_create_logs_dir(logs_dir)) {
- _delete_old_log_files(logs_dir);
-
- OS::Date date_now = OS::get_singleton()->get_date();
- OS::Time time_now = OS::get_singleton()->get_time();
-
- String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d",
- (int)date_now.year, (int)date_now.month, (int)date_now.day,
- (int)time_now.hour, (int)time_now.minute, (int)time_now.second);
-
- log_file_name += str_format("_%d", OS::get_singleton()->get_process_id());
-
- log_file_name += ".log";
-
- log_file_path = logs_dir.plus_file(log_file_name);
-
- log_file = FileAccess::open(log_file_path, FileAccess::WRITE);
- if (log_file.is_null()) {
- ERR_PRINT("Mono: Cannot create log file at: " + log_file_path);
- }
- }
-
- mono_trace_set_level_string(log_level.get_data());
- log_level_id = get_log_level_id(log_level.get_data());
-
- if (log_file.is_valid()) {
- OS::get_singleton()->print("Mono: Log file is: '%s'\n", log_file_path.utf8().get_data());
- mono_trace_set_log_handler(mono_log_callback, this);
- } else {
- OS::get_singleton()->printerr("Mono: No log file, using default log handler\n");
- }
-}
-
-GDMonoLog::GDMonoLog() {
- singleton = this;
-}
-
-GDMonoLog::~GDMonoLog() {
- singleton = nullptr;
-}
-
-#else
-
-void GDMonoLog::initialize() {
- CharString log_level = get_default_log_level();
- mono_trace_set_level_string(log_level.get_data());
-}
-
-GDMonoLog::GDMonoLog() {
- singleton = this;
-}
-
-GDMonoLog::~GDMonoLog() {
- singleton = nullptr;
-}
-
-#endif // !defined(JAVASCRIPT_ENABLED)
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
deleted file mode 100644
index 93ba6a410e..0000000000
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*************************************************************************/
-/* gd_mono_log.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 GD_MONO_LOG_H
-#define GD_MONO_LOG_H
-
-#include <mono/utils/mono-logger.h>
-
-#include "core/typedefs.h"
-
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
-// We have custom mono log callbacks for WASM and iOS
-#define GD_MONO_LOG_ENABLED
-#endif
-
-#ifdef GD_MONO_LOG_ENABLED
-#include "core/io/file_access.h"
-#endif
-
-class GDMonoLog {
-#ifdef GD_MONO_LOG_ENABLED
- int log_level_id = -1;
-
- Ref<FileAccess> log_file;
- String log_file_path;
-
- bool _try_create_logs_dir(const String &p_logs_dir);
- void _delete_old_log_files(const String &p_logs_dir);
-
- static void mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data);
-#endif
-
- static GDMonoLog *singleton;
-
-public:
- _FORCE_INLINE_ static GDMonoLog *get_singleton() { return singleton; }
-
- void initialize();
-
- GDMonoLog();
- ~GDMonoLog();
-};
-
-#endif // GD_MONO_LOG_H
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
deleted file mode 100644
index a860442764..0000000000
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ /dev/null
@@ -1,1824 +0,0 @@
-/*************************************************************************/
-/* gd_mono_marshal.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 "gd_mono_marshal.h"
-
-#include "../signal_awaiter_utils.h"
-#include "gd_mono.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-
-namespace GDMonoMarshal {
-
-Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant) {
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return Variant::BOOL;
-
- case MONO_TYPE_I1:
- return Variant::INT;
- case MONO_TYPE_I2:
- return Variant::INT;
- case MONO_TYPE_I4:
- return Variant::INT;
- case MONO_TYPE_I8:
- return Variant::INT;
-
- case MONO_TYPE_U1:
- return Variant::INT;
- case MONO_TYPE_U2:
- return Variant::INT;
- case MONO_TYPE_U4:
- return Variant::INT;
- case MONO_TYPE_U8:
- return Variant::INT;
-
- case MONO_TYPE_R4:
- return Variant::FLOAT;
- case MONO_TYPE_R8:
- return Variant::FLOAT;
-
- case MONO_TYPE_STRING: {
- return Variant::STRING;
- } break;
-
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
- if (vtclass == CACHED_CLASS(Vector2)) {
- return Variant::VECTOR2;
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- return Variant::VECTOR2I;
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- return Variant::RECT2;
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- return Variant::RECT2I;
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- return Variant::TRANSFORM2D;
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- return Variant::VECTOR3;
- }
-
- 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;
- }
-
- if (vtclass == CACHED_CLASS(Quaternion)) {
- return Variant::QUATERNION;
- }
-
- if (vtclass == CACHED_CLASS(Transform3D)) {
- return Variant::TRANSFORM3D;
- }
- if (vtclass == CACHED_CLASS(Projection)) {
- return Variant::PROJECTION;
- }
- if (vtclass == CACHED_CLASS(AABB)) {
- return Variant::AABB;
- }
-
- if (vtclass == CACHED_CLASS(Color)) {
- return Variant::COLOR;
- }
-
- if (vtclass == CACHED_CLASS(Plane)) {
- return Variant::PLANE;
- }
-
- if (vtclass == CACHED_CLASS(Callable)) {
- return Variant::CALLABLE;
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- return Variant::SIGNAL;
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- return Variant::INT;
- }
- } break;
-
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr());
-
- if (elem_class == CACHED_CLASS_RAW(MonoObject)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(uint8_t)) {
- return Variant::PACKED_BYTE_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(int32_t)) {
- return Variant::PACKED_INT32_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(int64_t)) {
- return Variant::PACKED_INT64_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(float)) {
- return Variant::PACKED_FLOAT32_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(double)) {
- return Variant::PACKED_FLOAT64_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(String)) {
- return Variant::PACKED_STRING_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Vector2)) {
- return Variant::PACKED_VECTOR2_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Vector3)) {
- return Variant::PACKED_VECTOR3_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(Color)) {
- return Variant::PACKED_COLOR_ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(StringName)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(NodePath)) {
- return Variant::ARRAY;
- }
-
- if (elem_class == CACHED_CLASS_RAW(RID)) {
- return Variant::ARRAY;
- }
-
- if (mono_class_is_enum(elem_class)) {
- return Variant::ARRAY;
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return Variant::ARRAY;
- }
- } break;
-
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- return Variant::OBJECT;
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- return Variant::STRING_NAME;
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- return Variant::NODE_PATH;
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- return Variant::RID;
- }
-
- if (CACHED_CLASS(Dictionary) == type_class) {
- return Variant::DICTIONARY;
- }
-
- if (CACHED_CLASS(Array) == type_class) {
- return Variant::ARRAY;
- }
-
- // IDictionary
- if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) {
- return Variant::DICTIONARY;
- }
-
- // ICollection or IEnumerable
- if (p_type.type_class == CACHED_CLASS(System_Collections_ICollection) ||
- p_type.type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
- return Variant::ARRAY;
- }
- } break;
-
- case MONO_TYPE_OBJECT: {
- if (r_nil_is_variant) {
- *r_nil_is_variant = true;
- }
- return Variant::NIL;
- } break;
-
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return Variant::ARRAY;
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- return Variant::ARRAY;
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- return Variant::DICTIONARY;
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- return Variant::ARRAY;
- }
-
- // GodotObject
- GDMonoClass *type_class = p_type.type_class;
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- return Variant::OBJECT;
- }
- } break;
-
- default: {
- } break;
- }
-
- if (r_nil_is_variant) {
- *r_nil_is_variant = false;
- }
-
- // Unknown
- return Variant::NIL;
-}
-
-bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
- switch (p_array_type.type_encoding) {
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr());
- r_elem_type = ManagedType::from_class(elem_class);
- return true;
- } break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
-
- if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype) ||
- GDMonoUtils::Marshal::type_is_system_generic_list(array_reftype) ||
- GDMonoUtils::Marshal::type_is_generic_icollection(array_reftype) ||
- GDMonoUtils::Marshal::type_is_generic_ienumerable(array_reftype)) {
- MonoReflectionType *elem_reftype;
-
- GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype);
-
- r_elem_type = ManagedType::from_reftype(elem_reftype);
- return true;
- }
- } break;
- default: {
- } break;
- }
-
- return false;
-}
-
-MonoString *variant_to_mono_string(const Variant &p_var) {
- if (p_var.get_type() == Variant::NIL) {
- return nullptr; // Otherwise, Variant -> String would return the string "Null"
- }
- return mono_string_from_godot(p_var.operator String());
-}
-
-MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) {
- MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
- return Array_to_mono_array(p_var.operator Array());
- }
-
- if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) {
- return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + GDMonoClass::get_full_name(array_type->eklass) + "'.");
-}
-
-MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) {
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) {
- return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
- }
-
- if (CACHED_CLASS(StringName) == p_type_class) {
- return GDMonoUtils::create_managed_from(p_var.operator StringName());
- }
-
- if (CACHED_CLASS(NodePath) == p_type_class) {
- return GDMonoUtils::create_managed_from(p_var.operator NodePath());
- }
-
- if (CACHED_CLASS(RID) == p_type_class) {
- return GDMonoUtils::create_managed_from(p_var.operator ::RID());
- }
-
- // Godot.Collections.Dictionary or IDictionary
- if (CACHED_CLASS(Dictionary) == p_type_class || CACHED_CLASS(System_Collections_IDictionary) == p_type_class) {
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
- }
-
- // Godot.Collections.Array or ICollection or IEnumerable
- if (CACHED_CLASS(Array) == p_type_class ||
- CACHED_CLASS(System_Collections_ICollection) == p_type_class ||
- CACHED_CLASS(System_Collections_IEnumerable) == p_type_class) {
- return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + p_type_class->get_full_name() + "'.");
-}
-
-MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype);
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class);
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class);
- }
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) {
- return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + p_type_class->get_full_name() + "'.");
-}
-
-MonoObject *variant_to_mono_object(const Variant &p_var) {
- // Variant
- switch (p_var.get_type()) {
- case Variant::BOOL: {
- MonoBoolean val = p_var.operator bool();
- return BOX_BOOLEAN(val);
- }
- case Variant::INT: {
- int64_t val = p_var.operator int64_t();
- return BOX_INT64(val);
- }
- case Variant::FLOAT: {
-#ifdef REAL_T_IS_DOUBLE
- double val = p_var.operator double();
- return BOX_DOUBLE(val);
-#else
- float val = p_var.operator float();
- return BOX_FLOAT(val);
-#endif
- }
- case Variant::STRING:
- return (MonoObject *)mono_string_from_godot(p_var.operator String());
- case Variant::VECTOR2: {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
- case Variant::VECTOR2I: {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
- case Variant::RECT2: {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
- case Variant::RECT2I: {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
- case Variant::VECTOR3: {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
- case Variant::VECTOR3I: {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
- case Variant::TRANSFORM2D: {
- 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);
- }
- case Variant::QUATERNION: {
- GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_var.operator ::Quaternion());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quaternion), &from);
- }
- case Variant::AABB: {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
- case Variant::BASIS: {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
- case Variant::TRANSFORM3D: {
- 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);
- }
- case Variant::STRING_NAME:
- return GDMonoUtils::create_managed_from(p_var.operator StringName());
- case Variant::NODE_PATH:
- return GDMonoUtils::create_managed_from(p_var.operator NodePath());
- case Variant::RID:
- return GDMonoUtils::create_managed_from(p_var.operator ::RID());
- case Variant::OBJECT:
- return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
- case Variant::CALLABLE: {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
- case Variant::SIGNAL: {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
- case Variant::DICTIONARY:
- return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
- case Variant::ARRAY:
- return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
- case Variant::PACKED_BYTE_ARRAY:
- return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
- case Variant::PACKED_INT32_ARRAY:
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
- case Variant::PACKED_INT64_ARRAY:
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
- case Variant::PACKED_FLOAT32_ARRAY:
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
- case Variant::PACKED_FLOAT64_ARRAY:
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
- case Variant::PACKED_STRING_ARRAY:
- return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
- case Variant::PACKED_VECTOR2_ARRAY:
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
- case Variant::PACKED_VECTOR3_ARRAY:
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
- case Variant::PACKED_COLOR_ARRAY:
- return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
- default:
- return nullptr;
- }
-}
-
-size_t variant_get_managed_unboxed_size(const ManagedType &p_type) {
- // This method prints no errors for unsupported types. It's called on all methods, not only
- // those that end up being invoked with Variant parameters.
-
- // For MonoObject* we return 0, as it doesn't need to be stored.
- constexpr size_t zero_for_mono_object = 0;
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return sizeof(MonoBoolean);
- case MONO_TYPE_CHAR:
- return sizeof(uint16_t);
- case MONO_TYPE_I1:
- return sizeof(int8_t);
- case MONO_TYPE_I2:
- return sizeof(int16_t);
- case MONO_TYPE_I4:
- return sizeof(int32_t);
- case MONO_TYPE_I8:
- return sizeof(int64_t);
- case MONO_TYPE_U1:
- return sizeof(uint8_t);
- case MONO_TYPE_U2:
- return sizeof(uint16_t);
- case MONO_TYPE_U4:
- return sizeof(uint32_t);
- case MONO_TYPE_U8:
- return sizeof(uint64_t);
- case MONO_TYPE_R4:
- return sizeof(float);
- case MONO_TYPE_R8:
- return sizeof(double);
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- return sizeof(M_##m_struct); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
- RETURN_CHECK_FOR_STRUCT(Callable);
- RETURN_CHECK_FOR_STRUCT(SignalInfo);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN:
- return sizeof(MonoBoolean);
- case MONO_TYPE_CHAR:
- return sizeof(uint16_t);
- case MONO_TYPE_I1:
- return sizeof(int8_t);
- case MONO_TYPE_I2:
- return sizeof(int16_t);
- case MONO_TYPE_I4:
- return sizeof(int32_t);
- case MONO_TYPE_I8:
- return sizeof(int64_t);
- case MONO_TYPE_U1:
- return sizeof(uint8_t);
- case MONO_TYPE_U2:
- return sizeof(uint16_t);
- case MONO_TYPE_U4:
- return sizeof(uint32_t);
- case MONO_TYPE_U8:
- return sizeof(uint64_t);
- default: {
- // Enum with unsupported base type. We return nullptr MonoObject* on error.
- return zero_for_mono_object;
- }
- }
- }
-
- // Enum with unsupported value type. We return nullptr MonoObject* on error.
- } break;
- case MONO_TYPE_STRING:
- return zero_for_mono_object;
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_GENERICINST:
- return zero_for_mono_object;
- case MONO_TYPE_OBJECT:
- return zero_for_mono_object;
- }
-
- // Unsupported type encoding. We return nullptr MonoObject* on error.
- return zero_for_mono_object;
-}
-
-void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
-#define RETURN_TYPE_VAL(m_type, m_val) \
- *reinterpret_cast<m_type *>(r_buffer) = m_val; \
- r_offset += sizeof(m_type); \
- return r_buffer;
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool());
- case MONO_TYPE_CHAR:
- RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
- case MONO_TYPE_I1:
- RETURN_TYPE_VAL(int8_t, p_var.operator signed char());
- case MONO_TYPE_I2:
- RETURN_TYPE_VAL(int16_t, p_var.operator signed short());
- case MONO_TYPE_I4:
- RETURN_TYPE_VAL(int32_t, p_var.operator signed int());
- case MONO_TYPE_I8:
- RETURN_TYPE_VAL(int64_t, p_var.operator int64_t());
- case MONO_TYPE_U1:
- RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char());
- case MONO_TYPE_U2:
- RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
- case MONO_TYPE_U4:
- RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int());
- case MONO_TYPE_U8:
- RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t());
- case MONO_TYPE_R4:
- RETURN_TYPE_VAL(float, p_var.operator float());
- case MONO_TYPE_R8:
- RETURN_TYPE_VAL(double, p_var.operator double());
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
- RETURN_TYPE_VAL(M_##m_struct, from); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- RETURN_TYPE_VAL(M_Callable, from);
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- RETURN_TYPE_VAL(M_SignalInfo, from);
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- RETURN_TYPE_VAL(MonoBoolean, val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- RETURN_TYPE_VAL(uint16_t, val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- RETURN_TYPE_VAL(int8_t, val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- RETURN_TYPE_VAL(int16_t, val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- RETURN_TYPE_VAL(int32_t, val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- RETURN_TYPE_VAL(int64_t, val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- RETURN_TYPE_VAL(uint8_t, val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- RETURN_TYPE_VAL(uint16_t, val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- RETURN_TYPE_VAL(uint32_t, val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- RETURN_TYPE_VAL(uint64_t, val);
- }
- default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'.");
- }
- }
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
- } break;
-#undef RETURN_TYPE_VAL
- case MONO_TYPE_STRING:
- return variant_to_mono_string(p_var);
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- return variant_to_mono_array(p_var, p_type.type_class);
- case MONO_TYPE_CLASS:
- return variant_to_mono_object_of_class(p_var, p_type.type_class);
- case MONO_TYPE_GENERICINST:
- return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
- case MONO_TYPE_OBJECT:
- return variant_to_mono_object(p_var);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
-}
-
-MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- return BOX_BOOLEAN(val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_UINT16(val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- return BOX_INT8(val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- return BOX_INT16(val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- return BOX_INT32(val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- return BOX_INT64(val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- return BOX_UINT8(val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_UINT16(val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- return BOX_UINT32(val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- return BOX_UINT64(val);
- }
- case MONO_TYPE_R4: {
- float val = p_var.operator float();
- return BOX_FLOAT(val);
- }
- case MONO_TYPE_R8: {
- double val = p_var.operator double();
- return BOX_DOUBLE(val);
- }
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
-#define RETURN_CHECK_FOR_STRUCT(m_struct) \
- if (vtclass == CACHED_CLASS(m_struct)) { \
- GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \
- }
-
- RETURN_CHECK_FOR_STRUCT(Vector2);
- RETURN_CHECK_FOR_STRUCT(Vector2i);
- RETURN_CHECK_FOR_STRUCT(Rect2);
- RETURN_CHECK_FOR_STRUCT(Rect2i);
- RETURN_CHECK_FOR_STRUCT(Transform2D);
- RETURN_CHECK_FOR_STRUCT(Vector3);
- RETURN_CHECK_FOR_STRUCT(Vector3i);
- RETURN_CHECK_FOR_STRUCT(Basis);
- RETURN_CHECK_FOR_STRUCT(Quaternion);
- RETURN_CHECK_FOR_STRUCT(Transform3D);
- RETURN_CHECK_FOR_STRUCT(AABB);
- RETURN_CHECK_FOR_STRUCT(Color);
- RETURN_CHECK_FOR_STRUCT(Plane);
-
-#undef RETURN_CHECK_FOR_STRUCT
-
- if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
- MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
- switch (mono_type_get_type(enum_basetype)) {
- case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var.operator bool();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_CHAR: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I1: {
- int8_t val = p_var.operator signed char();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I2: {
- int16_t val = p_var.operator signed short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I4: {
- int32_t val = p_var.operator signed int();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_I8: {
- int64_t val = p_var.operator int64_t();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U1: {
- uint8_t val = p_var.operator unsigned char();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U2: {
- uint16_t val = p_var.operator unsigned short();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U4: {
- uint32_t val = p_var.operator unsigned int();
- return BOX_ENUM(enum_baseclass, val);
- }
- case MONO_TYPE_U8: {
- uint64_t val = p_var.operator uint64_t();
- return BOX_ENUM(enum_baseclass, val);
- }
- default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'.");
- }
- }
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
- } break;
- case MONO_TYPE_STRING:
- return (MonoObject *)variant_to_mono_string(p_var);
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class);
- case MONO_TYPE_CLASS:
- return variant_to_mono_object_of_class(p_var, p_type.type_class);
- case MONO_TYPE_GENERICINST:
- return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
- case MONO_TYPE_OBJECT:
- return variant_to_mono_object(p_var);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
-}
-
-Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
- ERR_FAIL_COND_V(!p_type.type_class, Variant());
-
-#ifdef DEBUG_ENABLED
- CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known.");
-#endif
-
- switch (p_type.type_encoding) {
- case MONO_TYPE_BOOLEAN:
- return (bool)unbox<MonoBoolean>(p_obj);
- case MONO_TYPE_CHAR:
- return unbox<uint16_t>(p_obj);
- case MONO_TYPE_I1:
- return unbox<int8_t>(p_obj);
- case MONO_TYPE_I2:
- return unbox<int16_t>(p_obj);
- case MONO_TYPE_I4:
- return unbox<int32_t>(p_obj);
- case MONO_TYPE_I8:
- return unbox<int64_t>(p_obj);
- case MONO_TYPE_U1:
- return unbox<uint8_t>(p_obj);
- case MONO_TYPE_U2:
- return unbox<uint16_t>(p_obj);
- case MONO_TYPE_U4:
- return unbox<uint32_t>(p_obj);
- case MONO_TYPE_U8:
- return unbox<uint64_t>(p_obj);
- case MONO_TYPE_R4:
- return unbox<float>(p_obj);
- case MONO_TYPE_R8:
- return unbox<double>(p_obj);
- case MONO_TYPE_VALUETYPE: {
- GDMonoClass *vtclass = p_type.type_class;
-
- if (vtclass == CACHED_CLASS(Vector2)) {
- return MARSHALLED_IN(Vector2, unbox_addr<GDMonoMarshal::M_Vector2>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- return MARSHALLED_IN(Vector2i, unbox_addr<GDMonoMarshal::M_Vector2i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- return MARSHALLED_IN(Rect2, unbox_addr<GDMonoMarshal::M_Rect2>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- return MARSHALLED_IN(Rect2i, unbox_addr<GDMonoMarshal::M_Rect2i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- return MARSHALLED_IN(Transform2D, unbox_addr<GDMonoMarshal::M_Transform2D>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- return MARSHALLED_IN(Vector3, unbox_addr<GDMonoMarshal::M_Vector3>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Vector3i)) {
- return MARSHALLED_IN(Vector3i, unbox_addr<GDMonoMarshal::M_Vector3i>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Basis)) {
- return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Quaternion)) {
- return MARSHALLED_IN(Quaternion, unbox_addr<GDMonoMarshal::M_Quaternion>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Transform3D)) {
- return MARSHALLED_IN(Transform3D, unbox_addr<GDMonoMarshal::M_Transform3D>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(AABB)) {
- return MARSHALLED_IN(AABB, unbox_addr<GDMonoMarshal::M_AABB>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Color)) {
- return MARSHALLED_IN(Color, unbox_addr<GDMonoMarshal::M_Color>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Plane)) {
- return MARSHALLED_IN(Plane, unbox_addr<GDMonoMarshal::M_Plane>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(Callable)) {
- return managed_to_callable(unbox<GDMonoMarshal::M_Callable>(p_obj));
- }
-
- if (vtclass == CACHED_CLASS(SignalInfo)) {
- return managed_to_signal_info(unbox<GDMonoMarshal::M_SignalInfo>(p_obj));
- }
-
- if (mono_class_is_enum(vtclass->get_mono_ptr())) {
- return unbox<int32_t>(p_obj);
- }
- } break;
- case MONO_TYPE_STRING: {
- if (p_obj == nullptr) {
- return Variant(); // NIL
- }
- return mono_string_to_godot_not_null((MonoString *)p_obj);
- } break;
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return mono_array_to_PackedByteArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return mono_array_to_PackedInt32Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return mono_array_to_PackedInt64Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return mono_array_to_PackedFloat32Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return mono_array_to_PackedFloat64Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return mono_array_to_PackedStringArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return mono_array_to_PackedVector2Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return mono_array_to_PackedVector3Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return mono_array_to_PackedColorArray((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return mono_array_to_Array((MonoArray *)p_obj);
- }
-
- if (p_fail_with_err) {
- ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant.");
- } else {
- return Variant();
- }
- } break;
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- if (ptr != nullptr) {
- RefCounted *rc = Object::cast_to<RefCounted>(ptr);
- return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
- }
- return Variant();
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- StringName *ptr = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- NodePath *ptr = unbox<NodePath *>(CACHED_FIELD(NodePath, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- RID *ptr = unbox<RID *>(CACHED_FIELD(RID, ptr)->get_value(p_obj));
- return ptr ? Variant(*ptr) : Variant();
- }
-
- // Godot.Collections.Dictionary
- if (CACHED_CLASS(Dictionary) == type_class) {
- MonoException *exc = nullptr;
- Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return ptr ? Variant(*ptr) : Variant();
- }
-
- // Godot.Collections.Array
- if (CACHED_CLASS(Array) == type_class) {
- MonoException *exc = nullptr;
- Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return ptr ? Variant(*ptr) : Variant();
- }
- } break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- MonoException *exc = nullptr;
- MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return *unbox<Dictionary *>(ret);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- MonoException *exc = nullptr;
- MonoObject *ret = p_type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
- UNHANDLED_EXCEPTION(exc);
- return *unbox<Array *>(ret);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return system_generic_dict_to_Dictionary(p_obj, p_type.type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return system_generic_list_to_Array_variant(p_obj, p_type.type_class, elem_reftype);
- }
-
- // GodotObject
- GDMonoClass *type_class = p_type.type_class;
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
- if (ptr != nullptr) {
- RefCounted *rc = Object::cast_to<RefCounted>(ptr);
- return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
- }
- return Variant();
- }
- } break;
- }
-
- if (p_fail_with_err) {
- ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
- } else {
- return Variant();
- }
-}
-
-Variant mono_object_to_variant(MonoObject *p_obj) {
- if (!p_obj) {
- return Variant();
- }
-
- ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
-
- return mono_object_to_variant_impl(p_obj, type);
-}
-
-Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type) {
- if (!p_obj) {
- return Variant();
- }
-
- return mono_object_to_variant_impl(p_obj, p_type);
-}
-
-Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type) {
- if (!p_obj) {
- return Variant();
- }
-
- return mono_object_to_variant_impl(p_obj, p_type, /* fail_with_err: */ false);
-}
-
-String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
- if (p_obj == nullptr) {
- return String("null");
- }
-
- ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
- Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type);
-
- if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true
- // Cannot convert MonoObject* to Variant; fallback to 'ToString()'.
- MonoException *exc = nullptr;
- MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
-
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- }
- return String();
- }
-
- return GDMonoMarshal::mono_string_to_godot(mono_str);
- } else {
- return var.operator String();
- }
-}
-
-MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
- String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
- GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(ctor == nullptr);
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
- MonoObject *godot_dict = GDMonoUtils::create_managed_from(p_dict, godot_dict_class);
-
- void *ctor_args[1] = { godot_dict };
-
- MonoException *exc = nullptr;
- ctor->invoke_raw(mono_object, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
- String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
- GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(godot_dict_ctor == nullptr);
-
- MonoObject *godot_dict = mono_object_new(mono_domain_get(), godot_dict_class->get_mono_ptr());
- ERR_FAIL_NULL_V(godot_dict, Dictionary());
-
- void *ctor_args[1] = { p_obj };
-
- MonoException *exc = nullptr;
- godot_dict_ctor->invoke_raw(godot_dict, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- exc = nullptr;
- MonoObject *ret = godot_dict_class->get_method("GetPtr")->invoke(godot_dict, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return *unbox<Dictionary *>(ret);
-}
-
-MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) {
- MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype);
-
- String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)";
- GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
- CRASH_COND(ctor == nullptr);
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype);
- MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class);
-
- void *ctor_args[1] = { godot_array };
-
- MonoException *exc = nullptr;
- ctor->invoke_raw(mono_object, ctor_args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) {
- GDMonoMethod *to_array = p_class->get_method("ToArray", 0);
- CRASH_COND(to_array == nullptr);
-
- MonoException *exc = nullptr;
- MonoObject *array = to_array->invoke_raw(p_obj, nullptr, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- ERR_FAIL_NULL_V(array, Variant());
-
- ManagedType type = ManagedType::from_class(mono_object_get_class(array));
-
- bool result_is_array = type.type_encoding != MONO_TYPE_SZARRAY && type.type_encoding != MONO_TYPE_ARRAY;
- ERR_FAIL_COND_V(result_is_array, Variant());
-
- return mono_object_to_variant(array, type);
-}
-
-MonoArray *Array_to_mono_array(const Array &p_array) {
- int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), length);
-
- for (int i = 0; i < length; i++) {
- MonoObject *boxed = variant_to_mono_object(p_array[i]);
- mono_array_setref(ret, i, boxed);
- }
-
- return ret;
-}
-
-MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) {
- int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length);
-
- for (int i = 0; i < length; i++) {
- MonoObject *boxed = variant_to_mono_object(p_array[i]);
- mono_array_setref(ret, i, boxed);
- }
-
- return ret;
-}
-
-Array mono_array_to_Array(MonoArray *p_array) {
- Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
-
- for (int i = 0; i < length; i++) {
- MonoObject *elem = mono_array_get(p_array, MonoObject *, i);
- ret[i] = mono_object_to_variant(elem);
- }
-
- return ret;
-}
-
-MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array) {
- const int32_t *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int32_t), length);
-
- int32_t *dst = mono_array_addr(ret, int32_t, 0);
- memcpy(dst, src, length * sizeof(int32_t));
-
- return ret;
-}
-
-PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array) {
- PackedInt32Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- int32_t *dst = ret.ptrw();
-
- const int32_t *src = mono_array_addr(p_array, int32_t, 0);
- memcpy(dst, src, length * sizeof(int32_t));
-
- return ret;
-}
-
-MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array) {
- const int64_t *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(int64_t), length);
-
- int64_t *dst = mono_array_addr(ret, int64_t, 0);
- memcpy(dst, src, length * sizeof(int64_t));
-
- return ret;
-}
-
-PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array) {
- PackedInt64Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- int64_t *dst = ret.ptrw();
-
- const int64_t *src = mono_array_addr(p_array, int64_t, 0);
- memcpy(dst, src, length * sizeof(int64_t));
-
- return ret;
-}
-
-MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array) {
- const uint8_t *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), length);
-
- uint8_t *dst = mono_array_addr(ret, uint8_t, 0);
- memcpy(dst, src, length * sizeof(uint8_t));
-
- return ret;
-}
-
-PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array) {
- PackedByteArray ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- uint8_t *dst = ret.ptrw();
-
- const uint8_t *src = mono_array_addr(p_array, uint8_t, 0);
- memcpy(dst, src, length * sizeof(uint8_t));
-
- return ret;
-}
-
-MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array) {
- const float *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(float), length);
-
- float *dst = mono_array_addr(ret, float, 0);
- memcpy(dst, src, length * sizeof(float));
-
- return ret;
-}
-
-PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array) {
- PackedFloat32Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- float *dst = ret.ptrw();
-
- const float *src = mono_array_addr(p_array, float, 0);
- memcpy(dst, src, length * sizeof(float));
-
- return ret;
-}
-
-MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array) {
- const double *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(double), length);
-
- double *dst = mono_array_addr(ret, double, 0);
- memcpy(dst, src, length * sizeof(double));
-
- return ret;
-}
-
-PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array) {
- PackedFloat64Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- double *dst = ret.ptrw();
-
- const double *src = mono_array_addr(p_array, double, 0);
- memcpy(dst, src, length * sizeof(double));
-
- return ret;
-}
-
-MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array) {
- const String *r = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), length);
-
- for (int i = 0; i < length; i++) {
- MonoString *boxed = mono_string_from_godot(r[i]);
- mono_array_setref(ret, i, boxed);
- }
-
- return ret;
-}
-
-PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array) {
- PackedStringArray ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- String *w = ret.ptrw();
-
- for (int i = 0; i < length; i++) {
- MonoString *elem = mono_array_get(p_array, MonoString *, i);
- w[i] = mono_string_to_godot(elem);
- }
-
- return ret;
-}
-
-MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array) {
- const Color *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Color), length);
-
- if constexpr (InteropLayout::MATCHES_Color) {
- Color *dst = mono_array_addr(ret, Color, 0);
- memcpy(dst, src, length * sizeof(Color));
- } else {
- for (int i = 0; i < length; i++) {
- M_Color *raw = (M_Color *)mono_array_addr_with_size(ret, sizeof(M_Color), i);
- *raw = MARSHALLED_OUT(Color, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array) {
- PackedColorArray ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Color *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Color) {
- const Color *src = mono_array_addr(p_array, Color, 0);
- memcpy(dst, src, length * sizeof(Color));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Color, (M_Color *)mono_array_addr_with_size(p_array, sizeof(M_Color), i));
- }
- }
-
- return ret;
-}
-
-MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array) {
- const Vector2 *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector2), length);
-
- if constexpr (InteropLayout::MATCHES_Vector2) {
- Vector2 *dst = mono_array_addr(ret, Vector2, 0);
- memcpy(dst, src, length * sizeof(Vector2));
- } else {
- for (int i = 0; i < length; i++) {
- M_Vector2 *raw = (M_Vector2 *)mono_array_addr_with_size(ret, sizeof(M_Vector2), i);
- *raw = MARSHALLED_OUT(Vector2, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array) {
- PackedVector2Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Vector2 *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Vector2) {
- const Vector2 *src = mono_array_addr(p_array, Vector2, 0);
- memcpy(dst, src, length * sizeof(Vector2));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Vector2, (M_Vector2 *)mono_array_addr_with_size(p_array, sizeof(M_Vector2), i));
- }
- }
-
- return ret;
-}
-
-MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array) {
- const Vector3 *src = p_array.ptr();
- int length = p_array.size();
-
- MonoArray *ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(Vector3), length);
-
- if constexpr (InteropLayout::MATCHES_Vector3) {
- Vector3 *dst = mono_array_addr(ret, Vector3, 0);
- memcpy(dst, src, length * sizeof(Vector3));
- } else {
- for (int i = 0; i < length; i++) {
- M_Vector3 *raw = (M_Vector3 *)mono_array_addr_with_size(ret, sizeof(M_Vector3), i);
- *raw = MARSHALLED_OUT(Vector3, src[i]);
- }
- }
-
- return ret;
-}
-
-PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array) {
- PackedVector3Array ret;
- if (!p_array) {
- return ret;
- }
- int length = mono_array_length(p_array);
- ret.resize(length);
- Vector3 *dst = ret.ptrw();
-
- if constexpr (InteropLayout::MATCHES_Vector3) {
- const Vector3 *src = mono_array_addr(p_array, Vector3, 0);
- memcpy(dst, src, length * sizeof(Vector3));
- } else {
- for (int i = 0; i < length; i++) {
- dst[i] = MARSHALLED_IN(Vector3, (M_Vector3 *)mono_array_addr_with_size(p_array, sizeof(M_Vector3), i));
- }
- }
-
- return ret;
-}
-
-Callable managed_to_callable(const M_Callable &p_managed_callable) {
- if (p_managed_callable.delegate) {
- // TODO: Use pooling for ManagedCallable instances.
- CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
- return Callable(managed_callable);
- } else {
- Object *target = p_managed_callable.target
- ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target))
- : nullptr;
- StringName *method_ptr = p_managed_callable.method_string_name
- ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name))
- : nullptr;
- StringName method = method_ptr ? *method_ptr : StringName();
- return Callable(target, method);
- }
-}
-
-M_Callable callable_to_managed(const Callable &p_callable) {
- if (p_callable.is_custom()) {
- CallableCustom *custom = p_callable.get_custom();
- CallableCustom::CompareEqualFunc compare_equal_func = custom->get_compare_equal_func();
-
- if (compare_equal_func == ManagedCallable::compare_equal_func_ptr) {
- ManagedCallable *managed_callable = static_cast<ManagedCallable *>(custom);
- return {
- nullptr, nullptr,
- managed_callable->get_delegate()
- };
- } else if (compare_equal_func == SignalAwaiterCallable::compare_equal_func_ptr) {
- SignalAwaiterCallable *signal_awaiter_callable = static_cast<SignalAwaiterCallable *>(custom);
- return {
- GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(signal_awaiter_callable->get_object())),
- GDMonoUtils::create_managed_from(signal_awaiter_callable->get_signal()),
- nullptr
- };
- } else if (compare_equal_func == EventSignalCallable::compare_equal_func_ptr) {
- EventSignalCallable *event_signal_callable = static_cast<EventSignalCallable *>(custom);
- return {
- GDMonoUtils::unmanaged_get_managed(ObjectDB::get_instance(event_signal_callable->get_object())),
- GDMonoUtils::create_managed_from(event_signal_callable->get_signal()),
- nullptr
- };
- }
-
- // Some other CallableCustom. We only support ManagedCallable.
- return { nullptr, nullptr, nullptr };
- } else {
- MonoObject *target_managed = GDMonoUtils::unmanaged_get_managed(p_callable.get_object());
- MonoObject *method_string_name_managed = GDMonoUtils::create_managed_from(p_callable.get_method());
- return { target_managed, method_string_name_managed, nullptr };
- }
-}
-
-Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
- Object *owner = p_managed_signal.owner
- ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner))
- : nullptr;
- StringName *name_ptr = p_managed_signal.name_string_name
- ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name))
- : nullptr;
- StringName name = name_ptr ? *name_ptr : StringName();
- return Signal(owner, name);
-}
-
-M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
- Object *owner = p_signal.get_object();
- MonoObject *owner_managed = GDMonoUtils::unmanaged_get_managed(owner);
- MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
- return { owner_managed, name_string_name_managed };
-}
-} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
deleted file mode 100644
index 3b6fd25d71..0000000000
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ /dev/null
@@ -1,605 +0,0 @@
-/*************************************************************************/
-/* gd_mono_marshal.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 GD_MONO_MARSHAL_H
-#define GD_MONO_MARSHAL_H
-
-#include "core/variant/variant.h"
-
-#include "../managed_callable.h"
-#include "gd_mono.h"
-#include "gd_mono_utils.h"
-
-namespace GDMonoMarshal {
-
-template <typename T>
-T unbox(MonoObject *p_obj) {
- return *(T *)mono_object_unbox(p_obj);
-}
-
-template <typename T>
-T *unbox_addr(MonoObject *p_obj) {
- return (T *)mono_object_unbox(p_obj);
-}
-
-#define BOX_DOUBLE(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(double), &x)
-#define BOX_FLOAT(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(float), &x)
-#define BOX_INT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int64_t), &x)
-#define BOX_INT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int32_t), &x)
-#define BOX_INT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int16_t), &x)
-#define BOX_INT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(int8_t), &x)
-#define BOX_UINT64(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint64_t), &x)
-#define BOX_UINT32(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint32_t), &x)
-#define BOX_UINT16(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint16_t), &x)
-#define BOX_UINT8(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), &x)
-#define BOX_BOOLEAN(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(bool), &x)
-#define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x)
-#define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x)
-
-Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_variant = nullptr);
-
-bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type);
-
-// String
-
-_FORCE_INLINE_ String mono_string_to_godot_not_null(MonoString *p_mono_string) {
- char32_t *utf32 = (char32_t *)mono_string_to_utf32(p_mono_string);
- String ret = String(utf32);
- mono_free(utf32);
- return ret;
-}
-
-_FORCE_INLINE_ String mono_string_to_godot(MonoString *p_mono_string) {
- if (p_mono_string == nullptr) {
- return String();
- }
-
- return mono_string_to_godot_not_null(p_mono_string);
-}
-
-_FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
- return mono_string_from_utf32((mono_unichar4 *)(p_string.get_data()));
-}
-
-// Variant
-
-size_t variant_get_managed_unboxed_size(const ManagedType &p_type);
-void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset);
-MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type);
-
-MonoObject *variant_to_mono_object(const Variant &p_var);
-MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class);
-MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class);
-MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class);
-MonoString *variant_to_mono_string(const Variant &p_var);
-
-// These overloads were added to avoid passing a `const Variant *` to the `const Variant &`
-// parameter. That would result in the `Variant(bool)` copy constructor being called as
-// pointers are implicitly converted to bool. Implicit conversions are f-ing evil.
-
-_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
- return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset);
-}
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
- return variant_to_mono_object(*p_var, p_type);
-}
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) {
- return variant_to_mono_object(*p_var);
-}
-_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) {
- return variant_to_mono_array(*p_var, p_type_class);
-}
-_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) {
- return variant_to_mono_object_of_class(*p_var, p_type_class);
-}
-_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) {
- return variant_to_mono_object_of_genericinst(*p_var, p_type_class);
-}
-_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) {
- return variant_to_mono_string(*p_var);
-}
-
-Variant mono_object_to_variant(MonoObject *p_obj);
-Variant mono_object_to_variant(MonoObject *p_obj, const ManagedType &p_type);
-Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_type);
-
-/// Tries to convert the MonoObject* to Variant and then convert the Variant to String.
-/// If the MonoObject* cannot be converted to Variant, then 'ToString()' is called instead.
-String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc);
-
-// System.Collections.Generic
-
-MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-
-MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
-Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
-
-// Array
-
-MonoArray *Array_to_mono_array(const Array &p_array);
-MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class);
-Array mono_array_to_Array(MonoArray *p_array);
-
-// PackedInt32Array
-
-MonoArray *PackedInt32Array_to_mono_array(const PackedInt32Array &p_array);
-PackedInt32Array mono_array_to_PackedInt32Array(MonoArray *p_array);
-
-// PackedInt64Array
-
-MonoArray *PackedInt64Array_to_mono_array(const PackedInt64Array &p_array);
-PackedInt64Array mono_array_to_PackedInt64Array(MonoArray *p_array);
-
-// PackedByteArray
-
-MonoArray *PackedByteArray_to_mono_array(const PackedByteArray &p_array);
-PackedByteArray mono_array_to_PackedByteArray(MonoArray *p_array);
-
-// PackedFloat32Array
-
-MonoArray *PackedFloat32Array_to_mono_array(const PackedFloat32Array &p_array);
-PackedFloat32Array mono_array_to_PackedFloat32Array(MonoArray *p_array);
-
-// PackedFloat64Array
-
-MonoArray *PackedFloat64Array_to_mono_array(const PackedFloat64Array &p_array);
-PackedFloat64Array mono_array_to_PackedFloat64Array(MonoArray *p_array);
-
-// PackedStringArray
-
-MonoArray *PackedStringArray_to_mono_array(const PackedStringArray &p_array);
-PackedStringArray mono_array_to_PackedStringArray(MonoArray *p_array);
-
-// PackedColorArray
-
-MonoArray *PackedColorArray_to_mono_array(const PackedColorArray &p_array);
-PackedColorArray mono_array_to_PackedColorArray(MonoArray *p_array);
-
-// PackedVector2Array
-
-MonoArray *PackedVector2Array_to_mono_array(const PackedVector2Array &p_array);
-PackedVector2Array mono_array_to_PackedVector2Array(MonoArray *p_array);
-
-// PackedVector3Array
-
-MonoArray *PackedVector3Array_to_mono_array(const PackedVector3Array &p_array);
-PackedVector3Array mono_array_to_PackedVector3Array(MonoArray *p_array);
-
-#pragma pack(push, 1)
-
-struct M_Callable {
- MonoObject *target = nullptr;
- MonoObject *method_string_name = nullptr;
- MonoDelegate *delegate = nullptr;
-};
-
-struct M_SignalInfo {
- MonoObject *owner = nullptr;
- MonoObject *name_string_name = nullptr;
-};
-
-#pragma pack(pop)
-
-// Callable
-Callable managed_to_callable(const M_Callable &p_managed_callable);
-M_Callable callable_to_managed(const Callable &p_callable);
-
-// SignalInfo
-Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal);
-M_SignalInfo signal_info_to_managed(const Signal &p_signal);
-
-// Structures
-
-namespace InteropLayout {
-
-enum {
- MATCHES_int = (sizeof(int32_t) == sizeof(uint32_t)),
-
- MATCHES_float = (sizeof(float) == sizeof(uint32_t)),
-
- MATCHES_double = (sizeof(double) == sizeof(uint64_t)),
-
-#ifdef REAL_T_IS_DOUBLE
- MATCHES_real_t = (sizeof(real_t) == sizeof(uint64_t)),
-#else
- MATCHES_real_t = (sizeof(real_t) == sizeof(uint32_t)),
-#endif
-
- MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) &&
- offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector2, y) == (sizeof(real_t) * 1)),
-
- MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) &&
- offsetof(Vector2i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector2i, y) == (sizeof(int32_t) * 1)),
-
- MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) &&
- offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
- offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
-
- MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) &&
- offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) &&
- offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)),
-
- MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array
-
- MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) &&
- offsetof(Vector3, x) == (sizeof(real_t) * 0) &&
- 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) &&
- offsetof(Vector3i, z) == (sizeof(int32_t) * 2)),
-
- MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array
-
- MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) &&
- offsetof(Quaternion, x) == (sizeof(real_t) * 0) &&
- offsetof(Quaternion, y) == (sizeof(real_t) * 1) &&
- offsetof(Quaternion, z) == (sizeof(real_t) * 2) &&
- offsetof(Quaternion, w) == (sizeof(real_t) * 3)),
-
- MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) &&
- 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)),
-
- MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) &&
- offsetof(Color, r) == (sizeof(float) * 0) &&
- offsetof(Color, g) == (sizeof(float) * 1) &&
- offsetof(Color, b) == (sizeof(float) * 2) &&
- offsetof(Color, a) == (sizeof(float) * 3)),
-
- MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) &&
- offsetof(Plane, normal) == 0 &&
- offsetof(Plane, d) == sizeof(Vector3))
-};
-
-// 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_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
-
-#pragma pack(push, 1)
-
-struct M_Vector2 {
- real_t x, y;
-
- static _FORCE_INLINE_ Vector2 convert_to(const M_Vector2 &p_from) {
- return Vector2(p_from.x, p_from.y);
- }
-
- static _FORCE_INLINE_ M_Vector2 convert_from(const Vector2 &p_from) {
- M_Vector2 ret = { p_from.x, p_from.y };
- return ret;
- }
-};
-
-struct M_Vector2i {
- int32_t x, y;
-
- static _FORCE_INLINE_ Vector2i convert_to(const M_Vector2i &p_from) {
- return Vector2i(p_from.x, p_from.y);
- }
-
- static _FORCE_INLINE_ M_Vector2i convert_from(const Vector2i &p_from) {
- M_Vector2i ret = { p_from.x, p_from.y };
- return ret;
- }
-};
-
-struct M_Rect2 {
- M_Vector2 position;
- M_Vector2 size;
-
- static _FORCE_INLINE_ Rect2 convert_to(const M_Rect2 &p_from) {
- return Rect2(M_Vector2::convert_to(p_from.position),
- M_Vector2::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_Rect2 convert_from(const Rect2 &p_from) {
- M_Rect2 ret = { M_Vector2::convert_from(p_from.position), M_Vector2::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Rect2i {
- M_Vector2i position;
- M_Vector2i size;
-
- static _FORCE_INLINE_ Rect2i convert_to(const M_Rect2i &p_from) {
- return Rect2i(M_Vector2i::convert_to(p_from.position),
- M_Vector2i::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_Rect2i convert_from(const Rect2i &p_from) {
- M_Rect2i ret = { M_Vector2i::convert_from(p_from.position), M_Vector2i::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Transform2D {
- M_Vector2 elements[3];
-
- static _FORCE_INLINE_ Transform2D convert_to(const M_Transform2D &p_from) {
- return Transform2D(p_from.elements[0].x, p_from.elements[0].y,
- p_from.elements[1].x, p_from.elements[1].y,
- p_from.elements[2].x, p_from.elements[2].y);
- }
-
- static _FORCE_INLINE_ M_Transform2D convert_from(const Transform2D &p_from) {
- M_Transform2D ret = {
- 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;
- }
-};
-
-struct M_Vector3 {
- real_t x, y, z;
-
- static _FORCE_INLINE_ Vector3 convert_to(const M_Vector3 &p_from) {
- return Vector3(p_from.x, p_from.y, p_from.z);
- }
-
- static _FORCE_INLINE_ M_Vector3 convert_from(const Vector3 &p_from) {
- M_Vector3 ret = { p_from.x, p_from.y, p_from.z };
- return ret;
- }
-};
-
-struct M_Vector3i {
- int32_t x, y, z;
-
- static _FORCE_INLINE_ Vector3i convert_to(const M_Vector3i &p_from) {
- return Vector3i(p_from.x, p_from.y, p_from.z);
- }
-
- static _FORCE_INLINE_ M_Vector3i convert_from(const Vector3i &p_from) {
- M_Vector3i ret = { p_from.x, p_from.y, p_from.z };
- return ret;
- }
-};
-
-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];
-
- static _FORCE_INLINE_ Basis convert_to(const M_Basis &p_from) {
- return Basis(M_Vector3::convert_to(p_from.elements[0]),
- M_Vector3::convert_to(p_from.elements[1]),
- M_Vector3::convert_to(p_from.elements[2]));
- }
-
- static _FORCE_INLINE_ M_Basis convert_from(const Basis &p_from) {
- M_Basis ret = {
- 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;
- }
-};
-
-struct M_Quaternion {
- real_t x, y, z, w;
-
- static _FORCE_INLINE_ Quaternion convert_to(const M_Quaternion &p_from) {
- return Quaternion(p_from.x, p_from.y, p_from.z, p_from.w);
- }
-
- static _FORCE_INLINE_ M_Quaternion convert_from(const Quaternion &p_from) {
- M_Quaternion ret = { p_from.x, p_from.y, p_from.z, p_from.w };
- return ret;
- }
-};
-
-struct M_Transform3D {
- M_Basis basis;
- M_Vector3 origin;
-
- static _FORCE_INLINE_ Transform3D convert_to(const M_Transform3D &p_from) {
- return Transform3D(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin));
- }
-
- static _FORCE_INLINE_ M_Transform3D convert_from(const Transform3D &p_from) {
- M_Transform3D ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) };
- return ret;
- }
-};
-
-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;
-
- static _FORCE_INLINE_ AABB convert_to(const M_AABB &p_from) {
- return AABB(M_Vector3::convert_to(p_from.position), M_Vector3::convert_to(p_from.size));
- }
-
- static _FORCE_INLINE_ M_AABB convert_from(const AABB &p_from) {
- M_AABB ret = { M_Vector3::convert_from(p_from.position), M_Vector3::convert_from(p_from.size) };
- return ret;
- }
-};
-
-struct M_Color {
- float r, g, b, a;
-
- static _FORCE_INLINE_ Color convert_to(const M_Color &p_from) {
- return Color(p_from.r, p_from.g, p_from.b, p_from.a);
- }
-
- static _FORCE_INLINE_ M_Color convert_from(const Color &p_from) {
- M_Color ret = { p_from.r, p_from.g, p_from.b, p_from.a };
- return ret;
- }
-};
-
-struct M_Plane {
- M_Vector3 normal;
- real_t d;
-
- static _FORCE_INLINE_ Plane convert_to(const M_Plane &p_from) {
- return Plane(M_Vector3::convert_to(p_from.normal), p_from.d);
- }
-
- static _FORCE_INLINE_ M_Plane convert_from(const Plane &p_from) {
- M_Plane ret = { M_Vector3::convert_from(p_from.normal), p_from.d };
- return ret;
- }
-};
-
-#pragma pack(pop)
-
-#define DECL_TYPE_MARSHAL_TEMPLATES(m_type) \
- template <int> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl(const M_##m_type *p_from); \
- \
- template <> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<0>(const M_##m_type *p_from) { \
- return M_##m_type::convert_to(*p_from); \
- } \
- \
- template <> \
- _FORCE_INLINE_ m_type marshalled_in_##m_type##_impl<1>(const M_##m_type *p_from) { \
- return *reinterpret_cast<const m_type *>(p_from); \
- } \
- \
- _FORCE_INLINE_ m_type marshalled_in_##m_type(const M_##m_type *p_from) { \
- return marshalled_in_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
- } \
- \
- template <int> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl(const m_type &p_from); \
- \
- template <> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<0>(const m_type &p_from) { \
- return M_##m_type::convert_from(p_from); \
- } \
- \
- template <> \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type##_impl<1>(const m_type &p_from) { \
- return *reinterpret_cast<const M_##m_type *>(&p_from); \
- } \
- \
- _FORCE_INLINE_ M_##m_type marshalled_out_##m_type(const m_type &p_from) { \
- return marshalled_out_##m_type##_impl<InteropLayout::MATCHES_##m_type>(p_from); \
- }
-
-DECL_TYPE_MARSHAL_TEMPLATES(Vector2)
-DECL_TYPE_MARSHAL_TEMPLATES(Vector2i)
-DECL_TYPE_MARSHAL_TEMPLATES(Rect2)
-DECL_TYPE_MARSHAL_TEMPLATES(Rect2i)
-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)
-
-#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr))
-#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from))
-} // namespace GDMonoMarshal
-
-#endif // GD_MONO_MARSHAL_H
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
deleted file mode 100644
index 6734b44783..0000000000
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method.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 "gd_mono_method.h"
-
-#include <mono/metadata/attrdefs.h>
-#include <mono/metadata/debug-helpers.h>
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-void GDMonoMethod::_update_signature() {
- // Apparently MonoMethodSignature needs not to be freed.
- // mono_method_signature caches the result, we don't need to cache it ourselves.
-
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- _update_signature(method_sig);
-}
-
-void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
- params_count = mono_signature_get_param_count(p_method_sig);
-
- MonoType *ret_type = mono_signature_get_return_type(p_method_sig);
- if (ret_type) {
- return_type.type_encoding = mono_type_get_type(ret_type);
-
- if (return_type.type_encoding != MONO_TYPE_VOID) {
- MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
- return_type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
- }
- }
-
- void *iter = nullptr;
- MonoType *param_raw_type;
- while ((param_raw_type = mono_signature_get_params(p_method_sig, &iter)) != nullptr) {
- ManagedType param_type;
-
- param_type.type_encoding = mono_type_get_type(param_raw_type);
-
- MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
- param_type.type_class = GDMono::get_singleton()->get_class(param_type_class);
-
- param_types.push_back(param_type);
- }
-
- // clear the cache
- method_info_fetched = false;
- method_info = MethodInfo();
-
- for (int i = 0; i < params_count; i++) {
- params_buffer_size += GDMonoMarshal::variant_get_managed_unboxed_size(param_types[i]);
- }
-}
-
-GDMonoClass *GDMonoMethod::get_enclosing_class() const {
- return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method));
-}
-
-bool GDMonoMethod::is_static() {
- return mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoMethod::get_visibility() {
- switch (mono_method_get_flags(mono_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
- case MONO_METHOD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_METHOD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_METHOD_ATTR_ASSEM:
- return IMonoClassMember::INTERNAL;
- case MONO_METHOD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_METHOD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc) const {
- MonoException *exc = nullptr;
- MonoObject *ret;
-
- if (params_count > 0) {
- void **params = (void **)alloca(params_count * sizeof(void *));
- uint8_t *buffer = (uint8_t *)alloca(params_buffer_size);
- unsigned int offset = 0;
-
- for (int i = 0; i < params_count; i++) {
- params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset);
- }
-
- ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc);
- } else {
- ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
- }
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) const {
- ERR_FAIL_COND_V(get_parameters_count() > 0, nullptr);
- return invoke_raw(p_object, nullptr, r_exc);
-}
-
-MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) const {
- MonoException *exc = nullptr;
- MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc);
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-bool GDMonoMethod::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoMethod::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoMethod::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_method(mono_method);
- attrs_fetched = true;
-}
-
-String GDMonoMethod::get_full_name(bool p_signature) const {
- char *res = mono_method_full_name(mono_method, p_signature);
- String full_name(res);
- mono_free(res);
- return full_name;
-}
-
-String GDMonoMethod::get_full_name_no_class() const {
- String res;
-
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
-
- char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
- res += ret_str;
- mono_free(ret_str);
-
- res += " ";
- res += name;
- res += "(";
-
- char *sig_desc = mono_signature_get_desc(method_sig, true);
- res += sig_desc;
- mono_free(sig_desc);
-
- res += ")";
-
- return res;
-}
-
-String GDMonoMethod::get_ret_type_full_name() const {
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- char *ret_str = mono_type_full_name(mono_signature_get_return_type(method_sig));
- String res = ret_str;
- mono_free(ret_str);
- return res;
-}
-
-String GDMonoMethod::get_signature_desc(bool p_namespaces) const {
- MonoMethodSignature *method_sig = mono_method_signature(mono_method);
- char *sig_desc = mono_signature_get_desc(method_sig, p_namespaces);
- String res = sig_desc;
- mono_free(sig_desc);
- return res;
-}
-
-void GDMonoMethod::get_parameter_names(Vector<StringName> &names) const {
- if (params_count > 0) {
- const char **_names = memnew_arr(const char *, params_count);
- mono_method_get_param_names(mono_method, _names);
- for (int i = 0; i < params_count; ++i) {
- names.push_back(StringName(_names[i]));
- }
- memdelete_arr(_names);
- }
-}
-
-void GDMonoMethod::get_parameter_types(Vector<ManagedType> &types) const {
- for (int i = 0; i < params_count; ++i) {
- types.push_back(param_types[i]);
- }
-}
-
-const MethodInfo &GDMonoMethod::get_method_info() {
- if (!method_info_fetched) {
- method_info.name = name;
-
- bool nil_is_variant = false;
- method_info.return_val = PropertyInfo(GDMonoMarshal::managed_to_variant_type(return_type, &nil_is_variant), "");
- if (method_info.return_val.type == Variant::NIL && nil_is_variant) {
- method_info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
-
- Vector<StringName> names;
- get_parameter_names(names);
-
- for (int i = 0; i < params_count; ++i) {
- nil_is_variant = false;
- PropertyInfo arg_info = PropertyInfo(GDMonoMarshal::managed_to_variant_type(param_types[i], &nil_is_variant), names[i]);
- if (arg_info.type == Variant::NIL && nil_is_variant) {
- arg_info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
-
- method_info.arguments.push_back(arg_info);
- }
-
- // TODO: default arguments
-
- method_info_fetched = true;
- }
-
- return method_info;
-}
-
-GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) :
- name(p_name), mono_method(p_method) {
- _update_signature();
-}
-
-GDMonoMethod::~GDMonoMethod() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
deleted file mode 100644
index be11ef5bfe..0000000000
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method.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 GD_MONO_METHOD_H
-#define GD_MONO_METHOD_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoMethod : public IMonoClassMember {
- StringName name;
-
- uint16_t params_count;
- unsigned int params_buffer_size = 0;
- ManagedType return_type;
- Vector<ManagedType> param_types;
-
- bool method_info_fetched = false;
- MethodInfo method_info;
-
- bool attrs_fetched = false;
- MonoCustomAttrInfo *attributes = nullptr;
-
- void _update_signature();
- void _update_signature(MonoMethodSignature *p_method_sig);
-
- friend class GDMonoClass;
-
- MonoMethod *mono_method = nullptr;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final;
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_METHOD; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
-
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- _FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
-
- _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; }
- _FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
-
- MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
- MonoObject *invoke(MonoObject *p_object, MonoException **r_exc = nullptr) const;
- MonoObject *invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr) const;
-
- String get_full_name(bool p_signature = false) const;
- String get_full_name_no_class() const;
- String get_ret_type_full_name() const;
- String get_signature_desc(bool p_namespaces = false) const;
-
- void get_parameter_names(Vector<StringName> &names) const;
- void get_parameter_types(Vector<ManagedType> &types) const;
-
- const MethodInfo &get_method_info();
-
- GDMonoMethod(StringName p_name, MonoMethod *p_method);
- ~GDMonoMethod();
-};
-
-#endif // GD_MONO_METHOD_H
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
deleted file mode 100644
index 0180dee3ea..0000000000
--- a/modules/mono/mono_gd/gd_mono_method_thunk.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/*************************************************************************/
-/* gd_mono_method_thunk.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 GD_MONO_METHOD_THUNK_H
-#define GD_MONO_METHOD_THUNK_H
-
-#include <type_traits>
-
-#include "gd_mono_class.h"
-#include "gd_mono_header.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_method.h"
-#include "gd_mono_utils.h"
-
-#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED)
-#define HAVE_METHOD_THUNKS
-#endif
-
-#ifdef HAVE_METHOD_THUNKS
-
-template <class... ParamTypes>
-struct GDMonoMethodThunk {
- typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
-
- M mono_method_thunk = nullptr;
-
-public:
- _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_method_thunk(p_args..., r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method_thunk = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-
- GDMonoMethodThunk() {}
-
- explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-template <class R, class... ParamTypes>
-struct GDMonoMethodThunkR {
- typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
-
- M mono_method_thunk = nullptr;
-
-public:
- _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- R r = mono_method_thunk(p_args..., r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return r;
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method_thunk == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method_thunk = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-
- GDMonoMethodThunkR() {}
-
- explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
-#endif
- mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
- }
-};
-
-#else
-
-template <unsigned int ThunkParamCount, class P1, class... ParamTypes>
-struct VariadicInvokeMonoMethodImpl {
- static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[ThunkParamCount] = { p_arg1, p_args... };
- p_mono_method->invoke_raw(nullptr, args, r_exc);
- } else {
- void *args[ThunkParamCount] = { p_args... };
- p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
- }
- }
-};
-
-template <unsigned int ThunkParamCount, class... ParamTypes>
-struct VariadicInvokeMonoMethod {
- static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
- VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
- }
-};
-
-template <>
-struct VariadicInvokeMonoMethod<0> {
- static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!p_mono_method->is_static());
-#endif
- p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
- }
-};
-
-template <class P1>
-struct VariadicInvokeMonoMethod<1, P1> {
- static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[1] = { p_arg1 };
- p_mono_method->invoke_raw(nullptr, args, r_exc);
- } else {
- p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
- }
- }
-};
-
-template <class R>
-R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) {
- return GDMonoMarshal::unbox<R>(p_val);
-}
-
-template <class R>
-R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) {
- if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) {
- return GDMonoMarshal::unbox<R>(p_val);
- } else {
- // If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'.
- return (R)p_val;
- }
-}
-
-template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes>
-struct VariadicInvokeMonoMethodRImpl {
- static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[ThunkParamCount] = { p_arg1, p_args... };
- MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- } else {
- void *args[ThunkParamCount] = { p_args... };
- MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
- }
-};
-
-template <unsigned int ThunkParamCount, class R, class... ParamTypes>
-struct VariadicInvokeMonoMethodR {
- static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
- return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
- }
-};
-
-template <class R>
-struct VariadicInvokeMonoMethodR<0, R> {
- static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!p_mono_method->is_static());
-#endif
- MonoObject *r = p_mono_method->invoke_raw(nullptr, nullptr, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
-};
-
-template <class R, class P1>
-struct VariadicInvokeMonoMethodR<1, R, P1> {
- static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
- if (p_mono_method->is_static()) {
- void *args[1] = { p_arg1 };
- MonoObject *r = p_mono_method->invoke_raw(nullptr, args, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- } else {
- MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, nullptr, r_exc);
- return unbox_if_needed<R>(r, p_mono_method->get_return_type());
- }
- }
-};
-
-template <class... ParamTypes>
-struct GDMonoMethodThunk {
- GDMonoMethod *mono_method = nullptr;
-
-public:
- _FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
- VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc);
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method = p_mono_method;
- }
-
- GDMonoMethodThunk() {}
-
- explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-template <class R, class... ParamTypes>
-struct GDMonoMethodThunkR {
- GDMonoMethod *mono_method = nullptr;
-
-public:
- _FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
- return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc);
- }
-
- _FORCE_INLINE_ bool is_null() {
- return mono_method == nullptr;
- }
-
- _FORCE_INLINE_ void nullify() {
- mono_method = nullptr;
- }
-
- _FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(p_mono_method == nullptr);
- CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
-
- if (p_mono_method->is_static()) {
- CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
- } else {
- CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
- }
-#endif
- mono_method = p_mono_method;
- }
-
- GDMonoMethodThunkR() {}
-
- explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
- set_from_method(p_mono_method);
- }
-};
-
-#endif
-
-#endif // GD_MONO_METHOD_THUNK_H
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
deleted file mode 100644
index c9775ae9cb..0000000000
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*************************************************************************/
-/* gd_mono_property.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 "gd_mono_property.h"
-
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-#include "gd_mono_utils.h"
-
-#include <mono/metadata/attrdefs.h>
-
-GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) {
- owner = p_owner;
- mono_property = p_mono_property;
- name = String::utf8(mono_property_get_name(mono_property));
-
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
-
- if (prop_method) {
- MonoMethodSignature *getter_sig = mono_method_signature(prop_method);
-
- MonoType *ret_type = mono_signature_get_return_type(getter_sig);
-
- type.type_encoding = mono_type_get_type(ret_type);
- MonoClass *ret_type_class = mono_class_from_mono_type(ret_type);
- type.type_class = GDMono::get_singleton()->get_class(ret_type_class);
- } else {
- prop_method = mono_property_get_set_method(mono_property);
-
- MonoMethodSignature *setter_sig = mono_method_signature(prop_method);
-
- void *iter = nullptr;
- MonoType *param_raw_type = mono_signature_get_params(setter_sig, &iter);
-
- type.type_encoding = mono_type_get_type(param_raw_type);
- MonoClass *param_type_class = mono_class_from_mono_type(param_raw_type);
- type.type_class = GDMono::get_singleton()->get_class(param_type_class);
- }
-
- param_buffer_size = GDMonoMarshal::variant_get_managed_unboxed_size(type);
-
- attrs_fetched = false;
- attributes = nullptr;
-}
-
-GDMonoProperty::~GDMonoProperty() {
- if (attributes) {
- mono_custom_attrs_free(attributes);
- }
-}
-
-bool GDMonoProperty::is_static() {
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == nullptr) {
- prop_method = mono_property_get_set_method(mono_property);
- }
- return mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_STATIC;
-}
-
-IMonoClassMember::Visibility GDMonoProperty::get_visibility() {
- MonoMethod *prop_method = mono_property_get_get_method(mono_property);
- if (prop_method == nullptr) {
- prop_method = mono_property_get_set_method(mono_property);
- }
-
- switch (mono_method_get_flags(prop_method, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK) {
- case MONO_METHOD_ATTR_PRIVATE:
- return IMonoClassMember::PRIVATE;
- case MONO_METHOD_ATTR_FAM_AND_ASSEM:
- return IMonoClassMember::PROTECTED_AND_INTERNAL;
- case MONO_METHOD_ATTR_ASSEM:
- return IMonoClassMember::INTERNAL;
- case MONO_METHOD_ATTR_FAMILY:
- return IMonoClassMember::PROTECTED;
- case MONO_METHOD_ATTR_PUBLIC:
- return IMonoClassMember::PUBLIC;
- default:
- ERR_FAIL_V(IMonoClassMember::PRIVATE);
- }
-}
-
-bool GDMonoProperty::has_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, false);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return false;
- }
-
- return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-MonoObject *GDMonoProperty::get_attribute(GDMonoClass *p_attr_class) {
- ERR_FAIL_NULL_V(p_attr_class, nullptr);
-
- if (!attrs_fetched) {
- fetch_attributes();
- }
-
- if (!attributes) {
- return nullptr;
- }
-
- return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
-}
-
-void GDMonoProperty::fetch_attributes() {
- ERR_FAIL_COND(attributes != nullptr);
- attributes = mono_custom_attrs_from_property(owner->get_mono_ptr(), mono_property);
- attrs_fetched = true;
-}
-
-bool GDMonoProperty::has_getter() {
- return mono_property_get_get_method(mono_property) != nullptr;
-}
-
-bool GDMonoProperty::has_setter() {
- return mono_property_get_set_method(mono_property) != nullptr;
-}
-
-void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) {
- uint8_t *buffer = (uint8_t *)alloca(param_buffer_size);
- unsigned int offset = 0;
-
- void *params[1] = {
- GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset)
- };
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(offset != param_buffer_size);
-#endif
-
- MonoException *exc = nullptr;
- GDMonoUtils::property_set_value(mono_property, p_object, params, &exc);
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-}
-
-MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) {
- MonoException *exc = nullptr;
- MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, nullptr, &exc);
-
- if (exc) {
- ret = nullptr;
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-
- return ret;
-}
-
-bool GDMonoProperty::get_bool_value(MonoObject *p_object) {
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(get_value(p_object));
-}
-
-int GDMonoProperty::get_int_value(MonoObject *p_object) {
- return GDMonoMarshal::unbox<int32_t>(get_value(p_object));
-}
-
-String GDMonoProperty::get_string_value(MonoObject *p_object) {
- MonoObject *val = get_value(p_object);
- return GDMonoMarshal::mono_string_to_godot((MonoString *)val);
-}
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
deleted file mode 100644
index 6fc681aeb5..0000000000
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*************************************************************************/
-/* gd_mono_property.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 GD_MONO_PROPERTY_H
-#define GD_MONO_PROPERTY_H
-
-#include "gd_mono.h"
-#include "gd_mono_header.h"
-#include "i_mono_class_member.h"
-
-class GDMonoProperty : public IMonoClassMember {
- GDMonoClass *owner = nullptr;
- MonoProperty *mono_property = nullptr;
-
- StringName name;
- ManagedType type;
-
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes = nullptr;
-
- unsigned int param_buffer_size;
-
-public:
- virtual GDMonoClass *get_enclosing_class() const final { return owner; }
-
- virtual MemberType get_member_type() const final { return MEMBER_TYPE_PROPERTY; }
-
- virtual StringName get_name() const final { return name; }
-
- virtual bool is_static() final;
- virtual Visibility get_visibility() final;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) final;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) final;
- void fetch_attributes();
-
- bool has_getter();
- bool has_setter();
-
- _FORCE_INLINE_ ManagedType get_type() const { return type; }
-
- void set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc = nullptr);
- MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr);
-
- bool get_bool_value(MonoObject *p_object);
- int get_int_value(MonoObject *p_object);
- String get_string_value(MonoObject *p_object);
-
- GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner);
- ~GDMonoProperty();
-};
-
-#endif // GD_MONO_PROPERTY_H
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
deleted file mode 100644
index 1983d6ebe2..0000000000
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ /dev/null
@@ -1,677 +0,0 @@
-/*************************************************************************/
-/* gd_mono_utils.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 "gd_mono_utils.h"
-
-#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/exception.h>
-
-#include "core/debugger/engine_debugger.h"
-#include "core/debugger/script_debugger.h"
-#include "core/io/dir_access.h"
-#include "core/object/ref_counted.h"
-#include "core/os/mutex.h"
-#include "core/os/os.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/debugger/editor_debugger_node.h"
-#endif
-
-#include "../csharp_script.h"
-#include "../utils/macros.h"
-#include "gd_mono.h"
-#include "gd_mono_cache.h"
-#include "gd_mono_class.h"
-#include "gd_mono_marshal.h"
-
-namespace GDMonoUtils {
-
-MonoObject *unmanaged_get_managed(Object *unmanaged) {
- if (!unmanaged) {
- return nullptr;
- }
-
- if (unmanaged->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
-
- if (cs_instance) {
- return cs_instance->get_mono_object();
- }
- }
-
- // If the owner does not have a CSharpInstance...
-
- void *data = CSharpLanguage::get_instance_binding(unmanaged);
- ERR_FAIL_NULL_V(data, nullptr);
- CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
- ERR_FAIL_COND_V(!script_binding.inited, nullptr);
-
- MonoGCHandleData &gchandle = script_binding.gchandle;
-
- MonoObject *target = gchandle.get_target();
-
- if (target) {
- return target;
- }
-
- CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
-
- // Create a new one
-
-#ifdef DEBUG_ENABLED
- CRASH_COND(script_binding.type_name == StringName());
- CRASH_COND(script_binding.wrapper_class == nullptr);
-#endif
-
- MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- gchandle = MonoGCHandleData::new_strong_handle(mono_object);
-
- // Tie managed to unmanaged
- RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
-
- if (rc) {
- // Unsafe refcount increment. The managed instance also counts as a reference.
- // This way if the unmanaged world has no references to our owner
- // but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
- rc->reference();
- CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
- }
-
- return mono_object;
-}
-
-void set_main_thread(MonoThread *p_thread) {
- mono_thread_set_main(p_thread);
-}
-
-MonoThread *attach_current_thread() {
- ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr);
- MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain();
-#ifndef GD_MONO_SINGLE_APPDOMAIN
- MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain());
-#else
- // The scripts domain is the root domain
- MonoThread *mono_thread = mono_thread_attach(scripts_domain);
-#endif
- ERR_FAIL_NULL_V(mono_thread, nullptr);
- return mono_thread;
-}
-
-void detach_current_thread() {
- ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
- MonoThread *mono_thread = mono_thread_current();
- ERR_FAIL_NULL(mono_thread);
- mono_thread_detach(mono_thread);
-}
-
-void detach_current_thread(MonoThread *p_mono_thread) {
- ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
- ERR_FAIL_NULL(p_mono_thread);
- mono_thread_detach(p_mono_thread);
-}
-
-MonoThread *get_current_thread() {
- return mono_thread_current();
-}
-
-bool is_thread_attached() {
- return mono_domain_get() != nullptr;
-}
-
-uint32_t new_strong_gchandle(MonoObject *p_object) {
- return mono_gchandle_new(p_object, /* pinned: */ false);
-}
-
-uint32_t new_strong_gchandle_pinned(MonoObject *p_object) {
- return mono_gchandle_new(p_object, /* pinned: */ true);
-}
-
-uint32_t new_weak_gchandle(MonoObject *p_object) {
- return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
-}
-
-void free_gchandle(uint32_t p_gchandle) {
- mono_gchandle_free(p_gchandle);
-}
-
-void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
- GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
- ERR_FAIL_NULL(ctor);
- ctor->invoke_raw(p_this_obj, nullptr, r_exc);
-}
-
-bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b) {
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(Delegate, Equals).invoke((MonoObject *)p_a, (MonoObject *)p_b, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-GDMonoClass *get_object_class(MonoObject *p_object) {
- return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
-}
-
-GDMonoClass *type_get_proxy_class(const StringName &p_type) {
- String class_name = p_type;
-
- if (class_name[0] == '_') {
- class_name = class_name.substr(1, class_name.length());
- }
-
- GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
-
- if (klass && klass->is_static()) {
- // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
- return GDMonoCache::cached_data.class_GodotObject;
- }
-
-#ifdef TOOLS_ENABLED
- if (!klass) {
- return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
- }
-#endif
-
- return klass;
-}
-
-GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
- GDMonoClass *klass = p_class;
-
- do {
- const GDMonoAssembly *assembly = klass->get_assembly();
-
- if (assembly == GDMono::get_singleton()->get_core_api_assembly()) {
- return klass;
- }
-#ifdef TOOLS_ENABLED
- if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) {
- return klass;
- }
-#endif
- } while ((klass = klass->get_parent_class()) != nullptr);
-
- return nullptr;
-}
-
-MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
- bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
- ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
- "Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
-
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
-
- // Construct
- GDMonoUtils::runtime_object_init(mono_object, p_class);
-
- return mono_object;
-}
-
-MonoObject *create_managed_from(const StringName &p_from) {
- MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(StringName));
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- // Construct
- GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(StringName));
-
- CACHED_FIELD(StringName, ptr)->set_value_raw(mono_object, memnew(StringName(p_from)));
-
- return mono_object;
-}
-
-MonoObject *create_managed_from(const NodePath &p_from) {
- MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- // Construct
- GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(NodePath));
-
- CACHED_FIELD(NodePath, ptr)->set_value_raw(mono_object, memnew(NodePath(p_from)));
-
- return mono_object;
-}
-
-MonoObject *create_managed_from(const RID &p_from) {
- MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID));
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- // Construct
- GDMonoUtils::runtime_object_init(mono_object, CACHED_CLASS(RID));
-
- CACHED_FIELD(RID, ptr)->set_value_raw(mono_object, memnew(RID(p_from)));
-
- return mono_object;
-}
-
-MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- // Search constructor that takes a pointer as parameter
- MonoMethod *m;
- void *iter = nullptr;
- while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
- if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
- MonoMethodSignature *sig = mono_method_signature(m);
- void *front = nullptr;
- if (mono_signature_get_param_count(sig) == 1 &&
- mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
- break;
- }
- }
- }
-
- CRASH_COND(m == nullptr);
-
- Array *new_array = memnew(Array(p_from));
- void *args[1] = { &new_array };
-
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
- MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
- ERR_FAIL_NULL_V(mono_object, nullptr);
-
- // Search constructor that takes a pointer as parameter
- MonoMethod *m;
- void *iter = nullptr;
- while ((m = mono_class_get_methods(p_class->get_mono_ptr(), &iter))) {
- if (strcmp(mono_method_get_name(m), ".ctor") == 0) {
- MonoMethodSignature *sig = mono_method_signature(m);
- void *front = nullptr;
- if (mono_signature_get_param_count(sig) == 1 &&
- mono_class_from_mono_type(mono_signature_get_params(sig, &front)) == CACHED_CLASS(IntPtr)->get_mono_ptr()) {
- break;
- }
- }
- }
-
- CRASH_COND(m == nullptr);
-
- Dictionary *new_dict = memnew(Dictionary(p_from));
- void *args[1] = { &new_dict };
-
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
- UNHANDLED_EXCEPTION(exc);
-
- return mono_object;
-}
-
-MonoDomain *create_domain(const String &p_friendly_name) {
- print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
-
- MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr);
-
- if (domain) {
- // Workaround to avoid this exception:
- // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
- // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
- mono_domain_set_config(domain, ".", "");
- }
-
- return domain;
-}
-
-String get_type_desc(MonoType *p_type) {
- return mono_type_full_name(p_type);
-}
-
-String get_type_desc(MonoReflectionType *p_reftype) {
- return get_type_desc(mono_reflection_type_get_type(p_reftype));
-}
-
-String get_exception_name_and_message(MonoException *p_exc) {
- String res;
-
- MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
- MonoType *type = mono_class_get_type(klass);
-
- char *full_name = mono_type_full_name(type);
- res += full_name;
- mono_free(full_name);
-
- res += ": ";
-
- MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
- MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr);
- res += GDMonoMarshal::mono_string_to_godot(msg);
-
- return res;
-}
-
-void debug_print_unhandled_exception(MonoException *p_exc) {
- print_unhandled_exception(p_exc);
- debug_send_unhandled_exception_error(p_exc);
-}
-
-void debug_send_unhandled_exception_error(MonoException *p_exc) {
-#ifdef DEBUG_ENABLED
- if (!EngineDebugger::is_active()) {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
- }
-#endif
- return;
- }
-
- static thread_local bool _recursion_flag_ = false;
- if (_recursion_flag_) {
- return;
- }
- _recursion_flag_ = true;
- SCOPE_EXIT { _recursion_flag_ = false; };
-
- ScriptLanguage::StackInfo separator;
- separator.file = String();
- separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
- separator.line = 0;
-
- Vector<ScriptLanguage::StackInfo> si;
- String exc_msg;
-
- while (p_exc != nullptr) {
- GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
- MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
-
- MonoBoolean need_file_info = true;
- void *ctor_args[2] = { p_exc, &need_file_info };
-
- MonoException *unexpected_exc = nullptr;
- CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
-
- if (unexpected_exc) {
- GDMonoInternals::unhandled_exception(unexpected_exc);
- return;
- }
-
- Vector<ScriptLanguage::StackInfo> _si;
- if (stack_trace != nullptr) {
- _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
- for (int i = _si.size() - 1; i >= 0; i--) {
- si.insert(0, _si[i]);
- }
- }
-
- exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
-
- GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
- GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
- CRASH_COND(inner_exc_prop == nullptr);
-
- MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
- if (inner_exc != nullptr) {
- si.insert(0, separator);
- }
-
- p_exc = (MonoException *)inner_exc;
- }
-
- String file = si.size() ? si[0].file : __FILE__;
- String func = si.size() ? si[0].func : FUNCTION_STR;
- int line = si.size() ? si[0].line : __LINE__;
- String error_msg = "Unhandled exception";
-
- EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si);
-#endif
-}
-
-void debug_unhandled_exception(MonoException *p_exc) {
- GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
-}
-
-void print_unhandled_exception(MonoException *p_exc) {
- mono_print_unhandled_exception((MonoObject *)p_exc);
-}
-
-void set_pending_exception(MonoException *p_exc) {
-#ifdef NO_PENDING_EXCEPTIONS
- debug_unhandled_exception(p_exc);
-#else
- if (get_runtime_invoke_count() == 0) {
- debug_unhandled_exception(p_exc);
- return;
- }
-
- if (!mono_runtime_set_pending_exception(p_exc, false)) {
- ERR_PRINT("Exception thrown from managed code, but it could not be set as pending:");
- GDMonoUtils::debug_print_unhandled_exception(p_exc);
- }
-#endif
-}
-
-thread_local int current_invoke_count = 0;
-
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
-MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
-}
-
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
-uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) {
- r_error = false;
- switch (mono_type_get_type(p_enum_basetype)) {
- case MONO_TYPE_BOOLEAN:
- return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0;
- case MONO_TYPE_CHAR:
- return GDMonoMarshal::unbox<uint16_t>(p_boxed);
- case MONO_TYPE_U1:
- return GDMonoMarshal::unbox<uint8_t>(p_boxed);
- case MONO_TYPE_U2:
- return GDMonoMarshal::unbox<uint16_t>(p_boxed);
- case MONO_TYPE_U4:
- return GDMonoMarshal::unbox<uint32_t>(p_boxed);
- case MONO_TYPE_U8:
- return GDMonoMarshal::unbox<uint64_t>(p_boxed);
- case MONO_TYPE_I1:
- return GDMonoMarshal::unbox<int8_t>(p_boxed);
- case MONO_TYPE_I2:
- return GDMonoMarshal::unbox<int16_t>(p_boxed);
- case MONO_TYPE_I4:
- return GDMonoMarshal::unbox<int32_t>(p_boxed);
- case MONO_TYPE_I8:
- return GDMonoMarshal::unbox<int64_t>(p_boxed);
- default:
- r_error = true;
- return 0;
- }
-}
-
-void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
- CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc);
-}
-
-namespace Marshal {
-
-#ifdef MONO_GLUE_ENABLED
-#ifdef TOOLS_ENABLED
-#define NO_GLUE_RET(m_ret) \
- { \
- if (!GDMonoCache::cached_data.godot_api_cache_updated) \
- return m_ret; \
- }
-#else
-#define NO_GLUE_RET(m_ret) \
- {}
-#endif
-#else
-#define NO_GLUE_RET(m_ret) \
- { return m_ret; }
-#endif
-
-bool type_is_generic_array(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_system_generic_list(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_icollection(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return (bool)res;
-}
-
-bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
- NO_GLUE_RET(false);
- MonoException *exc = nullptr;
- MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- 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);
- UNHANDLED_EXCEPTION(exc);
-}
-
-void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
- MonoException *exc = nullptr;
- CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
-}
-
-void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
- MonoException *exc = nullptr;
- CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
-}
-
-GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
- NO_GLUE_RET(nullptr);
- MonoException *exc = nullptr;
- MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType).invoke(p_elem_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
-}
-
-GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
- NO_GLUE_RET(nullptr);
- MonoException *exc = nullptr;
- MonoReflectionType *reftype = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType).invoke(p_key_reftype, p_value_reftype, &exc);
- UNHANDLED_EXCEPTION(exc);
- return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
-}
-} // namespace Marshal
-
-ScopeThreadAttach::ScopeThreadAttach() {
- if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
- mono_thread = GDMonoUtils::attach_current_thread();
- }
-}
-
-ScopeThreadAttach::~ScopeThreadAttach() {
- if (unlikely(mono_thread)) {
- GDMonoUtils::detach_current_thread(mono_thread);
- }
-}
-
-StringName get_native_godot_class_name(GDMonoClass *p_class) {
- MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr);
- StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
- return ptr ? *ptr : StringName();
-}
-} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
deleted file mode 100644
index 300cacfa4b..0000000000
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*************************************************************************/
-/* gd_mono_utils.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 GD_MONO_UTILS_H
-#define GD_MONO_UTILS_H
-
-#include <mono/metadata/threads.h>
-
-#include "../mono_gc_handle.h"
-#include "../utils/macros.h"
-#include "gd_mono_header.h"
-#ifdef JAVASCRIPT_ENABLED
-#include "gd_mono_wasm_m2n.h"
-#endif
-
-#include "core/object/class_db.h"
-#include "core/object/ref_counted.h"
-
-#define UNHANDLED_EXCEPTION(m_exc) \
- if (unlikely(m_exc != nullptr)) { \
- GDMonoUtils::debug_unhandled_exception(m_exc); \
- GD_UNREACHABLE(); \
- } else \
- ((void)0)
-
-namespace GDMonoUtils {
-
-namespace Marshal {
-
-bool type_is_generic_array(MonoReflectionType *p_reftype);
-bool type_is_generic_dictionary(MonoReflectionType *p_reftype);
-bool type_is_system_generic_list(MonoReflectionType *p_reftype);
-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);
-
-void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
-void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
-
-GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
-GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-} // namespace Marshal
-
-_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
- p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
-}
-
-/**
- * If the object has a csharp script, returns the target of the gchandle stored in the script instance
- * Otherwise returns a newly constructed MonoObject* which is attached to the object
- * Returns nullptr on error
- */
-MonoObject *unmanaged_get_managed(Object *unmanaged);
-
-void set_main_thread(MonoThread *p_thread);
-MonoThread *attach_current_thread();
-void detach_current_thread();
-void detach_current_thread(MonoThread *p_mono_thread);
-MonoThread *get_current_thread();
-bool is_thread_attached();
-
-uint32_t new_strong_gchandle(MonoObject *p_object);
-uint32_t new_strong_gchandle_pinned(MonoObject *p_object);
-uint32_t new_weak_gchandle(MonoObject *p_object);
-void free_gchandle(uint32_t p_gchandle);
-
-void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc = nullptr);
-
-bool mono_delegate_equal(MonoDelegate *p_a, MonoDelegate *p_b);
-
-GDMonoClass *get_object_class(MonoObject *p_object);
-GDMonoClass *type_get_proxy_class(const StringName &p_type);
-GDMonoClass *get_class_native_base(GDMonoClass *p_class);
-
-MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object);
-
-MonoObject *create_managed_from(const StringName &p_from);
-MonoObject *create_managed_from(const NodePath &p_from);
-MonoObject *create_managed_from(const RID &p_from);
-MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class);
-MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class);
-
-MonoDomain *create_domain(const String &p_friendly_name);
-
-String get_type_desc(MonoType *p_type);
-String get_type_desc(MonoReflectionType *p_reftype);
-
-String get_exception_name_and_message(MonoException *p_exc);
-
-void debug_print_unhandled_exception(MonoException *p_exc);
-void debug_send_unhandled_exception_error(MonoException *p_exc);
-void debug_unhandled_exception(MonoException *p_exc);
-void print_unhandled_exception(MonoException *p_exc);
-
-/**
- * Sets the exception as pending. The exception will be thrown when returning to managed code.
- * If no managed method is being invoked by the runtime, the exception will be treated as
- * an unhandled exception and the method will not return.
- */
-void set_pending_exception(MonoException *p_exc);
-
-extern thread_local int current_invoke_count;
-
-_FORCE_INLINE_ int get_runtime_invoke_count() {
- return current_invoke_count;
-}
-
-_FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
- return current_invoke_count;
-}
-
-MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
-
-MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
-
-void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
-MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc);
-
-uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error);
-
-void dispose(MonoObject *p_mono_object, MonoException **r_exc);
-
-struct ScopeThreadAttach {
- ScopeThreadAttach();
- ~ScopeThreadAttach();
-
-private:
- MonoThread *mono_thread = nullptr;
-};
-
-StringName get_native_godot_class_name(GDMonoClass *p_class);
-
-template <typename... P>
-void add_internal_call(const char *p_name, void (*p_func)(P...)) {
-#ifdef JAVASCRIPT_ENABLED
- GDMonoWasmM2n::ICallTrampolines<P...>::add();
-#endif
- mono_add_internal_call(p_name, (void *)p_func);
-}
-
-template <typename R, typename... P>
-void add_internal_call(const char *p_name, R (*p_func)(P...)) {
-#ifdef JAVASCRIPT_ENABLED
- GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add();
-#endif
- mono_add_internal_call(p_name, (void *)p_func);
-}
-} // namespace GDMonoUtils
-
-#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
-
-#define GD_MONO_BEGIN_RUNTIME_INVOKE \
- int &_runtime_invoke_count_ref = GDMonoUtils::get_runtime_invoke_count_ref(); \
- _runtime_invoke_count_ref += 1; \
- ((void)0)
-
-#define GD_MONO_END_RUNTIME_INVOKE \
- _runtime_invoke_count_ref -= 1; \
- ((void)0)
-
-#define GD_MONO_SCOPE_THREAD_ATTACH \
- GDMonoUtils::ScopeThreadAttach __gdmono__scope__thread__attach__; \
- (void)__gdmono__scope__thread__attach__; \
- ((void)0)
-
-#ifdef DEBUG_ENABLED
-#define GD_MONO_ASSERT_THREAD_ATTACHED \
- CRASH_COND(!GDMonoUtils::is_thread_attached()); \
- ((void)0)
-#else
-#define GD_MONO_ASSERT_THREAD_ATTACHED ((void)0)
-#endif
-
-#endif // GD_MONO_UTILS_H
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
deleted file mode 100644
index dbfca2dc0c..0000000000
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* gd_mono_wasm_m2n.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 "gd_mono_wasm_m2n.h"
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/templates/oa_hash_map.h"
-
-typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs);
-
-// This extern function is implemented in our patched version of Mono
-MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook);
-
-namespace GDMonoWasmM2n {
-
-struct HashMapCookieComparator {
- static bool compare(const char *p_lhs, const char *p_rhs) {
- return strcmp(p_lhs, p_rhs) == 0;
- }
-};
-
-// The default hasher supports 'const char *' C Strings, but we need a custom comparator
-OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines;
-
-void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) {
- trampolines.set(cookies, trampoline_func);
-}
-
-mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) {
- TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie);
-
- if (!trampoline_func) {
- return false;
- }
-
- (*trampoline_func)(target_func, margs);
- return true;
-}
-
-bool initialized = false;
-
-void lazy_initialize() {
- // Doesn't need to be thread safe
- if (!initialized) {
- initialized = true;
- godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook);
- }
-}
-} // namespace GDMonoWasmM2n
-
-#endif
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
deleted file mode 100644
index 83e2750e5a..0000000000
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/*************************************************************************/
-/* gd_mono_wasm_m2n.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 GD_MONO_WASM_M2N_H
-#define GD_MONO_WASM_M2N_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/string/ustring.h"
-#include "core/typedefs.h"
-
-#include <mono/metadata/loader.h>
-#include <mono/utils/mono-publib.h>
-#include <stdexcept>
-#include <type_traits>
-
-extern "C" {
-
-struct Mono_InterpMethodArguments {
- size_t ilen;
- void **iargs;
- size_t flen;
- double *fargs = nullptr;
- void **retval;
- size_t is_float_ret;
- //#ifdef TARGET_WASM
- void *sig = nullptr;
- //#endif
-};
-} // extern "C"
-
-namespace GDMonoWasmM2n {
-
-template <typename T, size_t Size>
-struct array {
- T elems[Size];
-};
-
-template <typename T>
-constexpr char get_m2n_cookie_impl() {
-#define M2N_REG_COOKIE(m_type, m_cookie) \
- if constexpr (std::is_same_v<m_type, T>) { \
- return m_cookie; \
- }
-
- M2N_REG_COOKIE(MonoBoolean, 'I');
- M2N_REG_COOKIE(int8_t, 'I');
- M2N_REG_COOKIE(uint8_t, 'I');
- M2N_REG_COOKIE(int16_t, 'I');
- M2N_REG_COOKIE(uint16_t, 'I');
- M2N_REG_COOKIE(int32_t, 'I');
- M2N_REG_COOKIE(uint32_t, 'I');
- M2N_REG_COOKIE(int64_t, 'L');
- M2N_REG_COOKIE(uint64_t, 'L');
- M2N_REG_COOKIE(float, 'F');
- M2N_REG_COOKIE(double, 'D');
-
- if constexpr (std::is_pointer_v<T>) {
- if constexpr (sizeof(void *) == 4) {
- return 'I';
- } else {
- return 'L';
- }
- }
-
- if constexpr (std::is_void_v<T>) {
- return 'V';
- }
-
- return 'X';
-
-#undef M2N_REG_COOKIE
-}
-
-template <typename T>
-constexpr char get_m2n_cookie() {
- constexpr char cookie = get_m2n_cookie_impl<T>();
- static_assert(cookie != 'X', "Type not supported in internal call signature.");
- return cookie;
-}
-
-template <typename... T>
-constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() {
- return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' };
-}
-
-template <typename R, typename... T>
-constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() {
- return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' };
-}
-
-template <typename T>
-constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) {
- constexpr char cookie = get_m2n_cookie<T>();
-
- static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
-
- if constexpr (cookie == 'I' || cookie == 'L') {
- size_t ret = r_int_idx;
- r_int_idx += cookie == 'I' ? 1 : 2;
- return ret;
- } else {
- size_t ret = r_float_idx;
- r_float_idx += cookie == 'F' ? 1 : 2;
- return ret;
- }
-}
-
-template <typename... P>
-constexpr array<size_t, sizeof...(P)> get_indices_for_type() {
- size_t int_idx = 0;
- size_t float_idx = 0;
- return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... };
-}
-
-constexpr size_t fidx(size_t p_x) {
- if constexpr (sizeof(void *) == 4) {
- return p_x * 2;
- } else {
- return p_x;
- }
-}
-
-template <typename T>
-T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
- constexpr char cookie = get_m2n_cookie<T>();
-
- static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
-
- if constexpr (cookie == 'I') {
- return (T)(size_t)p_margs->iargs[p_idx];
- } else if constexpr (cookie == 'L') {
- static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> ||
- (sizeof(void *) == 8 && std::is_pointer_v<T>),
- "Invalid type for cookie 'L'.");
-
- union {
- T l;
- struct {
- int32_t lo;
- int32_t hi;
- } pair;
- } p;
-
- p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx];
- p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1];
-
- return p.l;
- } else if constexpr (cookie == 'F') {
- return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]);
- } else if constexpr (cookie == 'D') {
- return (T)p_margs->fargs[p_idx];
- }
-}
-
-template <typename... P, size_t... Is>
-void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
- constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
- typedef void (*Func)(P...);
- Func func = (Func)p_target_func;
- func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
-}
-
-template <typename R, typename... P, size_t... Is>
-void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
- constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
- typedef R (*Func)(P...);
- Func func = (Func)p_target_func;
- R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
- *reinterpret_cast<R *>(p_margs->retval) = res;
-}
-
-inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- typedef void (*Func)();
- Func func = (Func)p_target_func;
- func();
-}
-
-template <typename R>
-void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- typedef R (*Func)();
- Func func = (Func)p_target_func;
- R res = func();
- *reinterpret_cast<R *>(p_margs->retval) = res;
-}
-
-template <typename... P>
-void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- if constexpr (sizeof...(P) == 0) {
- m2n_trampoline_with_idx_seq_0(p_target_func, p_margs);
- } else {
- m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
- }
-}
-
-template <typename R, typename... P>
-void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
- if constexpr (sizeof...(P) == 0) {
- m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs);
- } else {
- m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
- }
-}
-
-typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs);
-
-void set_trampoline(const char *cookies, TrampolineFunc trampoline_func);
-
-void lazy_initialize();
-
-template <typename... P>
-struct ICallTrampolines {
- static constexpr auto cookies = get_m2n_cookies<P...>();
-
- static void add() {
- lazy_initialize();
- set_trampoline(cookies.elems, &m2n_trampoline<P...>);
- }
-};
-
-template <typename R, typename... P>
-struct ICallTrampolinesR {
- static constexpr auto cookies = get_m2n_cookies_r<R, P...>();
-
- static void add() {
- lazy_initialize();
- set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>);
- }
-};
-
-void initialize();
-} // namespace GDMonoWasmM2n
-
-#endif
-
-#endif // GD_MONO_WASM_M2N_H
diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h
deleted file mode 100644
index 14e8ca82b9..0000000000
--- a/modules/mono/mono_gd/i_mono_class_member.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*************************************************************************/
-/* i_mono_class_member.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 I_MONO_CLASS_MEMBER_H
-#define I_MONO_CLASS_MEMBER_H
-
-#include "gd_mono_header.h"
-
-#include <mono/metadata/object.h>
-
-class IMonoClassMember {
-public:
- enum Visibility {
- PRIVATE,
- PROTECTED_AND_INTERNAL, // FAM_AND_ASSEM
- INTERNAL, // ASSEMBLY
- PROTECTED, // FAMILY
- PUBLIC
- };
-
- enum MemberType {
- MEMBER_TYPE_FIELD,
- MEMBER_TYPE_PROPERTY,
- MEMBER_TYPE_METHOD
- };
-
- virtual ~IMonoClassMember() {}
-
- virtual GDMonoClass *get_enclosing_class() const = 0;
-
- virtual MemberType get_member_type() const = 0;
-
- virtual StringName get_name() const = 0;
-
- virtual bool is_static() = 0;
-
- virtual Visibility get_visibility() = 0;
-
- virtual bool has_attribute(GDMonoClass *p_attr_class) = 0;
- virtual MonoObject *get_attribute(GDMonoClass *p_attr_class) = 0;
-};
-
-#endif // I_MONO_CLASS_MEMBER_H
diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp
deleted file mode 100644
index 5860d7db1e..0000000000
--- a/modules/mono/mono_gd/managed_type.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*************************************************************************/
-/* managed_type.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 "managed_type.h"
-
-#include "gd_mono.h"
-#include "gd_mono_class.h"
-
-ManagedType ManagedType::from_class(GDMonoClass *p_class) {
- return ManagedType(mono_type_get_type(p_class->get_mono_type()), p_class);
-}
-
-ManagedType ManagedType::from_class(MonoClass *p_mono_class) {
- GDMonoClass *tclass = GDMono::get_singleton()->get_class(p_mono_class);
- ERR_FAIL_COND_V(!tclass, ManagedType());
-
- return ManagedType(mono_type_get_type(tclass->get_mono_type()), tclass);
-}
-
-ManagedType ManagedType::from_type(MonoType *p_mono_type) {
- MonoClass *mono_class = mono_class_from_mono_type(p_mono_type);
- GDMonoClass *tclass = GDMono::get_singleton()->get_class(mono_class);
- ERR_FAIL_COND_V(!tclass, ManagedType());
-
- return ManagedType(mono_type_get_type(p_mono_type), tclass);
-}
-
-ManagedType ManagedType::from_reftype(MonoReflectionType *p_mono_reftype) {
- MonoType *mono_type = mono_reflection_type_get_type(p_mono_reftype);
- return from_type(mono_type);
-}
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 4797d5dae1..6e8a5ce5c1 100644
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* android_support.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* android_support.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "android_support.h"
@@ -359,7 +359,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
jsize encodedLength = env->GetArrayLength(encoded);
- MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
+ MonoArray *encoded_ret = mono_array_new(mono_domain_get(), mono_get_byte_class(), encodedLength);
uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/mono/mono_gd/support/android_support.h
index 073cd31c65..5be4bac6e1 100644
--- a/modules/mono/mono_gd/support/android_support.h
+++ b/modules/mono/mono_gd/support/android_support.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* android_support.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* android_support.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ANDROID_SUPPORT_H
#define ANDROID_SUPPORT_H
diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h
index 03e86df698..cb397c8b46 100644
--- a/modules/mono/mono_gd/support/ios_support.h
+++ b/modules/mono/mono_gd/support/ios_support.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* ios_support.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* ios_support.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IOS_SUPPORT_H
#define IOS_SUPPORT_H
diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm
index 7c941b9d1e..f4abf636b1 100644
--- a/modules/mono/mono_gd/support/ios_support.mm
+++ b/modules/mono/mono_gd/support/ios_support.mm
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* ios_support.mm */
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* ios_support.mm */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "ios_support.h"
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 755e1f7a30..7dab4b037f 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h
index bc2690c277..8b194434bb 100644
--- a/modules/mono/register_types.h
+++ b/modules/mono/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_REGISTER_TYPES_H
#define MONO_REGISTER_TYPES_H
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 618e1b58e0..34b94b9755 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -1,57 +1,55 @@
-/*************************************************************************/
-/* signal_awaiter_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* signal_awaiter_utils.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "signal_awaiter_utils.h"
#include "csharp_script.h"
#include "mono_gd/gd_mono_cache.h"
-#include "mono_gd/gd_mono_class.h"
-#include "mono_gd/gd_mono_marshal.h"
-#include "mono_gd/gd_mono_utils.h"
-Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter) {
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr) {
ERR_FAIL_NULL_V(p_source, ERR_INVALID_DATA);
ERR_FAIL_NULL_V(p_target, ERR_INVALID_DATA);
// TODO: Use pooling for ManagedCallable instances.
- SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, p_awaiter, p_signal));
+ MonoGCHandleData awaiter_handle(p_awaiter_handle_ptr, gdmono::GCHandleType::STRONG_HANDLE);
+ SignalAwaiterCallable *awaiter_callable = memnew(SignalAwaiterCallable(p_target, awaiter_handle, p_signal));
Callable callable = Callable(awaiter_callable);
- return p_source->connect(p_signal, callable, Vector<Variant>(), Object::CONNECT_ONESHOT);
+ return p_source->connect(p_signal, callable, Object::CONNECT_ONE_SHOT);
}
bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
// Only called if both instances are of type SignalAwaiterCallable. Static cast is safe.
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
- return a->awaiter_handle.handle == b->awaiter_handle.handle;
+ return a->awaiter_handle.handle.value == b->awaiter_handle.handle.value;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -92,6 +90,10 @@ ObjectID SignalAwaiterCallable::get_object() const {
return target_id;
}
+StringName SignalAwaiterCallable::get_signal() const {
+ return signal;
+}
+
void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
@@ -101,38 +103,20 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va
"Resumed after await, but class instance is gone.");
#endif
- MonoArray *signal_args = nullptr;
-
- if (p_argcount > 0) {
- signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
-
- for (int i = 0; i < p_argcount; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
- mono_array_setref(signal_args, i, boxed);
- }
- }
+ bool awaiter_is_null = false;
+ GDMonoCache::managed_callbacks.SignalAwaiter_SignalCallback(awaiter_handle.get_intptr(), p_arguments, p_argcount, &awaiter_is_null);
- MonoObject *awaiter = awaiter_handle.get_target();
-
- if (!awaiter) {
+ if (awaiter_is_null) {
r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
- MonoException *exc = nullptr;
- CACHED_METHOD_THUNK(SignalAwaiter, SignalCallback).invoke(awaiter, signal_args, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL();
- } else {
- r_call_error.error = Callable::CallError::CALL_OK;
- }
+ r_call_error.error = Callable::CallError::CALL_OK;
}
-SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal) :
+SignalAwaiterCallable::SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal) :
target_id(p_target->get_instance_id()),
- awaiter_handle(MonoGCHandleData::new_strong_handle(p_awaiter)),
+ awaiter_handle(p_awaiter_handle),
signal(p_signal) {
}
@@ -148,7 +132,7 @@ bool EventSignalCallable::compare_equal(const CallableCustom *p_a, const Callabl
return false;
}
- if (a->event_signal != b->event_signal) {
+ if (a->event_signal_name != b->event_signal_name) {
return false;
}
@@ -163,7 +147,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();
+ uint32_t hash = event_signal_name.hash();
return hash_murmur3_one_64(owner->get_instance_id(), hash);
}
@@ -173,8 +157,7 @@ String EventSignalCallable::get_as_text() const {
if (script.is_valid() && script->get_path().is_resource_file()) {
class_name += "(" + script->get_path().get_file() + ")";
}
- StringName signal = event_signal->field->get_name();
- return class_name + "::EventSignalMiddleman::" + String(signal);
+ return class_name + "::EventSignalMiddleman::" + String(event_signal_name);
}
CallableCustom::CompareEqualFunc EventSignalCallable::get_compare_equal_func() const {
@@ -190,39 +173,32 @@ ObjectID EventSignalCallable::get_object() const {
}
StringName EventSignalCallable::get_signal() const {
- return event_signal->field->get_name();
+ return event_signal_name;
}
void EventSignalCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
- ERR_FAIL_COND(p_argcount < event_signal->invoke_method->get_parameters_count());
-
CSharpInstance *csharp_instance = CAST_CSHARP_INSTANCE(owner->get_script_instance());
ERR_FAIL_NULL(csharp_instance);
- MonoObject *owner_managed = csharp_instance->get_mono_object();
- ERR_FAIL_NULL(owner_managed);
+ GCHandleIntPtr owner_gchandle_intptr = csharp_instance->get_gchandle_intptr();
+
+ bool awaiter_is_null = false;
+ GDMonoCache::managed_callbacks.ScriptManagerBridge_RaiseEventSignal(
+ owner_gchandle_intptr, &event_signal_name,
+ p_arguments, p_argcount, &awaiter_is_null);
- MonoObject *delegate_field_value = event_signal->field->get_value(owner_managed);
- if (!delegate_field_value) {
- r_call_error.error = Callable::CallError::CALL_OK;
+ if (awaiter_is_null) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return;
}
- MonoException *exc = nullptr;
- event_signal->invoke_method->invoke(delegate_field_value, p_arguments, &exc);
-
- if (exc) {
- GDMonoUtils::set_pending_exception(exc);
- ERR_FAIL();
- } else {
- r_call_error.error = Callable::CallError::CALL_OK;
- }
+ r_call_error.error = Callable::CallError::CALL_OK;
}
-EventSignalCallable::EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal) :
+EventSignalCallable::EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name) :
owner(p_owner),
- event_signal(p_event_signal) {
+ event_signal_name(p_event_signal_name) {
}
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index 532aa3e327..635771f3dc 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* signal_awaiter_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* signal_awaiter_utils.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 SIGNAL_AWAITER_UTILS_H
#define SIGNAL_AWAITER_UTILS_H
@@ -36,9 +36,14 @@
#include "csharp_script.h"
#include "mono_gc_handle.h"
-Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, MonoObject *p_awaiter);
+Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signal, Object *p_target, GCHandleIntPtr p_awaiter_handle_ptr);
-class SignalAwaiterCallable : public CallableCustom {
+class BaseSignalCallable : public CallableCustom {
+public:
+ virtual StringName get_signal() const = 0;
+};
+
+class SignalAwaiterCallable : public BaseSignalCallable {
ObjectID target_id;
MonoGCHandleData awaiter_handle;
StringName signal;
@@ -59,17 +64,17 @@ public:
ObjectID get_object() const override;
- _FORCE_INLINE_ StringName get_signal() const { return signal; }
+ StringName get_signal() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
- SignalAwaiterCallable(Object *p_target, MonoObject *p_awaiter, const StringName &p_signal);
+ SignalAwaiterCallable(Object *p_target, MonoGCHandleData p_awaiter_handle, const StringName &p_signal);
~SignalAwaiterCallable();
};
-class EventSignalCallable : public CallableCustom {
+class EventSignalCallable : public BaseSignalCallable {
Object *owner = nullptr;
- const CSharpScript::EventSignal *event_signal;
+ StringName event_signal_name;
public:
static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
@@ -87,11 +92,11 @@ public:
ObjectID get_object() const override;
- StringName get_signal() const;
+ StringName get_signal() const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
- EventSignalCallable(Object *p_owner, const CSharpScript::EventSignal *p_event_signal);
+ EventSignalCallable(Object *p_owner, const StringName &p_event_signal_name);
};
#endif // SIGNAL_AWAITER_UTILS_H
diff --git a/modules/mono/thirdparty/coreclr_delegates.h b/modules/mono/thirdparty/coreclr_delegates.h
new file mode 100644
index 0000000000..914ab592df
--- /dev/null
+++ b/modules/mono/thirdparty/coreclr_delegates.h
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __CORECLR_DELEGATES_H__
+#define __CORECLR_DELEGATES_H__
+
+#include <stdint.h>
+
+#if defined(_WIN32)
+ #define CORECLR_DELEGATE_CALLTYPE __stdcall
+ #ifdef _WCHAR_T_DEFINED
+ typedef wchar_t char_t;
+ #else
+ typedef unsigned short char_t;
+ #endif
+#else
+ #define CORECLR_DELEGATE_CALLTYPE
+ typedef char char_t;
+#endif
+
+#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1)
+
+// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer
+typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_and_get_function_pointer_fn)(
+ const char_t *assembly_path /* Fully qualified path to assembly */,
+ const char_t *type_name /* Assembly qualified type name */,
+ const char_t *method_name /* Public static method name compatible with delegateType */,
+ const char_t *delegate_type_name /* Assembly qualified delegate type name or null
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void *reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void **delegate /* Pointer where to store the function pointer result */);
+
+// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default)
+typedef int (CORECLR_DELEGATE_CALLTYPE *component_entry_point_fn)(void *arg, int32_t arg_size_in_bytes);
+
+typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)(
+ const char_t *type_name /* Assembly qualified type name */,
+ const char_t *method_name /* Public static method name compatible with delegateType */,
+ const char_t *delegate_type_name /* Assembly qualified delegate type name or null,
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void *load_context /* Extensibility parameter (currently unused and must be 0) */,
+ void *reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void **delegate /* Pointer where to store the function pointer result */);
+
+#endif // __CORECLR_DELEGATES_H__
diff --git a/modules/mono/thirdparty/hostfxr.h b/modules/mono/thirdparty/hostfxr.h
new file mode 100644
index 0000000000..591a8ebbea
--- /dev/null
+++ b/modules/mono/thirdparty/hostfxr.h
@@ -0,0 +1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __HOSTFXR_H__
+#define __HOSTFXR_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+ #define HOSTFXR_CALLTYPE __cdecl
+ #ifdef _WCHAR_T_DEFINED
+ typedef wchar_t char_t;
+ #else
+ typedef unsigned short char_t;
+ #endif
+#else
+ #define HOSTFXR_CALLTYPE
+ typedef char char_t;
+#endif
+
+enum hostfxr_delegate_type
+{
+ hdt_com_activation,
+ hdt_load_in_memory_assembly,
+ hdt_winrt_activation,
+ hdt_com_register,
+ hdt_com_unregister,
+ hdt_load_assembly_and_get_function_pointer,
+ hdt_get_function_pointer,
+};
+
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv);
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_startupinfo_fn)(
+ const int argc,
+ const char_t **argv,
+ const char_t *host_path,
+ const char_t *dotnet_root,
+ const char_t *app_path);
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)(
+ const int argc,
+ const char_t** argv,
+ const char_t* host_path,
+ const char_t* dotnet_root,
+ const char_t* app_path,
+ int64_t bundle_header_offset);
+
+typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message);
+
+//
+// Sets a callback which is to be used to write errors to.
+//
+// Parameters:
+// error_writer
+// A callback function which will be invoked every time an error is to be reported.
+// Or nullptr to unregister previously registered callback and return to the default behavior.
+// Return value:
+// The previously registered callback (which is now unregistered), or nullptr if no previous callback
+// was registered
+//
+// The error writer is registered per-thread, so the registration is thread-local. On each thread
+// only one callback can be registered. Subsequent registrations overwrite the previous ones.
+//
+// By default no callback is registered in which case the errors are written to stderr.
+//
+// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
+// Multiple calls to the error writer may occur for one failure.
+//
+// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
+// will be propagated to hostpolicy for the duration of the call. This means that errors from
+// both hostfxr and hostpolicy will be reporter through the same error writer.
+//
+typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE *hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer);
+
+typedef void* hostfxr_handle;
+struct hostfxr_initialize_parameters
+{
+ size_t size;
+ const char_t *host_path;
+ const char_t *dotnet_root;
+};
+
+//
+// Initializes the hosting components for a dotnet command line running an application
+//
+// Parameters:
+// argc
+// Number of argv arguments
+// argv
+// Command-line arguments for running an application (as if through the dotnet executable).
+// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported.
+// For example 'app.dll app_argument_1 app_argument_2`.
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// HostInvalidState - Hosting components are already initialized
+//
+// This function parses the specified command-line arguments to determine the application to run. It will
+// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and
+// dependencies and prepare everything needed to load the runtime.
+//
+// This function only supports arguments for running an application. It does not support SDK commands.
+//
+// This function does not load the runtime.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)(
+ int argc,
+ const char_t **argv,
+ const struct hostfxr_initialize_parameters *parameters,
+ /*out*/ hostfxr_handle *host_context_handle);
+
+//
+// Initializes the hosting components using a .runtimeconfig.json file
+//
+// Parameters:
+// runtime_config_path
+// Path to the .runtimeconfig.json file
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
+// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
+// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
+//
+// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
+// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
+// may be next to the .runtimeconfig.json).
+//
+// This function does not load the runtime.
+//
+// If called when the runtime has already been loaded, this function will check if the specified runtime
+// config is compatible with the existing runtime.
+//
+// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful
+// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that
+// the difference in properties is acceptable.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_runtime_config_fn)(
+ const char_t *runtime_config_path,
+ const struct hostfxr_initialize_parameters *parameters,
+ /*out*/ hostfxr_handle *host_context_handle);
+
+//
+// Gets the runtime property value for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Out parameter. Pointer to a buffer with the property value.
+//
+// Return value:
+// The error code result.
+//
+// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// property value for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t *name,
+ /*out*/ const char_t **value);
+
+//
+// Sets the value of a runtime property for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Value to set
+//
+// Return value:
+// The error code result.
+//
+// Setting properties is only supported for the first host context, before the runtime has been loaded.
+//
+// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
+// property will be removed.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_set_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t *name,
+ const char_t *value);
+
+//
+// Gets all the runtime properties for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// count
+// [in] Size of the keys and values buffers
+// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
+// small or keys/values is nullptr, this is populated with the number of available properties
+// keys
+// Array of pointers to buffers with runtime property keys
+// values
+// Array of pointers to buffers with runtime property values
+//
+// Return value:
+// The error code result.
+//
+// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// properties for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_properties_fn)(
+ const hostfxr_handle host_context_handle,
+ /*inout*/ size_t * count,
+ /*out*/ const char_t **keys,
+ /*out*/ const char_t **values);
+
+//
+// Load CoreCLR and run the application for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
+//
+// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line.
+//
+// This function will not return until the managed application exits.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_run_app_fn)(const hostfxr_handle host_context_handle);
+
+//
+// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// type
+// Type of runtime delegate requested
+// delegate
+// An out parameter that will be assigned the delegate.
+//
+// Return value:
+// The error code result.
+//
+// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config,
+// then all delegate types are supported.
+// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line,
+// then only the following delegate types are currently supported:
+// hdt_load_assembly_and_get_function_pointer
+// hdt_get_function_pointer
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)(
+ const hostfxr_handle host_context_handle,
+ enum hostfxr_delegate_type type,
+ /*out*/ void **delegate);
+
+//
+// Closes an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// The error code result.
+//
+typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle);
+
+struct hostfxr_dotnet_environment_sdk_info
+{
+ size_t size;
+ const char_t* version;
+ const char_t* path;
+};
+
+typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
+ const struct hostfxr_dotnet_environment_info* info,
+ void* result_context);
+
+struct hostfxr_dotnet_environment_framework_info
+{
+ size_t size;
+ const char_t* name;
+ const char_t* version;
+ const char_t* path;
+};
+
+struct hostfxr_dotnet_environment_info
+{
+ size_t size;
+
+ const char_t* hostfxr_version;
+ const char_t* hostfxr_commit_hash;
+
+ size_t sdk_count;
+ const struct hostfxr_dotnet_environment_sdk_info* sdks;
+
+ size_t framework_count;
+ const struct hostfxr_dotnet_environment_framework_info* frameworks;
+};
+
+#endif //__HOSTFXR_H__
diff --git a/modules/mono/utils/macos_utils.cpp b/modules/mono/utils/macos_utils.cpp
index cd4f7a827e..8563c92dd8 100644
--- a/modules/mono/utils/macos_utils.cpp
+++ b/modules/mono/utils/macos_utils.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* macos_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* macos_utils.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "macos_utils.h"
diff --git a/modules/mono/utils/macos_utils.h b/modules/mono/utils/macos_utils.h
index ca4957f5a7..dfe917db20 100644
--- a/modules/mono/utils/macos_utils.h
+++ b/modules/mono/utils/macos_utils.h
@@ -1,40 +1,40 @@
-/*************************************************************************/
-/* macos_utils.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. */
-/*************************************************************************/
-
-#include "core/string/ustring.h"
+/**************************************************************************/
+/* macos_utils.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_MACOS_UTILS_H
#define MONO_MACOS_UTILS_H
#ifdef MACOS_ENABLED
+#include "core/string/ustring.h"
+
bool macos_is_app_bundle_installed(const String &p_bundle_id);
#endif
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index b7bd9a2495..1e073ccb5e 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* macros.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* macros.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_MACROS_H
#define MONO_MACROS_H
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
deleted file mode 100644
index 8e37e6943c..0000000000
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*************************************************************************/
-/* mono_reg_utils.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 "mono_reg_utils.h"
-#include "core/io/dir_access.h"
-
-#ifdef WINDOWS_ENABLED
-
-#include "core/os/os.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-namespace MonoRegUtils {
-
-template <int>
-REGSAM bitness_sam_impl();
-
-template <>
-REGSAM bitness_sam_impl<4>() {
- return KEY_WOW64_64KEY;
-}
-
-template <>
-REGSAM bitness_sam_impl<8>() {
- return KEY_WOW64_32KEY;
-}
-
-REGSAM _get_bitness_sam() {
- return bitness_sam_impl<sizeof(size_t)>();
-}
-
-LONG _RegOpenKey(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult) {
- LONG res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ, phkResult);
-
- if (res != ERROR_SUCCESS) {
- res = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_READ | _get_bitness_sam(), phkResult);
- }
-
- return res;
-}
-
-LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value) {
- Vector<WCHAR> buffer;
- buffer.resize(512);
- DWORD dwBufferSize = buffer.size();
-
- LONG res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
-
- if (res == ERROR_MORE_DATA) {
- // dwBufferSize now contains the actual size
- buffer.resize(dwBufferSize);
- res = RegQueryValueExW(hKey, (LPCWSTR)(p_value_name.utf16().get_data()), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);
- }
-
- if (res == ERROR_SUCCESS) {
- r_value = String(buffer.ptr(), buffer.size());
- } else {
- r_value = String();
- }
-
- return res;
-}
-
-LONG _find_mono_in_reg(const String &p_subkey, MonoRegInfo &r_info, bool p_old_reg = false) {
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- if (!p_old_reg) {
- res = _RegKeyQueryString(hKey, "Version", r_info.version);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
- }
-
- res = _RegKeyQueryString(hKey, "SdkInstallRoot", r_info.install_root_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "FrameworkAssemblyDirectory", r_info.assembly_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "MonoConfigDir", r_info.config_dir);
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- if (r_info.install_root_dir.ends_with("\\")) {
- r_info.bin_dir = r_info.install_root_dir + "bin";
- } else {
- r_info.bin_dir = r_info.install_root_dir + "\\bin";
- }
-
-cleanup:
- RegCloseKey(hKey);
- return res;
-}
-
-LONG _find_mono_in_reg_old(const String &p_subkey, MonoRegInfo &r_info) {
- String default_clr;
-
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, (LPCWSTR)(p_subkey.utf16().get_data()), &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "DefaultCLR", default_clr);
-
- if (res == ERROR_SUCCESS && default_clr.length()) {
- r_info.version = default_clr;
- res = _find_mono_in_reg(p_subkey + "\\" + default_clr, r_info, true);
- }
-
-cleanup:
- RegCloseKey(hKey);
- return res;
-}
-
-MonoRegInfo find_mono() {
- MonoRegInfo info;
-
- if (_find_mono_in_reg("Software\\Mono", info) == ERROR_SUCCESS) {
- return info;
- }
-
- if (_find_mono_in_reg_old("Software\\Novell\\Mono", info) == ERROR_SUCCESS) {
- return info;
- }
-
- return MonoRegInfo();
-}
-
-String find_msbuild_tools_path() {
- String msbuild_tools_path;
-
- // Try to find 15.0 with vswhere
-
- String vswhere_path = OS::get_singleton()->get_environment(sizeof(size_t) == 8 ? "ProgramFiles(x86)" : "ProgramFiles");
- vswhere_path += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
-
- List<String> vswhere_args;
- vswhere_args.push_back("-latest");
- vswhere_args.push_back("-products");
- vswhere_args.push_back("*");
- vswhere_args.push_back("-requires");
- vswhere_args.push_back("Microsoft.Component.MSBuild");
-
- String output;
- int exit_code;
- OS::get_singleton()->execute(vswhere_path, vswhere_args, &output, &exit_code);
-
- if (exit_code == 0) {
- Vector<String> lines = output.split("\n");
-
- for (int i = 0; i < lines.size(); i++) {
- const String &line = lines[i];
- int sep_idx = line.find(":");
-
- if (sep_idx > 0) {
- String key = line.substr(0, sep_idx); // No need to trim
-
- if (key == "installationPath") {
- String val = line.substr(sep_idx + 1, line.length()).strip_edges();
-
- ERR_BREAK(val.is_empty());
-
- if (!val.ends_with("\\")) {
- val += "\\";
- }
-
- // Since VS2019, the directory is simply named "Current"
- String msbuild_dir = val + "MSBuild\\Current\\Bin";
- if (DirAccess::exists(msbuild_dir)) {
- return msbuild_dir;
- }
-
- // Directory name "15.0" is used in VS 2017
- return val + "MSBuild\\15.0\\Bin";
- }
- }
- }
- }
-
- // Try to find 14.0 in the Registry
-
- HKEY hKey;
- LONG res = _RegOpenKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\14.0", &hKey);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
- res = _RegKeyQueryString(hKey, "MSBuildToolsPath", msbuild_tools_path);
-
- if (res != ERROR_SUCCESS) {
- goto cleanup;
- }
-
-cleanup:
- RegCloseKey(hKey);
-
- return msbuild_tools_path;
-}
-} // namespace MonoRegUtils
-
-#endif // WINDOWS_ENABLED
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index a1905dfcfe..b5a3816ed7 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* path_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* path_utils.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "path_utils.h"
@@ -51,6 +51,37 @@
namespace path {
+String find_executable(const String &p_name) {
+#ifdef WINDOWS_ENABLED
+ Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false);
+#endif
+ Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false);
+
+ if (env_path.is_empty()) {
+ return String();
+ }
+
+ for (int i = 0; i < env_path.size(); i++) {
+ String p = path::join(env_path[i], p_name);
+
+#ifdef WINDOWS_ENABLED
+ for (int j = 0; j < exts.size(); j++) {
+ String p2 = p + exts[j].to_lower(); // lowercase to reduce risk of case mismatch warning
+
+ if (FileAccess::exists(p2)) {
+ return p2;
+ }
+ }
+#else
+ if (FileAccess::exists(p)) {
+ return p;
+ }
+#endif
+ }
+
+ return String();
+}
+
String cwd() {
#ifdef WINDOWS_ENABLED
const DWORD expected_size = ::GetCurrentDirectoryW(0, nullptr);
@@ -174,7 +205,7 @@ String relative_to_impl(const String &p_path, const String &p_relative_to) {
return p_path;
}
- return String("..").plus_file(relative_to_impl(p_path, base_dir));
+ return String("..").path_join(relative_to_impl(p_path, base_dir));
}
}
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index 9a2c757361..25d130fc58 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* path_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* path_utils.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_PATH_UTILS_H
#define MONO_PATH_UTILS_H
@@ -36,6 +36,8 @@
namespace path {
+String find_executable(const String &p_name);
+
String join(const String &p_a, const String &p_b);
String join(const String &p_a, const String &p_b, const String &p_c);
String join(const String &p_a, const String &p_b, const String &p_c, const String &p_d);
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 975f2d8332..c732d066d9 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* string_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* string_utils.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "string_utils.h"
@@ -65,7 +65,7 @@ int sfind(const String &p_text, int p_from) {
break;
case 1: {
char32_t c = src[read_pos];
- found = src[read_pos] == 's' || (c >= '0' && c <= '4');
+ found = src[read_pos] == 's' || (c >= '0' && c <= '5');
break;
}
default:
@@ -86,32 +86,13 @@ int sfind(const String &p_text, int p_from) {
}
} // namespace
-String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) {
+String sformat(const String &p_text, const String &p1, const String &p2,
+ const String &p3, const String &p4, const String &p5, const String &p6) {
if (p_text.length() < 2) {
return p_text;
}
- Array args;
-
- if (p1.get_type() != Variant::NIL) {
- args.push_back(p1);
-
- if (p2.get_type() != Variant::NIL) {
- args.push_back(p2);
-
- if (p3.get_type() != Variant::NIL) {
- args.push_back(p3);
-
- if (p4.get_type() != Variant::NIL) {
- args.push_back(p4);
-
- if (p5.get_type() != Variant::NIL) {
- args.push_back(p5);
- }
- }
- }
- }
- }
+ String args[6] = { p1, p2, p3, p4, p5, p6 };
String new_string;
@@ -125,7 +106,7 @@ String sformat(const String &p_text, const Variant &p1, const Variant &p2, const
int req_index = (c == 's' ? findex++ : c - '0');
new_string += p_text.substr(search_from, result - search_from);
- new_string += args[req_index].operator String();
+ new_string += args[req_index];
search_from = result + 2;
}
diff --git a/modules/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index fa4c5e89f4..c261aaa7f7 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* string_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* string_utils.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MONO_STRING_UTILS_H
#define MONO_STRING_UTILS_H
@@ -36,7 +36,8 @@
#include <stdarg.h>
-String sformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant());
+String sformat(const String &p_text, const String &p1 = String(), const String &p2 = String(),
+ const String &p3 = String(), const String &p4 = String(), const String &p5 = String(), const String &p6 = String());
#ifdef TOOLS_ENABLED
bool is_csharp_keyword(const String &p_name);
diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub
index 227369f4bd..0c269bc7f4 100644
--- a/modules/msdfgen/SCsub
+++ b/modules/msdfgen/SCsub
@@ -36,7 +36,7 @@ if env["builtin_msdfgen"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"])
+ env_msdfgen.Prepend(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"])
lib = env_msdfgen.add_library("msdfgen_builtin", thirdparty_sources)
thirdparty_obj += lib
diff --git a/modules/msdfgen/config.py b/modules/msdfgen/config.py
index 653e466a74..631894400d 100644
--- a/modules/msdfgen/config.py
+++ b/modules/msdfgen/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("msdfgen", ["freetype"])
+ env.module_add_dependencies("msdfgen", ["freetype"])
+ return True
def configure(env):
diff --git a/modules/msdfgen/register_types.cpp b/modules/msdfgen/register_types.cpp
index 2d3a2a0c69..f918802018 100644
--- a/modules/msdfgen/register_types.cpp
+++ b/modules/msdfgen/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/msdfgen/register_types.h b/modules/msdfgen/register_types.h
index 749204f390..ff8d03555c 100644
--- a/modules/msdfgen/register_types.h
+++ b/modules/msdfgen/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 MSDFGEN_REGISTER_TYPES_H
#define MSDFGEN_REGISTER_TYPES_H
diff --git a/modules/multiplayer/SCsub b/modules/multiplayer/SCsub
index ff33655537..e89038c3e0 100644
--- a/modules/multiplayer/SCsub
+++ b/modules/multiplayer/SCsub
@@ -8,7 +8,7 @@ env_mp = env_modules.Clone()
module_obj = []
env_mp.add_source_files(module_obj, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_mp.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index 44ab34f52c..b6a31cf542 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -1,65 +1,75 @@
<?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>
+ Automatically replicates spawnable nodes from the authority to other multiplayer peers.
</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.
+ Spawnable scenes can be configured in the editor or through code (see [method add_spawnable_scene]).
+ Also supports custom node spawns through [method spawn], calling [member spawn_function] on all peers.
+ Internally, [MultiplayerSpawner] 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" />
+ <param index="0" name="path" type="String" />
<description>
+ Adds a scene path to spawnable scenes, making it automatically replicated from the multiplayer authority to other peers when added as children of the node pointed by [member spawn_path].
</description>
</method>
<method name="clear_spawnable_scenes">
<return type="void" />
<description>
+ Clears all spawnable scenes. Does not despawn existing instances on remote peers.
</description>
</method>
<method name="get_spawnable_scene" qualifiers="const">
<return type="String" />
- <argument index="0" name="path" type="int" />
+ <param index="0" name="index" type="int" />
<description>
+ Returns the spawnable scene path by index.
</description>
</method>
<method name="get_spawnable_scene_count" qualifiers="const">
<return type="int" />
<description>
+ Returns the count of spawnable scene paths.
</description>
</method>
<method name="spawn">
<return type="Node" />
- <argument index="0" name="data" type="Variant" default="null" />
+ <param index="0" name="data" type="Variant" default="null" />
<description>
+ Requests a custom spawn, with [code]data[/code] passed to [member spawn_function] on all peers. Returns the locally spawned node instance already inside the scene tree, and added as a child of the node pointed by [member spawn_path].
+ [b]Note:[/b] Spawnable scenes are spawned automatically. [method spawn] is only needed for custom spawns.
</description>
</method>
</methods>
<members>
+ <member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function">
+ Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
+ [b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
+ </member>
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
+ Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
+ When set to [code]0[/code] (the default), there is no limit.
</member>
<member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
+ Path to the spawn root. Spawnable scenes that are added as direct children are replicated to other peers.
</member>
</members>
<signals>
<signal name="despawned">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
+ <param index="0" name="node" type="Node" />
<description>
+ Emitted when a spawnable scene or custom spawn was despawned by the multiplayer authority. Only called on puppets.
</description>
</signal>
<signal name="spawned">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
+ <param index="0" name="node" type="Node" />
<description>
+ Emitted when a spawnable scene or custom spawn was spawned by the multiplayer authority. Only called on puppets.
</description>
</signal>
</signals>
diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
index ebd1b50201..af7c345f15 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml
@@ -1,70 +1,90 @@
<?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>
+ Synchronizes properties from the multiplayer authority to the remote peers.
</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.
+ By default, [MultiplayerSynchronizer] synchronizes configured properties to all peers.
+ Visibility can be handled directly with [method set_visibility_for] or as-needed with [method add_visibility_filter] and [method update_visibility].
+ [MultiplayerSpawner]s will handle nodes according to visibility of synchronizers as long as the node at [member root_path] was spawned by one.
+ Internally, [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" />
+ <param index="0" name="filter" type="Callable" />
<description>
+ Adds a peer visibility filter for this synchronizer.
+ [code]filter[/code] should take a peer ID [int] and return a [bool].
</description>
</method>
<method name="get_visibility_for" qualifiers="const">
<return type="bool" />
- <argument index="0" name="peer" type="int" />
+ <param index="0" name="peer" type="int" />
<description>
+ Queries the current visibility for peer [code]peer[/code].
</description>
</method>
<method name="remove_visibility_filter">
<return type="void" />
- <argument index="0" name="filter" type="Callable" />
+ <param index="0" name="filter" type="Callable" />
<description>
+ Removes a peer visibility filter from this synchronizer.
</description>
</method>
<method name="set_visibility_for">
<return type="void" />
- <argument index="0" name="peer" type="int" />
- <argument index="1" name="visible" type="bool" />
+ <param index="0" name="peer" type="int" />
+ <param index="1" name="visible" type="bool" />
<description>
+ Sets the visibility of [code]peer[/code] to [code]visible[/code]. If [code]peer[/code] is [code]0[/code], the value of [member public_visibility] will be updated instead.
</description>
</method>
<method name="update_visibility">
<return type="void" />
- <argument index="0" name="for_peer" type="int" default="0" />
+ <param index="0" name="for_peer" type="int" default="0" />
<description>
+ Updates the visibility of [code]peer[/code] according to visibility filters. If [code]peer[/code] is [code]0[/code] (the default), all peers' visibilties are updated.
</description>
</method>
</methods>
<members>
<member name="public_visibility" type="bool" setter="set_visibility_public" getter="is_visibility_public" default="true">
+ Whether synchronization should be visible to all peers by default. See [method set_visibility_for] and [method add_visibility_filter] for ways of configuring fine-grained visibility options.
</member>
<member name="replication_config" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config">
+ Resource containing which properties to synchronize.
</member>
<member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
+ Time interval between synchronizes. When set to [code]0.0[/code] (the default), synchronizes happen every network process frame.
</member>
<member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;..&quot;)">
+ Node path that replicated properties are relative to.
+ If [member root_path] was spawned by a [MultiplayerSpawner], the node will be also be spawned and despawned based on this synchronizer visibility options.
</member>
<member name="visibility_update_mode" type="int" setter="set_visibility_update_mode" getter="get_visibility_update_mode" enum="MultiplayerSynchronizer.VisibilityUpdateMode" default="0">
+ Specifies when visibility filters are updated (see [enum VisibilityUpdateMode] for options).
</member>
</members>
<signals>
<signal name="visibility_changed">
- <argument index="0" name="for_peer" type="int" />
+ <param index="0" name="for_peer" type="int" />
<description>
+ Emitted when visibility of [code]for_peer[/code] is updated. See [method update_visibility].
</description>
</signal>
</signals>
<constants>
<constant name="VISIBILITY_PROCESS_IDLE" value="0" enum="VisibilityUpdateMode">
+ Visibility filters are updated every idle process frame.
</constant>
<constant name="VISIBILITY_PROCESS_PHYSICS" value="1" enum="VisibilityUpdateMode">
+ Visibility filters are updated every physics process frame.
</constant>
<constant name="VISIBILITY_PROCESS_NONE" value="2" enum="VisibilityUpdateMode">
+ Visibility filters are not updated automatically, and must be updated manually by calling [method update_visibility].
</constant>
</constants>
</class>
diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
index 0c3ed2d784..a688c5fd79 100644
--- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml
+++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml
@@ -19,12 +19,41 @@
Clears the current SceneMultiplayer network state (you shouldn't call this unless you know what you are doing).
</description>
</method>
+ <method name="complete_auth">
+ <return type="int" enum="Error" />
+ <param index="0" name="id" type="int" />
+ <description>
+ Mark the authentication step as completed for the remote peer identified by [param id]. The [signal MultiplayerAPI.peer_connected] signal will be emitted for this peer once the remote side also completes the authentication. No further authentication messages are expected to be received from this peer.
+ If a peer disconnects before completing authentication, either due to a network issue, the [member auth_timeout] expiring, or manually calling [method disconnect_peer], the [signal peer_authentication_failed] signal will be emitted instead of [signal MultiplayerAPI.peer_disconnected].
+ </description>
+ </method>
+ <method name="disconnect_peer">
+ <return type="void" />
+ <param index="0" name="id" type="int" />
+ <description>
+ Disconnects the peer identified by [param id], removing it from the list of connected peers, and closing the underlying connection with it.
+ </description>
+ </method>
+ <method name="get_authenticating_peers">
+ <return type="PackedInt32Array" />
+ <description>
+ Returns the IDs of the peers currently trying to authenticate with this [MultiplayerAPI].
+ </description>
+ </method>
+ <method name="send_auth">
+ <return type="int" enum="Error" />
+ <param index="0" name="id" type="int" />
+ <param index="1" name="data" type="PackedByteArray" />
+ <description>
+ Sends the specified [param data] to the remote peer identified by [param id] as part of an authentication message. This can be used to authenticate peers, and control when [signal MultiplayerAPI.peer_connected] is emitted (and the remote peer accepted as one of the connected peers).
+ </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" />
+ <param index="0" name="bytes" type="PackedByteArray" />
+ <param index="1" name="id" type="int" default="0" />
+ <param index="2" name="mode" type="int" enum="MultiplayerPeer.TransferMode" default="2" />
+ <param 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>
@@ -35,6 +64,12 @@
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="auth_callback" type="Callable" setter="set_auth_callback" getter="get_auth_callback">
+ The callback to execute when when receiving authentication data sent via [method send_auth]. If the [Callable] is empty (default), peers will be automatically accepted as soon as they connect.
+ </member>
+ <member name="auth_timeout" type="float" setter="set_auth_timeout" getter="get_auth_timeout" default="3.0">
+ If set to a value greater than [code]0.0[/code], the maximum amount of time peers can stay in the authenticating state, after which the authentication will automatically fail. See the [signal peer_authenticating] and [signal peer_authentication_failed] signals.
+ </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>
@@ -42,11 +77,28 @@
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>
+ <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
+ Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
+ [b]Note:[/b] Changing this option while other peers are connected may lead to unexpected behaviors.
+ [b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported].
+ </member>
</members>
<signals>
+ <signal name="peer_authenticating">
+ <param index="0" name="id" type="int" />
+ <description>
+ Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] connects to a new peer and a valid [member auth_callback] is set. In this case, the [signal MultiplayerAPI.peer_connected] will not be emitted until [method complete_auth] is called with given peer [param id]. While in this state, the peer will not be included in the list returned by [method MultiplayerAPI.get_peers] (but in the one returned by [method get_authenticating_peers]), and only authentication data will be sent or received. See [method send_auth] for sending authentication data.
+ </description>
+ </signal>
+ <signal name="peer_authentication_failed">
+ <param index="0" name="id" type="int" />
+ <description>
+ Emitted when this MultiplayerAPI's [member MultiplayerAPI.multiplayer_peer] disconnects from a peer for which authentication had not yet completed. See [signal peer_authenticating].
+ </description>
+ </signal>
<signal name="peer_packet">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="packet" type="PackedByteArray" />
+ <param index="0" name="id" type="int" />
+ <param 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>
diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
index 1d6dec2f92..fdc441e9c3 100644
--- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
+++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
@@ -1,6 +1,7 @@
<?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>
+ Configuration for properties to synchronize with a [MultiplayerSynchronizer].
</brief_description>
<description>
</description>
@@ -9,58 +10,67 @@
<methods>
<method name="add_property">
<return type="void" />
- <argument index="0" name="path" type="NodePath" />
- <argument index="1" name="index" type="int" default="-1" />
+ <param index="0" name="path" type="NodePath" />
+ <param index="1" name="index" type="int" default="-1" />
<description>
+ Adds the property identified by the given [code]path[/code] to the list of the properties being synchronized, optionally passing an [code]index[/code].
</description>
</method>
<method name="get_properties" qualifiers="const">
<return type="NodePath[]" />
<description>
+ Returns a list of synchronized property [NodePath]s.
</description>
</method>
<method name="has_property" qualifiers="const">
<return type="bool" />
- <argument index="0" name="path" type="NodePath" />
+ <param index="0" name="path" type="NodePath" />
<description>
+ Returns whether the given [code]path[/code] is configured for synchronization.
</description>
</method>
<method name="property_get_index" qualifiers="const">
<return type="int" />
- <argument index="0" name="path" type="NodePath" />
+ <param index="0" name="path" type="NodePath" />
<description>
+ Finds the index of the given [code]path[/code].
</description>
</method>
<method name="property_get_spawn">
<return type="bool" />
- <argument index="0" name="path" type="NodePath" />
+ <param index="0" name="path" type="NodePath" />
<description>
+ Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn.
</description>
</method>
<method name="property_get_sync">
<return type="bool" />
- <argument index="0" name="path" type="NodePath" />
+ <param index="0" name="path" type="NodePath" />
<description>
+ Returns whether the property identified by the given [code]path[/code] is configured to be synchronized on process.
</description>
</method>
<method name="property_set_spawn">
<return type="void" />
- <argument index="0" name="path" type="NodePath" />
- <argument index="1" name="enabled" type="bool" />
+ <param index="0" name="path" type="NodePath" />
+ <param index="1" name="enabled" type="bool" />
<description>
+ Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on spawn.
</description>
</method>
<method name="property_set_sync">
<return type="void" />
- <argument index="0" name="path" type="NodePath" />
- <argument index="1" name="enabled" type="bool" />
+ <param index="0" name="path" type="NodePath" />
+ <param index="1" name="enabled" type="bool" />
<description>
+ Sets whether the property identified by the given [code]path[/code] is configured to be synchronized on process.
</description>
</method>
<method name="remove_property">
<return type="void" />
- <argument index="0" name="path" type="NodePath" />
+ <param index="0" name="path" type="NodePath" />
<description>
+ Removes the property identified by the given [code]path[/code] from the configuration.
</description>
</method>
</methods>
diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp
new file mode 100644
index 0000000000..e320657ab5
--- /dev/null
+++ b/modules/multiplayer/editor/editor_network_profiler.cpp
@@ -0,0 +1,347 @@
+/**************************************************************************/
+/* editor_network_profiler.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_network_profiler.h"
+
+#include "core/os/os.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+
+void EditorNetworkProfiler::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
+ ADD_SIGNAL(MethodInfo("open_request", PropertyInfo(Variant::STRING, "path")));
+}
+
+void EditorNetworkProfiler::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ node_icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons"));
+ if (activate->is_pressed()) {
+ activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
+ } else {
+ activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons")));
+ }
+ clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")));
+ incoming_bandwidth_text->set_right_icon(get_theme_icon(SNAME("ArrowDown"), SNAME("EditorIcons")));
+ outgoing_bandwidth_text->set_right_icon(get_theme_icon(SNAME("ArrowUp"), SNAME("EditorIcons")));
+
+ // This needs to be done here to set the faded color when the profiler is first opened
+ incoming_bandwidth_text->add_theme_color_override("font_uneditable_color", get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.5));
+ outgoing_bandwidth_text->add_theme_color_override("font_uneditable_color", get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.5));
+ } break;
+ }
+}
+
+void EditorNetworkProfiler::_refresh() {
+ if (!dirty) {
+ return;
+ }
+ dirty = false;
+ refresh_rpc_data();
+ refresh_replication_data();
+}
+
+void EditorNetworkProfiler::refresh_rpc_data() {
+ counters_display->clear();
+
+ TreeItem *root = counters_display->create_item();
+ int cols = counters_display->get_columns();
+
+ for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_data) {
+ TreeItem *node = counters_display->create_item(root);
+
+ for (int j = 0; j < cols; ++j) {
+ node->set_text_alignment(j, j > 0 ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT);
+ }
+
+ node->set_text(0, E.value.node_path);
+ node->set_text(1, E.value.incoming_rpc == 0 ? "-" : vformat(TTR("%d (%s)"), E.value.incoming_rpc, String::humanize_size(E.value.incoming_size)));
+ node->set_text(2, E.value.outgoing_rpc == 0 ? "-" : vformat(TTR("%d (%s)"), E.value.outgoing_rpc, String::humanize_size(E.value.outgoing_size)));
+ }
+}
+
+void EditorNetworkProfiler::refresh_replication_data() {
+ replication_display->clear();
+
+ TreeItem *root = replication_display->create_item();
+
+ for (const KeyValue<ObjectID, SyncInfo> &E : sync_data) {
+ // Ensure the nodes have at least a temporary cache.
+ ObjectID ids[3] = { E.value.synchronizer, E.value.config, E.value.root_node };
+ for (uint32_t i = 0; i < 3; i++) {
+ const ObjectID &id = ids[i];
+ if (!node_data.has(id)) {
+ missing_node_data.insert(id);
+ node_data[id] = NodeInfo(id);
+ }
+ }
+
+ TreeItem *node = replication_display->create_item(root);
+
+ const NodeInfo &root_info = node_data[E.value.root_node];
+ const NodeInfo &sync_info = node_data[E.value.synchronizer];
+ const NodeInfo &cfg_info = node_data[E.value.config];
+
+ node->set_text(0, root_info.path.get_file());
+ node->set_icon(0, has_theme_icon(root_info.type, SNAME("EditorIcons")) ? get_theme_icon(root_info.type, SNAME("EditorIcons")) : node_icon);
+ node->set_tooltip_text(0, root_info.path);
+
+ node->set_text(1, sync_info.path.get_file());
+ node->set_icon(1, get_theme_icon("MultiplayerSynchronizer", SNAME("EditorIcons")));
+ node->set_tooltip_text(1, sync_info.path);
+
+ int cfg_idx = cfg_info.path.find("::");
+ if (cfg_info.path.begins_with("res://") && ResourceLoader::exists(cfg_info.path) && cfg_idx > 0) {
+ String res_idstr = cfg_info.path.substr(cfg_idx + 2).replace("SceneReplicationConfig_", "");
+ String scene_path = cfg_info.path.substr(0, cfg_idx);
+ node->set_text(2, vformat("%s (%s)", res_idstr, scene_path.get_file()));
+ node->add_button(2, get_theme_icon(SNAME("InstanceOptions"), SNAME("EditorIcons")));
+ node->set_tooltip_text(2, cfg_info.path);
+ node->set_metadata(2, scene_path);
+ } else {
+ node->set_text(2, cfg_info.path);
+ node->set_metadata(2, "");
+ }
+
+ node->set_text(3, vformat("%d - %d", E.value.incoming_syncs, E.value.outgoing_syncs));
+ node->set_text(4, vformat("%d - %d", E.value.incoming_size, E.value.outgoing_size));
+ }
+}
+
+Array EditorNetworkProfiler::pop_missing_node_data() {
+ Array out;
+ for (const ObjectID &id : missing_node_data) {
+ out.push_back(id);
+ }
+ missing_node_data.clear();
+ return out;
+}
+
+void EditorNetworkProfiler::add_node_data(const NodeInfo &p_info) {
+ ERR_FAIL_COND(!node_data.has(p_info.id));
+ node_data[p_info.id] = p_info;
+ dirty = true;
+}
+
+void EditorNetworkProfiler::_activate_pressed() {
+ if (activate->is_pressed()) {
+ refresh_timer->start();
+ activate->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
+ activate->set_text(TTR("Stop"));
+ } else {
+ refresh_timer->stop();
+ activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons")));
+ activate->set_text(TTR("Start"));
+ }
+ emit_signal(SNAME("enable_profiling"), activate->is_pressed());
+}
+
+void EditorNetworkProfiler::_clear_pressed() {
+ rpc_data.clear();
+ sync_data.clear();
+ node_data.clear();
+ missing_node_data.clear();
+ set_bandwidth(0, 0);
+ refresh_rpc_data();
+ refresh_replication_data();
+}
+
+void EditorNetworkProfiler::_replication_button_clicked(TreeItem *p_item, int p_column, int p_idx, MouseButton p_button) {
+ if (!p_item) {
+ return;
+ }
+ String meta = p_item->get_metadata(p_column);
+ if (meta.size() && ResourceLoader::exists(meta)) {
+ emit_signal("open_request", meta);
+ }
+}
+
+void EditorNetworkProfiler::add_rpc_frame_data(const RPCNodeInfo &p_frame) {
+ dirty = true;
+ if (!rpc_data.has(p_frame.node)) {
+ rpc_data.insert(p_frame.node, p_frame);
+ } else {
+ rpc_data[p_frame.node].incoming_rpc += p_frame.incoming_rpc;
+ rpc_data[p_frame.node].outgoing_rpc += p_frame.outgoing_rpc;
+ }
+ if (p_frame.incoming_rpc) {
+ rpc_data[p_frame.node].incoming_size = p_frame.incoming_size / p_frame.incoming_rpc;
+ }
+ if (p_frame.outgoing_rpc) {
+ rpc_data[p_frame.node].outgoing_size = p_frame.outgoing_size / p_frame.outgoing_rpc;
+ }
+}
+
+void EditorNetworkProfiler::add_sync_frame_data(const SyncInfo &p_frame) {
+ dirty = true;
+ if (!sync_data.has(p_frame.synchronizer)) {
+ sync_data[p_frame.synchronizer] = p_frame;
+ } else {
+ sync_data[p_frame.synchronizer].incoming_syncs += p_frame.incoming_syncs;
+ sync_data[p_frame.synchronizer].outgoing_syncs += p_frame.outgoing_syncs;
+ }
+ SyncInfo &info = sync_data[p_frame.synchronizer];
+ if (info.incoming_syncs) {
+ info.incoming_size = p_frame.incoming_size / p_frame.incoming_syncs;
+ }
+ if (info.outgoing_syncs) {
+ info.outgoing_size = p_frame.outgoing_size / p_frame.outgoing_syncs;
+ }
+}
+
+void EditorNetworkProfiler::set_bandwidth(int p_incoming, int p_outgoing) {
+ incoming_bandwidth_text->set_text(vformat(TTR("%s/s"), String::humanize_size(p_incoming)));
+ outgoing_bandwidth_text->set_text(vformat(TTR("%s/s"), String::humanize_size(p_outgoing)));
+
+ // Make labels more prominent when the bandwidth is greater than 0 to attract user attention
+ incoming_bandwidth_text->add_theme_color_override(
+ "font_uneditable_color",
+ get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, p_incoming > 0 ? 1 : 0.5));
+ outgoing_bandwidth_text->add_theme_color_override(
+ "font_uneditable_color",
+ get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, p_outgoing > 0 ? 1 : 0.5));
+}
+
+bool EditorNetworkProfiler::is_profiling() {
+ return activate->is_pressed();
+}
+
+EditorNetworkProfiler::EditorNetworkProfiler() {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ hb->add_theme_constant_override("separation", 8 * EDSCALE);
+ add_child(hb);
+
+ activate = memnew(Button);
+ activate->set_toggle_mode(true);
+ activate->set_text(TTR("Start"));
+ activate->connect("pressed", callable_mp(this, &EditorNetworkProfiler::_activate_pressed));
+ hb->add_child(activate);
+
+ clear_button = memnew(Button);
+ clear_button->set_text(TTR("Clear"));
+ clear_button->connect("pressed", callable_mp(this, &EditorNetworkProfiler::_clear_pressed));
+ hb->add_child(clear_button);
+
+ hb->add_spacer();
+
+ Label *lb = memnew(Label);
+ lb->set_text(TTR("Down"));
+ hb->add_child(lb);
+
+ incoming_bandwidth_text = memnew(LineEdit);
+ incoming_bandwidth_text->set_editable(false);
+ incoming_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
+ incoming_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ hb->add_child(incoming_bandwidth_text);
+
+ Control *down_up_spacer = memnew(Control);
+ down_up_spacer->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
+ hb->add_child(down_up_spacer);
+
+ lb = memnew(Label);
+ lb->set_text(TTR("Up"));
+ hb->add_child(lb);
+
+ outgoing_bandwidth_text = memnew(LineEdit);
+ outgoing_bandwidth_text->set_editable(false);
+ outgoing_bandwidth_text->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
+ outgoing_bandwidth_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ hb->add_child(outgoing_bandwidth_text);
+
+ // Set initial texts in the incoming/outgoing bandwidth labels
+ set_bandwidth(0, 0);
+
+ HSplitContainer *sc = memnew(HSplitContainer);
+ add_child(sc);
+ sc->set_v_size_flags(SIZE_EXPAND_FILL);
+ sc->set_h_size_flags(SIZE_EXPAND_FILL);
+ sc->set_split_offset(100 * EDSCALE);
+
+ // RPC
+ counters_display = memnew(Tree);
+ counters_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
+ counters_display->set_v_size_flags(SIZE_EXPAND_FILL);
+ counters_display->set_h_size_flags(SIZE_EXPAND_FILL);
+ counters_display->set_hide_folding(true);
+ counters_display->set_hide_root(true);
+ counters_display->set_columns(3);
+ counters_display->set_column_titles_visible(true);
+ counters_display->set_column_title(0, TTR("Node"));
+ counters_display->set_column_expand(0, true);
+ counters_display->set_column_clip_content(0, true);
+ counters_display->set_column_custom_minimum_width(0, 60 * EDSCALE);
+ counters_display->set_column_title(1, TTR("Incoming RPC"));
+ counters_display->set_column_expand(1, false);
+ counters_display->set_column_clip_content(1, true);
+ counters_display->set_column_custom_minimum_width(1, 120 * EDSCALE);
+ counters_display->set_column_title(2, TTR("Outgoing RPC"));
+ counters_display->set_column_expand(2, false);
+ counters_display->set_column_clip_content(2, true);
+ counters_display->set_column_custom_minimum_width(2, 120 * EDSCALE);
+ sc->add_child(counters_display);
+
+ // Replication
+ replication_display = memnew(Tree);
+ replication_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
+ replication_display->set_v_size_flags(SIZE_EXPAND_FILL);
+ replication_display->set_h_size_flags(SIZE_EXPAND_FILL);
+ replication_display->set_hide_folding(true);
+ replication_display->set_hide_root(true);
+ replication_display->set_columns(5);
+ replication_display->set_column_titles_visible(true);
+ replication_display->set_column_title(0, TTR("Root"));
+ replication_display->set_column_expand(0, true);
+ replication_display->set_column_clip_content(0, true);
+ replication_display->set_column_custom_minimum_width(0, 80 * EDSCALE);
+ replication_display->set_column_title(1, TTR("Synchronizer"));
+ replication_display->set_column_expand(1, true);
+ replication_display->set_column_clip_content(1, true);
+ replication_display->set_column_custom_minimum_width(1, 80 * EDSCALE);
+ replication_display->set_column_title(2, TTR("Config"));
+ replication_display->set_column_expand(2, true);
+ replication_display->set_column_clip_content(2, true);
+ replication_display->set_column_custom_minimum_width(2, 80 * EDSCALE);
+ replication_display->set_column_title(3, TTR("Count"));
+ replication_display->set_column_expand(3, false);
+ replication_display->set_column_clip_content(3, true);
+ replication_display->set_column_custom_minimum_width(3, 80 * EDSCALE);
+ replication_display->set_column_title(4, TTR("Size"));
+ replication_display->set_column_expand(4, false);
+ replication_display->set_column_clip_content(4, true);
+ replication_display->set_column_custom_minimum_width(4, 80 * EDSCALE);
+ replication_display->connect("button_clicked", callable_mp(this, &EditorNetworkProfiler::_replication_button_clicked));
+ sc->add_child(replication_display);
+
+ refresh_timer = memnew(Timer);
+ refresh_timer->set_wait_time(0.5);
+ refresh_timer->connect("timeout", callable_mp(this, &EditorNetworkProfiler::_refresh));
+ add_child(refresh_timer);
+}
diff --git a/modules/multiplayer/editor/editor_network_profiler.h b/modules/multiplayer/editor/editor_network_profiler.h
new file mode 100644
index 0000000000..e8702371e0
--- /dev/null
+++ b/modules/multiplayer/editor/editor_network_profiler.h
@@ -0,0 +1,101 @@
+/**************************************************************************/
+/* editor_network_profiler.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_NETWORK_PROFILER_H
+#define EDITOR_NETWORK_PROFILER_H
+
+#include "scene/debugger/scene_debugger.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tree.h"
+
+#include "../multiplayer_debugger.h"
+
+class EditorNetworkProfiler : public VBoxContainer {
+ GDCLASS(EditorNetworkProfiler, VBoxContainer)
+
+public:
+ struct NodeInfo {
+ ObjectID id;
+ String type;
+ String path;
+
+ NodeInfo() {}
+ NodeInfo(const ObjectID &p_id) {
+ id = p_id;
+ path = String::num_int64(p_id);
+ }
+ };
+
+private:
+ using RPCNodeInfo = MultiplayerDebugger::RPCNodeInfo;
+ using SyncInfo = MultiplayerDebugger::SyncInfo;
+
+ bool dirty = false;
+ Timer *refresh_timer = nullptr;
+ Button *activate = nullptr;
+ Button *clear_button = nullptr;
+ Tree *counters_display = nullptr;
+ LineEdit *incoming_bandwidth_text = nullptr;
+ LineEdit *outgoing_bandwidth_text = nullptr;
+ Tree *replication_display = nullptr;
+
+ HashMap<ObjectID, RPCNodeInfo> rpc_data;
+ HashMap<ObjectID, SyncInfo> sync_data;
+ HashMap<ObjectID, NodeInfo> node_data;
+ HashSet<ObjectID> missing_node_data;
+ Ref<Texture2D> node_icon;
+
+ void _activate_pressed();
+ void _clear_pressed();
+ void _refresh();
+ void _replication_button_clicked(TreeItem *p_item, int p_column, int p_idx, MouseButton p_button);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void refresh_rpc_data();
+ void refresh_replication_data();
+
+ Array pop_missing_node_data();
+ void add_node_data(const NodeInfo &p_info);
+ void add_rpc_frame_data(const RPCNodeInfo &p_frame);
+ void add_sync_frame_data(const SyncInfo &p_frame);
+ void set_bandwidth(int p_incoming, int p_outgoing);
+ bool is_profiling();
+
+ EditorNetworkProfiler();
+};
+
+#endif // EDITOR_NETWORK_PROFILER_H
diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.cpp b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp
new file mode 100644
index 0000000000..28d05e1dee
--- /dev/null
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.cpp
@@ -0,0 +1,175 @@
+/**************************************************************************/
+/* multiplayer_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_editor_plugin.h"
+
+#include "../multiplayer_synchronizer.h"
+#include "editor_network_profiler.h"
+#include "replication_editor.h"
+
+#include "editor/editor_node.h"
+
+void MultiplayerEditorDebugger::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("open_request", PropertyInfo(Variant::STRING, "path")));
+}
+
+bool MultiplayerEditorDebugger::has_capture(const String &p_capture) const {
+ return p_capture == "multiplayer";
+}
+
+void MultiplayerEditorDebugger::_open_request(const String &p_path) {
+ emit_signal("open_request", p_path);
+}
+
+bool MultiplayerEditorDebugger::capture(const String &p_message, const Array &p_data, int p_session) {
+ ERR_FAIL_COND_V(!profilers.has(p_session), false);
+ EditorNetworkProfiler *profiler = profilers[p_session];
+ if (p_message == "multiplayer:rpc") {
+ MultiplayerDebugger::RPCFrame frame;
+ frame.deserialize(p_data);
+ for (int i = 0; i < frame.infos.size(); i++) {
+ profiler->add_rpc_frame_data(frame.infos[i]);
+ }
+ return true;
+ } else if (p_message == "multiplayer:syncs") {
+ MultiplayerDebugger::ReplicationFrame frame;
+ frame.deserialize(p_data);
+ for (const KeyValue<ObjectID, MultiplayerDebugger::SyncInfo> &E : frame.infos) {
+ profiler->add_sync_frame_data(E.value);
+ }
+ Array missing = profiler->pop_missing_node_data();
+ if (missing.size()) {
+ // Asks for the object information.
+ get_session(p_session)->send_message("multiplayer:cache", missing);
+ }
+ return true;
+ } else if (p_message == "multiplayer:cache") {
+ ERR_FAIL_COND_V(p_data.size() % 3, false);
+ for (int i = 0; i < p_data.size(); i += 3) {
+ EditorNetworkProfiler::NodeInfo info;
+ info.id = p_data[i].operator ObjectID();
+ info.type = p_data[i + 1].operator String();
+ info.path = p_data[i + 2].operator String();
+ profiler->add_node_data(info);
+ }
+ return true;
+ } else if (p_message == "multiplayer:bandwidth") {
+ ERR_FAIL_COND_V(p_data.size() < 2, false);
+ profiler->set_bandwidth(p_data[0], p_data[1]);
+ return true;
+ }
+ return false;
+}
+
+void MultiplayerEditorDebugger::_profiler_activate(bool p_enable, int p_session_id) {
+ Ref<EditorDebuggerSession> session = get_session(p_session_id);
+ ERR_FAIL_COND(session.is_null());
+ session->toggle_profiler("multiplayer:bandwidth", p_enable);
+ session->toggle_profiler("multiplayer:rpc", p_enable);
+ session->toggle_profiler("multiplayer:replication", p_enable);
+}
+
+void MultiplayerEditorDebugger::setup_session(int p_session_id) {
+ Ref<EditorDebuggerSession> session = get_session(p_session_id);
+ ERR_FAIL_COND(session.is_null());
+ EditorNetworkProfiler *profiler = memnew(EditorNetworkProfiler);
+ profiler->connect("enable_profiling", callable_mp(this, &MultiplayerEditorDebugger::_profiler_activate).bind(p_session_id));
+ profiler->connect("open_request", callable_mp(this, &MultiplayerEditorDebugger::_open_request));
+ profiler->set_name(TTR("Network Profiler"));
+ session->add_session_tab(profiler);
+ profilers[p_session_id] = profiler;
+}
+
+/// MultiplayerEditorPlugin
+
+MultiplayerEditorPlugin::MultiplayerEditorPlugin() {
+ 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, &MultiplayerEditorPlugin::_pinned));
+ debugger.instantiate();
+ debugger->connect("open_request", callable_mp(this, &MultiplayerEditorPlugin::_open_request));
+}
+
+void MultiplayerEditorPlugin::_open_request(const String &p_path) {
+ get_editor_interface()->open_scene_from_path(p_path);
+}
+
+void MultiplayerEditorPlugin::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ get_tree()->connect("node_removed", callable_mp(this, &MultiplayerEditorPlugin::_node_removed));
+ add_debugger_plugin(debugger);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ remove_debugger_plugin(debugger);
+ }
+ }
+}
+
+void MultiplayerEditorPlugin::_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 MultiplayerEditorPlugin::_pinned() {
+ if (!repl_editor->get_pin()->is_pressed()) {
+ if (repl_editor->is_visible_in_tree()) {
+ EditorNode::get_singleton()->hide_bottom_panel();
+ }
+ button->hide();
+ }
+}
+
+void MultiplayerEditorPlugin::edit(Object *p_object) {
+ repl_editor->edit(Object::cast_to<MultiplayerSynchronizer>(p_object));
+}
+
+bool MultiplayerEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("MultiplayerSynchronizer");
+}
+
+void MultiplayerEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ 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/multiplayer_editor_plugin.h b/modules/multiplayer/editor/multiplayer_editor_plugin.h
new file mode 100644
index 0000000000..310653585a
--- /dev/null
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.h
@@ -0,0 +1,85 @@
+/**************************************************************************/
+/* multiplayer_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_EDITOR_PLUGIN_H
+#define MULTIPLAYER_EDITOR_PLUGIN_H
+
+#include "editor/editor_plugin.h"
+
+#include "editor/plugins/editor_debugger_plugin.h"
+
+class EditorNetworkProfiler;
+class MultiplayerEditorDebugger : public EditorDebuggerPlugin {
+ GDCLASS(MultiplayerEditorDebugger, EditorDebuggerPlugin);
+
+private:
+ HashMap<int, EditorNetworkProfiler *> profilers;
+
+ void _open_request(const String &p_path);
+ void _profiler_activate(bool p_enable, int p_session_id);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual bool has_capture(const String &p_capture) const override;
+ virtual bool capture(const String &p_message, const Array &p_data, int p_index) override;
+ virtual void setup_session(int p_session_id) override;
+
+ MultiplayerEditorDebugger() {}
+};
+
+class ReplicationEditor;
+
+class MultiplayerEditorPlugin : public EditorPlugin {
+ GDCLASS(MultiplayerEditorPlugin, EditorPlugin);
+
+private:
+ Button *button = nullptr;
+ ReplicationEditor *repl_editor = nullptr;
+ Ref<MultiplayerEditorDebugger> debugger;
+
+ void _open_request(const String &p_path);
+ void _node_removed(Node *p_node);
+
+ 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;
+
+ MultiplayerEditorPlugin();
+};
+
+#endif // MULTIPLAYER_EDITOR_PLUGIN_H
diff --git a/modules/multiplayer/editor/replication_editor_plugin.cpp b/modules/multiplayer/editor/replication_editor.cpp
index c6484264d0..66e12a338a 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -1,40 +1,46 @@
-/*************************************************************************/
-/* 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"
+/**************************************************************************/
+/* replication_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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.h"
+
+#include "../multiplayer_synchronizer.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector_dock.h"
-#include "modules/multiplayer/multiplayer_synchronizer.h"
+#include "editor/property_selector.h"
+#include "editor/scene_tree_editor.h"
#include "scene/gui/dialogs.h"
+#include "scene/gui/separator.h"
#include "scene/gui/tree.h"
void ReplicationEditor::_pick_node_filter_text_changed(const String &p_newtext) {
@@ -136,7 +142,7 @@ void ReplicationEditor::_add_sync_property(String p_path) {
return;
}
- UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Add property to synchronizer"));
if (config.is_null()) {
@@ -196,7 +202,7 @@ ReplicationEditor::ReplicationEditor() {
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.."));
+ 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));
@@ -244,16 +250,12 @@ ReplicationEditor::ReplicationEditor() {
tree->add_child(drop_label);
drop_label->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- tree->set_drag_forwarding(this);
+ SET_DRAG_FORWARDING_CDU(tree, ReplicationEditor);
}
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 {
@@ -319,10 +321,6 @@ void ReplicationEditor::_notification(int p_what) {
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;
}
}
@@ -338,28 +336,15 @@ void ReplicationEditor::_add_pressed() {
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();
+ int idx = np_text.find(":");
+ if (idx == -1) {
+ np_text = ".:" + np_text;
+ } else if (idx == 0) {
+ np_text = "." + np_text;
}
- 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();
+ NodePath path = NodePath(np_text);
+
+ _add_sync_property(path);
}
void ReplicationEditor::_tree_item_edited() {
@@ -370,7 +355,7 @@ void ReplicationEditor::_tree_item_edited() {
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();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
bool value = ti->is_checked(column);
String method;
if (column == 1) {
@@ -410,7 +395,7 @@ void ReplicationEditor::_dialog_closed(bool p_confirmed) {
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();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
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);
@@ -437,32 +422,12 @@ void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bo
}
}
-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();
@@ -473,7 +438,6 @@ void ReplicationEditor::_update_config() {
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) {
@@ -528,118 +492,3 @@ void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn,
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.h
index 57fa4c82fa..d1e0e0a541 100644
--- a/modules/multiplayer/editor/replication_editor_plugin.h
+++ b/modules/multiplayer/editor/replication_editor.h
@@ -1,47 +1,48 @@
-/*************************************************************************/
-/* 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
+/**************************************************************************/
+/* replication_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_H
+#define REPLICATION_EDITOR_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"
+#include "modules/multiplayer/scene_replication_config.h"
+#include "scene/gui/box_container.h"
class ConfirmationDialog;
class MultiplayerSynchronizer;
+class AcceptDialog;
+class LineEdit;
class Tree;
+class TreeItem;
+class PropertySelector;
+class SceneTreeDialog;
class ReplicationEditor : public VBoxContainer {
GDCLASS(ReplicationEditor, VBoxContainer);
@@ -60,7 +61,6 @@ private:
Ref<SceneReplicationConfig> config;
NodePath deleting;
Tree *tree = nullptr;
- bool keying = false;
PropertySelector *prop_selector = nullptr;
SceneTreeDialog *pick_node = nullptr;
@@ -97,52 +97,12 @@ protected:
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
+#endif // REPLICATION_EDITOR_H
diff --git a/modules/multiplayer/multiplayer_debugger.cpp b/modules/multiplayer/multiplayer_debugger.cpp
new file mode 100644
index 0000000000..ea52741601
--- /dev/null
+++ b/modules/multiplayer/multiplayer_debugger.cpp
@@ -0,0 +1,333 @@
+/**************************************************************************/
+/* multiplayer_debugger.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_debugger.h"
+
+#include "multiplayer_synchronizer.h"
+#include "scene_replication_config.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "scene/main/node.h"
+
+List<Ref<EngineProfiler>> multiplayer_profilers;
+
+void MultiplayerDebugger::initialize() {
+ Ref<BandwidthProfiler> bandwidth;
+ bandwidth.instantiate();
+ bandwidth->bind("multiplayer:bandwidth");
+ multiplayer_profilers.push_back(bandwidth);
+
+ Ref<RPCProfiler> rpc_profiler;
+ rpc_profiler.instantiate();
+ rpc_profiler->bind("multiplayer:rpc");
+ multiplayer_profilers.push_back(rpc_profiler);
+
+ Ref<ReplicationProfiler> replication_profiler;
+ replication_profiler.instantiate();
+ replication_profiler->bind("multiplayer:replication");
+ multiplayer_profilers.push_back(replication_profiler);
+
+ EngineDebugger::register_message_capture("multiplayer", EngineDebugger::Capture(nullptr, &_capture));
+}
+
+void MultiplayerDebugger::deinitialize() {
+ multiplayer_profilers.clear();
+}
+
+Error MultiplayerDebugger::_capture(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
+ if (p_msg == "cache") {
+ Array out;
+ for (int i = 0; i < p_args.size(); i++) {
+ ObjectID id = p_args[i].operator ObjectID();
+ Object *obj = ObjectDB::get_instance(id);
+ ERR_CONTINUE(!obj);
+ if (Object::cast_to<SceneReplicationConfig>(obj)) {
+ out.push_back(id);
+ out.push_back(obj->get_class());
+ out.push_back(((SceneReplicationConfig *)obj)->get_path());
+ } else if (Object::cast_to<Node>(obj)) {
+ out.push_back(id);
+ out.push_back(obj->get_class());
+ out.push_back(String(((Node *)obj)->get_path()));
+ } else {
+ ERR_FAIL_V(FAILED);
+ }
+ }
+ EngineDebugger::get_singleton()->send_message("multiplayer:cache", out);
+ return OK;
+ }
+ ERR_FAIL_V(FAILED);
+}
+
+// BandwidthProfiler
+
+int MultiplayerDebugger::BandwidthProfiler::bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) {
+ ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
+ int total_bandwidth = 0;
+
+ uint64_t timestamp = OS::get_singleton()->get_ticks_msec();
+ uint64_t final_timestamp = timestamp - 1000;
+
+ int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
+
+ while (i != p_pointer && p_buffer[i].packet_size > 0) {
+ if (p_buffer[i].timestamp < final_timestamp) {
+ return total_bandwidth;
+ }
+ total_bandwidth += p_buffer[i].packet_size;
+ i = (i + p_buffer.size() - 1) % p_buffer.size();
+ }
+
+ ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate.");
+ return total_bandwidth;
+}
+
+void MultiplayerDebugger::BandwidthProfiler::toggle(bool p_enable, const Array &p_opts) {
+ if (!p_enable) {
+ bandwidth_in.clear();
+ bandwidth_out.clear();
+ } else {
+ bandwidth_in_ptr = 0;
+ bandwidth_in.resize(16384); // ~128kB
+ for (int i = 0; i < bandwidth_in.size(); ++i) {
+ bandwidth_in.write[i].packet_size = -1;
+ }
+ bandwidth_out_ptr = 0;
+ bandwidth_out.resize(16384); // ~128kB
+ for (int i = 0; i < bandwidth_out.size(); ++i) {
+ bandwidth_out.write[i].packet_size = -1;
+ }
+ }
+}
+
+void MultiplayerDebugger::BandwidthProfiler::add(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() < 3);
+ const String inout = p_data[0];
+ int time = p_data[1];
+ int size = p_data[2];
+ if (inout == "in") {
+ bandwidth_in.write[bandwidth_in_ptr].timestamp = time;
+ bandwidth_in.write[bandwidth_in_ptr].packet_size = size;
+ bandwidth_in_ptr = (bandwidth_in_ptr + 1) % bandwidth_in.size();
+ } else if (inout == "out") {
+ bandwidth_out.write[bandwidth_out_ptr].timestamp = time;
+ bandwidth_out.write[bandwidth_out_ptr].packet_size = size;
+ bandwidth_out_ptr = (bandwidth_out_ptr + 1) % bandwidth_out.size();
+ }
+}
+
+void MultiplayerDebugger::BandwidthProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_bandwidth_time > 200) {
+ last_bandwidth_time = pt;
+ int incoming_bandwidth = bandwidth_usage(bandwidth_in, bandwidth_in_ptr);
+ int outgoing_bandwidth = bandwidth_usage(bandwidth_out, bandwidth_out_ptr);
+
+ Array arr;
+ arr.push_back(incoming_bandwidth);
+ arr.push_back(outgoing_bandwidth);
+ EngineDebugger::get_singleton()->send_message("multiplayer:bandwidth", arr);
+ }
+}
+
+// RPCProfiler
+
+Array MultiplayerDebugger::RPCFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 6);
+ for (int i = 0; i < infos.size(); ++i) {
+ arr.push_back(uint64_t(infos[i].node));
+ arr.push_back(infos[i].node_path);
+ arr.push_back(infos[i].incoming_rpc);
+ arr.push_back(infos[i].incoming_size);
+ arr.push_back(infos[i].outgoing_rpc);
+ arr.push_back(infos[i].outgoing_size);
+ }
+ return arr;
+}
+
+bool MultiplayerDebugger::RPCFrame::deserialize(const Array &p_arr) {
+ ERR_FAIL_COND_V(p_arr.size() < 1, false);
+ uint32_t size = p_arr[0];
+ ERR_FAIL_COND_V(size % 6, false);
+ ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
+ infos.resize(size / 6);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 6; i++) {
+ infos.write[i].node = uint64_t(p_arr[idx]);
+ infos.write[i].node_path = p_arr[idx + 1];
+ infos.write[i].incoming_rpc = p_arr[idx + 2];
+ infos.write[i].incoming_size = p_arr[idx + 3];
+ infos.write[i].outgoing_rpc = p_arr[idx + 4];
+ infos.write[i].outgoing_size = p_arr[idx + 5];
+ idx += 6;
+ }
+ return true;
+}
+
+void MultiplayerDebugger::RPCProfiler::init_node(const ObjectID p_node) {
+ if (rpc_node_data.has(p_node)) {
+ return;
+ }
+ rpc_node_data.insert(p_node, RPCNodeInfo());
+ rpc_node_data[p_node].node = p_node;
+ rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
+}
+
+void MultiplayerDebugger::RPCProfiler::toggle(bool p_enable, const Array &p_opts) {
+ rpc_node_data.clear();
+}
+
+void MultiplayerDebugger::RPCProfiler::add(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() != 3);
+ const String what = p_data[0];
+ const ObjectID id = p_data[1];
+ const int size = p_data[2];
+ init_node(id);
+ RPCNodeInfo &info = rpc_node_data[id];
+ if (what == "rpc_in") {
+ info.incoming_rpc++;
+ info.incoming_size += size;
+ } else if (what == "rpc_out") {
+ info.outgoing_rpc++;
+ info.outgoing_size += size;
+ }
+}
+
+void MultiplayerDebugger::RPCProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_profile_time > 100) {
+ last_profile_time = pt;
+ RPCFrame frame;
+ for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
+ frame.infos.push_back(E.value);
+ }
+ rpc_node_data.clear();
+ EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
+ }
+}
+
+// ReplicationProfiler
+
+MultiplayerDebugger::SyncInfo::SyncInfo(MultiplayerSynchronizer *p_sync) {
+ ERR_FAIL_COND(!p_sync);
+ synchronizer = p_sync->get_instance_id();
+ if (p_sync->get_replication_config().is_valid()) {
+ config = p_sync->get_replication_config()->get_instance_id();
+ }
+ if (p_sync->get_root_node()) {
+ root_node = p_sync->get_root_node()->get_instance_id();
+ }
+}
+
+void MultiplayerDebugger::SyncInfo::write_to_array(Array &r_arr) const {
+ r_arr.push_back(synchronizer);
+ r_arr.push_back(config);
+ r_arr.push_back(root_node);
+ r_arr.push_back(incoming_syncs);
+ r_arr.push_back(incoming_size);
+ r_arr.push_back(outgoing_syncs);
+ r_arr.push_back(outgoing_size);
+}
+
+bool MultiplayerDebugger::SyncInfo::read_from_array(const Array &p_arr, int p_offset) {
+ ERR_FAIL_COND_V(p_arr.size() - p_offset < 7, false);
+ synchronizer = int64_t(p_arr[p_offset]);
+ config = int64_t(p_arr[p_offset + 1]);
+ root_node = int64_t(p_arr[p_offset + 2]);
+ incoming_syncs = p_arr[p_offset + 3];
+ incoming_size = p_arr[p_offset + 4];
+ outgoing_syncs = p_arr[p_offset + 5];
+ outgoing_size = p_arr[p_offset + 6];
+ return true;
+}
+
+Array MultiplayerDebugger::ReplicationFrame::serialize() {
+ Array arr;
+ arr.push_back(infos.size() * 7);
+ for (const KeyValue<ObjectID, SyncInfo> &E : infos) {
+ E.value.write_to_array(arr);
+ }
+ return arr;
+}
+
+bool MultiplayerDebugger::ReplicationFrame::deserialize(const Array &p_arr) {
+ ERR_FAIL_COND_V(p_arr.size() < 1, false);
+ uint32_t size = p_arr[0];
+ ERR_FAIL_COND_V(size % 7, false);
+ ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
+ int idx = 1;
+ for (uint32_t i = 0; i < size / 7; i++) {
+ SyncInfo info;
+ if (!info.read_from_array(p_arr, idx)) {
+ return false;
+ }
+ infos[info.synchronizer] = info;
+ idx += 7;
+ }
+ return true;
+}
+
+void MultiplayerDebugger::ReplicationProfiler::toggle(bool p_enable, const Array &p_opts) {
+ sync_data.clear();
+}
+
+void MultiplayerDebugger::ReplicationProfiler::add(const Array &p_data) {
+ ERR_FAIL_COND(p_data.size() != 3);
+ const String what = p_data[0];
+ const ObjectID id = p_data[1];
+ const uint64_t size = p_data[2];
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(id));
+ ERR_FAIL_COND(!sync);
+ if (!sync_data.has(id)) {
+ sync_data[id] = SyncInfo(sync);
+ }
+ SyncInfo &info = sync_data[id];
+ if (what == "sync_in") {
+ info.incoming_syncs++;
+ info.incoming_size += size;
+ } else if (what == "sync_out") {
+ info.outgoing_syncs++;
+ info.outgoing_size += size;
+ }
+}
+
+void MultiplayerDebugger::ReplicationProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
+ uint64_t pt = OS::get_singleton()->get_ticks_msec();
+ if (pt - last_profile_time > 100) {
+ last_profile_time = pt;
+ ReplicationFrame frame;
+ for (const KeyValue<ObjectID, SyncInfo> &E : sync_data) {
+ frame.infos[E.key] = E.value;
+ }
+ sync_data.clear();
+ EngineDebugger::get_singleton()->send_message("multiplayer:syncs", frame.serialize());
+ }
+}
diff --git a/modules/multiplayer/multiplayer_debugger.h b/modules/multiplayer/multiplayer_debugger.h
new file mode 100644
index 0000000000..7d2eaa4584
--- /dev/null
+++ b/modules/multiplayer/multiplayer_debugger.h
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/* multiplayer_debugger.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_DEBUGGER_H
+#define MULTIPLAYER_DEBUGGER_H
+
+#include "core/debugger/engine_profiler.h"
+
+#include "core/os/os.h"
+
+class MultiplayerSynchronizer;
+
+class MultiplayerDebugger {
+public:
+ struct RPCNodeInfo {
+ ObjectID node;
+ String node_path;
+ int incoming_rpc = 0;
+ int incoming_size = 0;
+ int outgoing_rpc = 0;
+ int outgoing_size = 0;
+ };
+
+ struct RPCFrame {
+ Vector<RPCNodeInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+ struct SyncInfo {
+ ObjectID synchronizer;
+ ObjectID config;
+ ObjectID root_node;
+ int incoming_syncs = 0;
+ int incoming_size = 0;
+ int outgoing_syncs = 0;
+ int outgoing_size = 0;
+
+ void write_to_array(Array &r_arr) const;
+ bool read_from_array(const Array &p_arr, int p_offset);
+
+ SyncInfo() {}
+ SyncInfo(MultiplayerSynchronizer *p_sync);
+ };
+
+ struct ReplicationFrame {
+ HashMap<ObjectID, SyncInfo> infos;
+
+ Array serialize();
+ bool deserialize(const Array &p_arr);
+ };
+
+private:
+ class BandwidthProfiler : public EngineProfiler {
+ protected:
+ struct BandwidthFrame {
+ uint32_t timestamp;
+ int packet_size;
+ };
+
+ int bandwidth_in_ptr = 0;
+ Vector<BandwidthFrame> bandwidth_in;
+ int bandwidth_out_ptr = 0;
+ Vector<BandwidthFrame> bandwidth_out;
+ uint64_t last_bandwidth_time = 0;
+
+ int bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer);
+
+ public:
+ void toggle(bool p_enable, const Array &p_opts);
+ void add(const Array &p_data);
+ void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time);
+ };
+
+ class RPCProfiler : public EngineProfiler {
+ private:
+ HashMap<ObjectID, RPCNodeInfo> rpc_node_data;
+ uint64_t last_profile_time = 0;
+
+ void init_node(const ObjectID p_node);
+
+ public:
+ void toggle(bool p_enable, const Array &p_opts);
+ void add(const Array &p_data);
+ void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time);
+ };
+
+ class ReplicationProfiler : public EngineProfiler {
+ private:
+ HashMap<ObjectID, SyncInfo> sync_data;
+ uint64_t last_profile_time = 0;
+
+ public:
+ void toggle(bool p_enable, const Array &p_opts);
+ void add(const Array &p_data);
+ void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time);
+ };
+
+ static Error _capture(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
+
+public:
+ static void initialize();
+ static void deinitialize();
+};
+
+#endif // MULTIPLAYER_DEBUGGER_H
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index f5edffbb0c..0aa54b69f9 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* multiplayer_spawner.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -86,23 +86,34 @@ void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
#endif
+
+PackedStringArray MultiplayerSpawner::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
+
+ if (spawn_path.is_empty() || !has_node(spawn_path)) {
+ warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
+ }
+ return warnings;
+}
+
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 {
+ ERR_FAIL_INDEX_V(p_idx, (int)spawnable_scenes.size(), "");
return spawnable_scenes[p_idx].path;
}
+
void MultiplayerSpawner::clear_spawnable_scenes() {
spawnable_scenes.clear();
}
@@ -126,7 +137,7 @@ void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) {
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("get_spawnable_scene", "index"), &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);
@@ -144,10 +155,12 @@ void MultiplayerSpawner::_bind_methods() {
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");
+ ClassDB::bind_method(D_METHOD("get_spawn_function"), &MultiplayerSpawner::get_spawn_function);
+ ClassDB::bind_method(D_METHOD("set_spawn_function", "spawn_function"), &MultiplayerSpawner::set_spawn_function);
+ ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_spawn_function", "get_spawn_function");
- 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")));
+ ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
}
void MultiplayerSpawner::_update_spawn_node() {
@@ -165,7 +178,7 @@ void MultiplayerSpawner::_update_spawn_node() {
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)) {
+ if (get_spawnable_scene_count()) {
node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
}
} else {
@@ -186,10 +199,6 @@ void MultiplayerSpawner::_notification(int p_what) {
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();
@@ -230,12 +239,12 @@ void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_s
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);
+ p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit).bind(p_node->get_instance_id()), CONNECT_ONE_SHOT);
+ _spawn_notify(p_node->get_instance_id());
}
}
-void MultiplayerSpawner::_node_ready(ObjectID p_id) {
+void MultiplayerSpawner::_spawn_notify(ObjectID p_id) {
get_multiplayer()->object_configuration_add(ObjectDB::get_instance(p_id), this);
}
@@ -270,31 +279,36 @@ const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const
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();
+ SpawnableScene &sc = spawnable_scenes[p_id];
+ if (sc.cache.is_null()) {
+ sc.cache = ResourceLoader::load(sc.path);
+ }
+ ERR_FAIL_COND_V_MSG(sc.cache.is_null(), nullptr, "Invalid spawnable scene: " + sc.path);
+ return sc.cache->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;
+ ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires a valid 'spawn_function'.");
+ const Variant *argv[1] = { &p_data };
+ Variant ret;
+ Callable::CallError ce;
+ spawn_function.callp(argv, 1, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, nullptr, "Failed to call spawn function.");
+ ERR_FAIL_COND_V_MSG(ret.get_type() != Variant::OBJECT, nullptr, "The spawn function must return a Node.");
+ return Object::cast_to<Node>(ret.operator Object *());
}
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.");
+ ERR_FAIL_COND_V_MSG(!spawn_function.is_valid(), nullptr, "Custom spawn requires the 'spawn_function' property to be a valid callable.");
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.");
+ ERR_FAIL_COND_V_MSG(!node, nullptr, "The 'spawn_function' callable must return a valid node.");
_track(node, p_data);
parent->add_child(node, true);
diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
index 80bb878a74..8a54140e32 100644
--- a/modules/multiplayer/multiplayer_spawner.h
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* multiplayer_spawner.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -71,12 +71,13 @@ private:
ObjectID spawn_node;
HashMap<ObjectID, SpawnInfo> tracked_nodes;
uint32_t spawn_limit = 0;
+ Callable spawn_function;
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);
+ void _spawn_notify(ObjectID p_id);
Vector<String> _get_spawnable_scenes() const;
void _set_spawnable_scenes(const Vector<String> &p_scenes);
@@ -91,7 +92,11 @@ protected:
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; }
+ PackedStringArray get_configuration_warnings() const override;
+
+ 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;
@@ -102,6 +107,8 @@ public:
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; }
+ void set_spawn_function(Callable p_spawn_function) { spawn_function = p_spawn_function; }
+ Callable get_spawn_function() const { return spawn_function; }
const Variant get_spawn_argument(const ObjectID &p_id) const;
int find_spawnable_scene_index_from_object(const ObjectID &p_id) const;
@@ -110,8 +117,6 @@ public:
Node *instantiate_custom(const Variant &p_data);
Node *instantiate_scene(int p_idx);
- GDVIRTUAL1R(Object *, _spawn_custom, const Variant &);
-
MultiplayerSpawner() {}
};
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index eee1495c14..10714db6df 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* multiplayer_synchronizer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -48,6 +48,8 @@ void MultiplayerSynchronizer::_stop() {
return;
}
#endif
+ root_node_cache = ObjectID();
+ reset();
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
get_multiplayer()->object_configuration_remove(node, this);
@@ -60,8 +62,11 @@ void MultiplayerSynchronizer::_start() {
return;
}
#endif
+ root_node_cache = ObjectID();
+ reset();
Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
if (node) {
+ root_node_cache = node->get_instance_id();
get_multiplayer()->object_configuration_add(node, this);
_update_process();
}
@@ -94,6 +99,54 @@ void MultiplayerSynchronizer::_update_process() {
}
}
+Node *MultiplayerSynchronizer::get_root_node() {
+ return root_node_cache.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(root_node_cache)) : nullptr;
+}
+
+void MultiplayerSynchronizer::reset() {
+ net_id = 0;
+ last_sync_msec = 0;
+ last_inbound_sync = 0;
+}
+
+uint32_t MultiplayerSynchronizer::get_net_id() const {
+ return net_id;
+}
+
+void MultiplayerSynchronizer::set_net_id(uint32_t p_net_id) {
+ net_id = p_net_id;
+}
+
+bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_msec) {
+ if (last_sync_msec == p_msec) {
+ // last_sync_msec has been updated on this frame.
+ return true;
+ }
+ if (p_msec >= last_sync_msec + interval_msec) {
+ last_sync_msec = p_msec;
+ return true;
+ }
+ return false;
+}
+
+bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time) {
+ if (p_network_time <= last_inbound_sync && last_inbound_sync - p_network_time < 32767) {
+ return false;
+ }
+ last_inbound_sync = p_network_time;
+ return true;
+}
+
+PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
+
+ if (root_path.is_empty() || !has_node(root_path)) {
+ warnings.push_back(RTR("A valid NodePath must be set in the \"Root Path\" property in order for MultiplayerSynchronizer to be able to synchronize properties."));
+ }
+
+ return warnings;
+}
+
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());
@@ -253,10 +306,6 @@ 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;
}
@@ -289,10 +338,11 @@ NodePath MultiplayerSynchronizer::get_root_path() const {
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) {
+ if (!node || get_multiplayer_authority() == p_peer_id) {
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);
diff --git a/modules/multiplayer/multiplayer_synchronizer.h b/modules/multiplayer/multiplayer_synchronizer.h
index e84d41db86..11590f4156 100644
--- a/modules/multiplayer/multiplayer_synchronizer.h
+++ b/modules/multiplayer/multiplayer_synchronizer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* multiplayer_synchronizer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -53,6 +53,11 @@ private:
HashSet<Callable> visibility_filters;
HashSet<int> peer_visibility;
+ ObjectID root_node_cache;
+ uint64_t last_sync_msec = 0;
+ uint16_t last_inbound_sync = 0;
+ uint32_t net_id = 0;
+
static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
void _start();
void _stop();
@@ -66,9 +71,19 @@ 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 reset();
+ Node *get_root_node();
+
+ uint32_t get_net_id() const;
+ void set_net_id(uint32_t p_net_id);
+
+ bool update_outbound_sync_time(uint64_t p_msec);
+ bool update_inbound_sync_time(uint16_t p_network_time);
+
+ PackedStringArray get_configuration_warnings() const override;
+
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();
diff --git a/modules/multiplayer/register_types.cpp b/modules/multiplayer/register_types.cpp
index a2c524da80..ea562af43f 100644
--- a/modules/multiplayer/register_types.cpp
+++ b/modules/multiplayer/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -36,9 +36,10 @@
#include "scene_replication_interface.h"
#include "scene_rpc_interface.h"
+#include "multiplayer_debugger.h"
+
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
-#include "editor/replication_editor_plugin.h"
+#include "editor/multiplayer_editor_plugin.h"
#endif
void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
@@ -46,15 +47,18 @@ void initialize_multiplayer_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(SceneReplicationConfig);
GDREGISTER_CLASS(MultiplayerSpawner);
GDREGISTER_CLASS(MultiplayerSynchronizer);
+ GDREGISTER_CLASS(OfflineMultiplayerPeer);
GDREGISTER_CLASS(SceneMultiplayer);
MultiplayerAPI::set_default_interface("SceneMultiplayer");
+ MultiplayerDebugger::initialize();
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
- EditorPlugins::add_by_type<ReplicationEditorPlugin>();
+ EditorPlugins::add_by_type<MultiplayerEditorPlugin>();
}
#endif
}
void uninitialize_multiplayer_module(ModuleInitializationLevel p_level) {
+ MultiplayerDebugger::deinitialize();
}
diff --git a/modules/multiplayer/register_types.h b/modules/multiplayer/register_types.h
index aca6cf46ed..0144b0cedd 100644
--- a/modules/multiplayer/register_types.h
+++ b/modules/multiplayer/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp
index b3b642f815..fed124c41a 100644
--- a/modules/multiplayer/scene_cache_interface.cpp
+++ b/modules/multiplayer/scene_cache_interface.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_cache_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -99,14 +99,9 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac
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());
+ multiplayer->send_command(p_from, packet.ptr(), packet.size());
}
void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
@@ -156,16 +151,11 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat
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 = multiplayer->send_command(peer_id, 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);
diff --git a/modules/multiplayer/scene_cache_interface.h b/modules/multiplayer/scene_cache_interface.h
index 1e80792fe7..9400417cdb 100644
--- a/modules/multiplayer/scene_cache_interface.h
+++ b/modules/multiplayer/scene_cache_interface.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_cache_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
index 3fc1eef366..01fc1b5275 100644
--- a/modules/multiplayer/scene_multiplayer.cpp
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_multiplayer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -40,25 +40,43 @@
#endif
#ifdef DEBUG_ENABLED
-void SceneMultiplayer::profile_bandwidth(const String &p_inout, int p_size) {
- if (EngineDebugger::is_profiling("multiplayer")) {
+_FORCE_INLINE_ void SceneMultiplayer::_profile_bandwidth(const String &p_what, int p_value) {
+ if (EngineDebugger::is_profiling("multiplayer:bandwidth")) {
Array values;
- values.push_back(p_inout);
+ values.push_back(p_what);
values.push_back(OS::get_singleton()->get_ticks_msec());
- values.push_back(p_size);
- EngineDebugger::profiler_add_frame_data("multiplayer", values);
+ values.push_back(p_value);
+ EngineDebugger::profiler_add_frame_data("multiplayer:bandwidth", values);
}
}
#endif
+void SceneMultiplayer::_update_status() {
+ MultiplayerPeer::ConnectionStatus status = multiplayer_peer.is_valid() ? multiplayer_peer->get_connection_status() : MultiplayerPeer::CONNECTION_DISCONNECTED;
+ if (last_connection_status != status) {
+ if (status == MultiplayerPeer::CONNECTION_DISCONNECTED) {
+ if (last_connection_status == MultiplayerPeer::CONNECTION_CONNECTING) {
+ emit_signal(SNAME("connection_failed"));
+ } else {
+ emit_signal(SNAME("server_disconnected"));
+ }
+ clear();
+ }
+ last_connection_status = status;
+ }
+}
+
Error SceneMultiplayer::poll() {
- if (!multiplayer_peer.is_valid() || multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) {
- return ERR_UNCONFIGURED;
+ _update_status();
+ if (last_connection_status == MultiplayerPeer::CONNECTION_DISCONNECTED) {
+ return OK;
}
multiplayer_peer->poll();
- if (!multiplayer_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here.
+ _update_status();
+ if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) {
+ // We might be still connecting, or polling might have resulted in a disconnection.
return OK;
}
@@ -67,25 +85,103 @@ Error SceneMultiplayer::poll() {
const uint8_t *packet;
int len;
+ int channel = multiplayer_peer->get_packet_channel();
+ MultiplayerPeer::TransferMode mode = multiplayer_peer->get_packet_mode();
+
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;
+#ifdef DEBUG_ENABLED
+ _profile_bandwidth("in", len);
+#endif
+
+ if (pending_peers.has(sender)) {
+ if (pending_peers[sender].local) {
+ // If the auth is over, admit the peer at the first packet.
+ pending_peers.erase(sender);
+ _admit_peer(sender);
+ } else {
+ ERR_CONTINUE(len < 2 || (packet[0] & CMD_MASK) != NETWORK_COMMAND_SYS || packet[1] != SYS_COMMAND_AUTH);
+ // Auth message.
+ PackedByteArray pba;
+ pba.resize(len - 2);
+ if (pba.size()) {
+ memcpy(pba.ptrw(), &packet[2], len - 2);
+ // User callback
+ const Variant sv = sender;
+ const Variant pbav = pba;
+ const Variant *argv[2] = { &sv, &pbav };
+ Variant ret;
+ Callable::CallError ce;
+ auth_callback.callp(argv, 2, ret, ce);
+ ERR_CONTINUE_MSG(ce.error != Callable::CallError::CALL_OK, "Failed to call authentication callback");
+ } else {
+ // Remote complete notification.
+ pending_peers[sender].remote = true;
+ if (pending_peers[sender].local) {
+ pending_peers.erase(sender);
+ _admit_peer(sender);
+ }
+ }
+ continue; // Auth in progress.
+ }
+ }
+
+ ERR_CONTINUE(!connected_peers.has(sender));
+
+ if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) {
+ // Sys messages are processed separately since they might call _process_packet themselves.
+ if (len > 1 && packet[1] == SYS_COMMAND_AUTH) {
+ ERR_CONTINUE(len != 2);
+ // If we are here, we already admitted the peer locally, and this is just a confirmation packet.
+ continue;
+ }
+
+ _process_sys(sender, packet, len, mode, channel);
+ } else {
+ 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.
+ _update_status();
+ if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) { // It's possible that processing a packet might have resulted in a disconnection, so check here.
+ return OK;
}
}
+ if (pending_peers.size() && auth_timeout) {
+ HashSet<int> to_drop;
+ uint64_t time = OS::get_singleton()->get_ticks_msec();
+ for (const KeyValue<int, PendingPeer> &pending : pending_peers) {
+ if (pending.value.time + auth_timeout <= time) {
+ multiplayer_peer->disconnect_peer(pending.key);
+ to_drop.insert(pending.key);
+ }
+ }
+ for (const int &P : to_drop) {
+ // Each signal might trigger a disconnection.
+ pending_peers.erase(P);
+ emit_signal(SNAME("peer_authentication_failed"), P);
+ }
+ }
+
+ _update_status();
+ if (last_connection_status != MultiplayerPeer::CONNECTION_CONNECTED) { // Signals might have triggered disconnection.
+ return OK;
+ }
+
replicator->on_network_process();
return OK;
}
void SceneMultiplayer::clear() {
+ last_connection_status = MultiplayerPeer::CONNECTION_DISCONNECTED;
+ pending_peers.clear();
connected_peers.clear();
packet_cache.clear();
+ replicator->on_reset();
cache->clear();
+ relay_buffer->clear();
}
void SceneMultiplayer::set_root_path(const NodePath &p_path) {
@@ -108,9 +204,6 @@ void SceneMultiplayer::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer)
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();
}
@@ -119,11 +212,8 @@ void SceneMultiplayer::set_multiplayer_peer(const Ref<MultiplayerPeer> &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();
+ _update_status();
}
Ref<MultiplayerPeer> SceneMultiplayer::get_multiplayer_peer() {
@@ -134,10 +224,6 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
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;
@@ -166,34 +252,191 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int
case NETWORK_COMMAND_SYNC: {
replicator->on_sync_receive(p_from, p_packet, p_packet_len);
} break;
+ default: {
+ ERR_FAIL_MSG("Invalid network command from " + itos(p_from));
+ } break;
+ }
+}
+
+#ifdef DEBUG_ENABLED
+_FORCE_INLINE_ Error SceneMultiplayer::_send(const uint8_t *p_packet, int p_packet_len) {
+ _profile_bandwidth("out", p_packet_len);
+ return multiplayer_peer->put_packet(p_packet, p_packet_len);
+}
+#endif
+
+Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) {
+ if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Send relay packet.
+ relay_buffer->seek(0);
+ relay_buffer->put_u8(NETWORK_COMMAND_SYS);
+ relay_buffer->put_u8(SYS_COMMAND_RELAY);
+ relay_buffer->put_32(p_to); // Set the destination.
+ relay_buffer->put_data(p_packet, p_packet_len);
+ multiplayer_peer->set_target_peer(1);
+ const Vector<uint8_t> data = relay_buffer->get_data_array();
+ return _send(data.ptr(), relay_buffer->get_position());
+ }
+ if (p_to > 0) {
+ ERR_FAIL_COND_V(!connected_peers.has(p_to), ERR_BUG);
+ multiplayer_peer->set_target_peer(p_to);
+ return _send(p_packet, p_packet_len);
+ } else {
+ for (const int &pid : connected_peers) {
+ if (p_to && pid == -p_to) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(pid);
+ _send(p_packet, p_packet_len);
+ }
+ return OK;
+ }
+}
+
+void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel) {
+ ERR_FAIL_COND_MSG(p_packet_len < SYS_CMD_SIZE, "Invalid packet received. Size too small.");
+ uint8_t sys_cmd_type = p_packet[1];
+ int32_t peer = int32_t(decode_uint32(&p_packet[2]));
+ switch (sys_cmd_type) {
+ case SYS_COMMAND_ADD_PEER: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
+ _admit_peer(peer); // Relayed peers are automatically accepted.
+ } break;
+ case SYS_COMMAND_DEL_PEER: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1);
+ _del_peer(peer);
+ } break;
+ case SYS_COMMAND_RELAY: {
+ ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported());
+ ERR_FAIL_COND(p_packet_len < SYS_CMD_SIZE + 1);
+ const uint8_t *packet = p_packet + SYS_CMD_SIZE;
+ int len = p_packet_len - SYS_CMD_SIZE;
+ bool should_process = false;
+ if (get_unique_id() == 1) { // I am the server.
+ // Direct messages to server should not go through relay.
+ ERR_FAIL_COND(peer > 0 && !connected_peers.has(peer));
+ // Send relay packet.
+ relay_buffer->seek(0);
+ relay_buffer->put_u8(NETWORK_COMMAND_SYS);
+ relay_buffer->put_u8(SYS_COMMAND_RELAY);
+ relay_buffer->put_32(p_from); // Set the source.
+ relay_buffer->put_data(packet, len);
+ const Vector<uint8_t> data = relay_buffer->get_data_array();
+ multiplayer_peer->set_transfer_mode(p_mode);
+ multiplayer_peer->set_transfer_channel(p_channel);
+ if (peer > 0) {
+ multiplayer_peer->set_target_peer(peer);
+ _send(data.ptr(), relay_buffer->get_position());
+ } else {
+ for (const int &P : connected_peers) {
+ // Not to sender, nor excluded.
+ if (P == p_from || (peer < 0 && P != -peer)) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(P);
+ _send(data.ptr(), relay_buffer->get_position());
+ }
+ }
+ if (peer == 0 || peer == -1) {
+ should_process = true;
+ peer = p_from; // Process as the source.
+ }
+ } else {
+ ERR_FAIL_COND(p_from != 1); // Bug.
+ should_process = true;
+ }
+ if (should_process) {
+ remote_sender_id = peer;
+ _process_packet(peer, packet, len);
+ remote_sender_id = 0;
+ }
+ } break;
+ default: {
+ ERR_FAIL();
+ }
}
}
void SceneMultiplayer::_add_peer(int p_id) {
+ if (auth_callback.is_valid()) {
+ pending_peers[p_id] = PendingPeer();
+ pending_peers[p_id].time = OS::get_singleton()->get_ticks_msec();
+ emit_signal(SNAME("peer_authenticating"), p_id);
+ return;
+ } else {
+ _admit_peer(p_id);
+ }
+}
+
+void SceneMultiplayer::_admit_peer(int p_id) {
+ if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Notify others of connection, and send connected peers to newly connected one.
+ uint8_t buf[SYS_CMD_SIZE];
+ buf[0] = NETWORK_COMMAND_SYS;
+ buf[1] = SYS_COMMAND_ADD_PEER;
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ for (const int &P : connected_peers) {
+ // Send new peer to already connected.
+ encode_uint32(p_id, &buf[2]);
+ multiplayer_peer->set_target_peer(P);
+ _send(buf, sizeof(buf));
+ // Send already connected to new peer.
+ encode_uint32(P, &buf[2]);
+ multiplayer_peer->set_target_peer(p_id);
+ _send(buf, sizeof(buf));
+ }
+ }
+
connected_peers.insert(p_id);
cache->on_peer_change(p_id, true);
replicator->on_peer_change(p_id, true);
+ if (p_id == 1) {
+ emit_signal(SNAME("connected_to_server"));
+ }
emit_signal(SNAME("peer_connected"), p_id);
}
void SceneMultiplayer::_del_peer(int p_id) {
+ if (pending_peers.has(p_id)) {
+ pending_peers.erase(p_id);
+ emit_signal(SNAME("peer_authentication_failed"), p_id);
+ return;
+ } else if (!connected_peers.has(p_id)) {
+ return;
+ }
+
+ if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) {
+ // Notify others of disconnection.
+ uint8_t buf[SYS_CMD_SIZE];
+ buf[0] = NETWORK_COMMAND_SYS;
+ buf[1] = SYS_COMMAND_DEL_PEER;
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ encode_uint32(p_id, &buf[2]);
+ for (const int &P : connected_peers) {
+ if (P == p_id) {
+ continue;
+ }
+ multiplayer_peer->set_target_peer(P);
+ _send(buf, sizeof(buf));
+ }
+ }
+
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"));
+void SceneMultiplayer::disconnect_peer(int p_id) {
+ ERR_FAIL_COND(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED);
+ if (pending_peers.has(p_id)) {
+ pending_peers.erase(p_id);
+ } else if (connected_peers.has(p_id)) {
+ connected_peers.has(p_id);
+ }
+ multiplayer_peer->disconnect_peer(p_id);
}
Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) {
@@ -209,11 +452,64 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer
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 send_command(p_to, packet_cache.ptr(), p_data.size() + 1);
+}
+
+Error SceneMultiplayer::send_auth(int p_to, Vector<uint8_t> p_data) {
+ ERR_FAIL_COND_V(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!pending_peers.has(p_to), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(pending_peers[p_to].local, ERR_FILE_CANT_WRITE, "The authentication session was previously marked as completed, no more authentication data can be sent.");
+ ERR_FAIL_COND_V_MSG(pending_peers[p_to].remote, ERR_FILE_CANT_WRITE, "The remote peer notified that the authentication session was completed, no more authentication data can be sent.");
- return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1);
+ if (packet_cache.size() < p_data.size() + 2) {
+ packet_cache.resize(p_data.size() + 2);
+ }
+
+ packet_cache.write[0] = NETWORK_COMMAND_SYS;
+ packet_cache.write[1] = SYS_COMMAND_AUTH;
+ memcpy(&packet_cache.write[2], p_data.ptr(), p_data.size());
+
+ multiplayer_peer->set_target_peer(p_to);
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ return _send(packet_cache.ptr(), p_data.size() + 2);
+}
+
+Error SceneMultiplayer::complete_auth(int p_peer) {
+ ERR_FAIL_COND_V(multiplayer_peer.is_null() || multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!pending_peers.has(p_peer), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(pending_peers[p_peer].local, ERR_FILE_CANT_WRITE, "The authentication session was already marked as completed.");
+ pending_peers[p_peer].local = true;
+ // Notify the remote peer that the authentication has completed.
+ uint8_t buf[2] = { NETWORK_COMMAND_SYS, SYS_COMMAND_AUTH };
+ Error err = _send(buf, 2);
+ // The remote peer already reported the authentication as completed, so admit the peer.
+ // May generate new packets, so it must happen after sending confirmation.
+ if (pending_peers[p_peer].remote) {
+ pending_peers.erase(p_peer);
+ _admit_peer(p_peer);
+ }
+ return err;
+}
+
+void SceneMultiplayer::set_auth_callback(Callable p_callback) {
+ auth_callback = p_callback;
+}
+
+Callable SceneMultiplayer::get_auth_callback() const {
+ return auth_callback;
+}
+
+void SceneMultiplayer::set_auth_timeout(double p_timeout) {
+ ERR_FAIL_COND_MSG(p_timeout < 0, "Timeout must be greater or equal to 0 (where 0 means no timeout)");
+ auth_timeout = uint64_t(p_timeout * 1000);
+}
+
+double SceneMultiplayer::get_auth_timeout() const {
+ return double(auth_timeout) / 1000.0;
}
void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
@@ -255,6 +551,16 @@ Vector<int> SceneMultiplayer::get_peer_ids() {
return ret;
}
+Vector<int> SceneMultiplayer::get_authenticating_peer_ids() {
+ Vector<int> out;
+ out.resize(pending_peers.size());
+ int idx = 0;
+ for (const KeyValue<int, PendingPeer> &E : pending_peers) {
+ out.write[idx++] = E.key;
+ }
+ return out;
+}
+
void SceneMultiplayer::set_allow_object_decoding(bool p_enable) {
allow_object_decoding = p_enable;
}
@@ -303,28 +609,58 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con
return ERR_INVALID_PARAMETER;
}
+void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) {
+ server_relay = p_enabled;
+}
+
+bool SceneMultiplayer::is_server_relay_enabled() const {
+ return server_relay;
+}
+
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("disconnect_peer", "id"), &SceneMultiplayer::disconnect_peer);
+
+ ClassDB::bind_method(D_METHOD("get_authenticating_peers"), &SceneMultiplayer::get_authenticating_peer_ids);
+ ClassDB::bind_method(D_METHOD("send_auth", "id", "data"), &SceneMultiplayer::send_auth);
+ ClassDB::bind_method(D_METHOD("complete_auth", "id"), &SceneMultiplayer::complete_auth);
+
+ ClassDB::bind_method(D_METHOD("set_auth_callback", "callback"), &SceneMultiplayer::set_auth_callback);
+ ClassDB::bind_method(D_METHOD("get_auth_callback"), &SceneMultiplayer::get_auth_callback);
+ ClassDB::bind_method(D_METHOD("set_auth_timeout", "timeout"), &SceneMultiplayer::set_auth_timeout);
+ ClassDB::bind_method(D_METHOD("get_auth_timeout"), &SceneMultiplayer::get_auth_timeout);
+
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("set_server_relay_enabled", "enabled"), &SceneMultiplayer::set_server_relay_enabled);
+ ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled);
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::CALLABLE, "auth_callback"), "set_auth_callback", "get_auth_callback");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auth_timeout", PROPERTY_HINT_RANGE, "0,30,0.1,or_greater,suffix:s"), "set_auth_timeout", "get_auth_timeout");
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(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
+
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
+ ADD_SIGNAL(MethodInfo("peer_authenticating", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_authentication_failed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));
}
SceneMultiplayer::SceneMultiplayer() {
+ relay_buffer.instantiate();
replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this)));
rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this)));
cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));
+ set_multiplayer_peer(Ref<OfflineMultiplayerPeer>(memnew(OfflineMultiplayerPeer)));
}
SceneMultiplayer::~SceneMultiplayer() {
diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h
index a99cca7b21..1dbbf07853 100644
--- a/modules/multiplayer/scene_multiplayer.h
+++ b/modules/multiplayer/scene_multiplayer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_multiplayer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -37,6 +37,31 @@
#include "scene_replication_interface.h"
#include "scene_rpc_interface.h"
+class OfflineMultiplayerPeer : public MultiplayerPeer {
+ GDCLASS(OfflineMultiplayerPeer, MultiplayerPeer);
+
+public:
+ virtual int get_available_packet_count() const override { return 0; }
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override {
+ *r_buffer = nullptr;
+ r_buffer_size = 0;
+ return OK;
+ }
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override { return OK; }
+ virtual int get_max_packet_size() const override { return 0; }
+
+ virtual void set_target_peer(int p_peer_id) override {}
+ virtual int get_packet_peer() const override { return 0; }
+ virtual TransferMode get_packet_mode() const override { return TRANSFER_MODE_RELIABLE; };
+ virtual int get_packet_channel() const override { return 0; }
+ virtual void disconnect_peer(int p_peer, bool p_force = false) override {}
+ virtual bool is_server() const override { return true; }
+ virtual void poll() override {}
+ virtual void close() override {}
+ virtual int get_unique_id() const override { return TARGET_PEER_SERVER; }
+ virtual ConnectionStatus get_connection_status() const override { return CONNECTION_CONNECTED; };
+};
+
class SceneMultiplayer : public MultiplayerAPI {
GDCLASS(SceneMultiplayer, MultiplayerAPI);
@@ -49,6 +74,18 @@ public:
NETWORK_COMMAND_SPAWN,
NETWORK_COMMAND_DESPAWN,
NETWORK_COMMAND_SYNC,
+ NETWORK_COMMAND_SYS,
+ };
+
+ enum SysCommands {
+ SYS_COMMAND_AUTH,
+ SYS_COMMAND_ADD_PEER,
+ SYS_COMMAND_DEL_PEER,
+ SYS_COMMAND_RELAY,
+ };
+
+ enum {
+ SYS_CMD_SIZE = 6, // Command + sys command + peer_id (+ optional payload).
};
// For each command, the 4 MSB can contain custom flags, as defined by subsystems.
@@ -65,7 +102,17 @@ public:
};
private:
+ struct PendingPeer {
+ bool local = false;
+ bool remote = false;
+ uint64_t time = 0;
+ };
+
Ref<MultiplayerPeer> multiplayer_peer;
+ MultiplayerPeer::ConnectionStatus last_connection_status = MultiplayerPeer::CONNECTION_DISCONNECTED;
+ HashMap<int, PendingPeer> pending_peers; // true if locally finalized.
+ Callable auth_callback;
+ uint64_t auth_timeout = 3000;
HashSet<int> connected_peers;
int remote_sender_id = 0;
int remote_sender_override = 0;
@@ -74,22 +121,33 @@ private:
NodePath root_path;
bool allow_object_decoding = false;
+ bool server_relay = true;
+ Ref<StreamPeerBuffer> relay_buffer;
Ref<SceneCacheInterface> cache;
Ref<SceneReplicationInterface> replicator;
Ref<SceneRPCInterface> rpc;
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void _profile_bandwidth(const String &p_what, int p_value);
+ _FORCE_INLINE_ Error _send(const uint8_t *p_packet, int p_packet_len); // Also profiles.
+#else
+ _FORCE_INLINE_ Error _send(const uint8_t *p_packet, int p_packet_len) {
+ return multiplayer_peer->put_packet(p_packet, p_packet_len);
+ }
+#endif
+
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 _process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel);
void _add_peer(int p_id);
+ void _admit_peer(int p_id);
void _del_peer(int p_id);
- void _connected_to_server();
- void _connection_failed();
- void _server_disconnected();
+ void _update_status();
public:
virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
@@ -111,6 +169,17 @@ public:
void set_root_path(const NodePath &p_path);
NodePath get_root_path() const;
+ void disconnect_peer(int p_id);
+
+ Error send_auth(int p_to, Vector<uint8_t> p_bytes);
+ Error complete_auth(int p_peer);
+ void set_auth_callback(Callable p_callback);
+ Callable get_auth_callback() const;
+ void set_auth_timeout(double p_timeout);
+ double get_auth_timeout() const;
+ Vector<int> get_authenticating_peer_ids();
+
+ Error send_command(int p_to, const uint8_t *p_packet, int p_packet_len); // Used internally to relay packets when needed.
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);
@@ -123,11 +192,11 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
- Ref<SceneCacheInterface> get_path_cache() { return cache; }
+ void set_server_relay_enabled(bool p_enabled);
+ bool is_server_relay_enabled() const;
-#ifdef DEBUG_ENABLED
- void profile_bandwidth(const String &p_inout, int p_size);
-#endif
+ Ref<SceneCacheInterface> get_path_cache() { return cache; }
+ Ref<SceneReplicationInterface> get_replicator() { return replicator; }
SceneMultiplayer();
~SceneMultiplayer();
diff --git a/modules/multiplayer/scene_replication_config.cpp b/modules/multiplayer/scene_replication_config.cpp
index ae06516b7b..c8621e7357 100644
--- a/modules/multiplayer/scene_replication_config.cpp
+++ b/modules/multiplayer/scene_replication_config.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_replication_config.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -34,11 +34,11 @@
#include "scene/main/node.h"
bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
+ String prop_name = p_name;
- if (name.begins_with("properties/")) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
+ if (prop_name.begins_with("properties/")) {
+ int idx = prop_name.get_slicec('/', 1).to_int();
+ String what = prop_name.get_slicec('/', 2);
if (properties.size() == idx && what == "path") {
ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false);
@@ -72,11 +72,11 @@ bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_val
}
bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const {
- String name = p_name;
+ String prop_name = p_name;
- if (name.begins_with("properties/")) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
+ if (prop_name.begins_with("properties/")) {
+ int idx = prop_name.get_slicec('/', 1).to_int();
+ String what = prop_name.get_slicec('/', 2);
ERR_FAIL_INDEX_V(idx, properties.size(), false);
const ReplicationProperty &prop = properties[idx];
if (what == "path") {
diff --git a/modules/multiplayer/scene_replication_config.h b/modules/multiplayer/scene_replication_config.h
index ab3658d2a7..addfec4da3 100644
--- a/modules/multiplayer/scene_replication_config.h
+++ b/modules/multiplayer/scene_replication_config.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_replication_config.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp
index c89270fbe8..233ff76c7d 100644
--- a/modules/multiplayer/scene_replication_interface.cpp
+++ b/modules/multiplayer/scene_replication_interface.cpp
@@ -1,85 +1,152 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_replication_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "scene_multiplayer.h"
+
+#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "scene/main/node.h"
-
-#include "multiplayer_spawner.h"
-#include "multiplayer_synchronizer.h"
-#include "scene_multiplayer.h"
+#include "scene/scene_string_names.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);
+#ifdef DEBUG_ENABLED
+_FORCE_INLINE_ void SceneReplicationInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) {
+ if (EngineDebugger::is_profiling("multiplayer:replication")) {
+ Array values;
+ values.push_back(p_what);
+ values.push_back(p_id);
+ values.push_back(p_size);
+ EngineDebugger::profiler_add_frame_data("multiplayer:replication", values);
+ }
+}
+#endif
+
+SceneReplicationInterface::TrackedNode &SceneReplicationInterface::_track(const ObjectID &p_id) {
+ if (!tracked_nodes.has(p_id)) {
+ tracked_nodes[p_id] = TrackedNode(p_id);
+ Node *node = get_id_as<Node>(p_id);
+ node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationInterface::_untrack).bind(p_id), Node::CONNECT_ONE_SHOT);
+ }
+ return tracked_nodes[p_id];
+}
+
+void SceneReplicationInterface::_untrack(const ObjectID &p_id) {
+ if (!tracked_nodes.has(p_id)) {
+ return;
+ }
+ 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.spawn_nodes.erase(p_id);
+ }
+ }
+}
+
+void SceneReplicationInterface::_free_remotes(const PeerInfo &p_info) {
+ for (const KeyValue<uint32_t, ObjectID> &E : p_info.recv_nodes) {
+ Node *node = tracked_nodes.has(E.value) ? get_id_as<Node>(E.value) : nullptr;
ERR_CONTINUE(!node);
- node->queue_delete();
+ node->queue_free();
}
}
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()) {
+ peers_info[p_id] = PeerInfo();
+ for (const ObjectID &oid : 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);
- }
+ for (const ObjectID &oid : sync_nodes) {
+ _update_sync_visibility(p_id, get_id_as<MultiplayerSynchronizer>(oid));
}
} else {
- _free_remotes(p_id);
- rep_state->on_peer_change(p_id, p_connected);
+ ERR_FAIL_COND(!peers_info.has(p_id));
+ _free_remotes(peers_info[p_id]);
+ peers_info.erase(p_id);
}
}
void SceneReplicationInterface::on_reset() {
- for (int pid : rep_state->get_peers()) {
- _free_remotes(pid);
+ for (const KeyValue<int, PeerInfo> &E : peers_info) {
+ _free_remotes(E.value);
+ }
+ peers_info.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;
+ }
+ for (const ObjectID &oid : sync_nodes) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
+ ERR_CONTINUE(!sync);
+ sync->reset();
}
- rep_state->reset();
+ last_net_id = 0;
}
void SceneReplicationInterface::on_network_process() {
+ // Prevent endless stalling in case of unforseen spawn errors.
+ if (spawn_queue.size()) {
+ ERR_PRINT("An error happened during last spawn, this usually means the 'ready' signal was not emitted by the spawned node.");
+ for (const ObjectID &oid : spawn_queue) {
+ Node *node = get_id_as<Node>(oid);
+ ERR_CONTINUE(!node);
+ if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready))) {
+ node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready));
+ }
+ }
+ spawn_queue.clear();
+ }
+
+ // Process timed syncs.
uint64_t msec = OS::get_singleton()->get_ticks_msec();
- for (int peer : rep_state->get_peers()) {
- _send_sync(peer, msec);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ const HashSet<ObjectID> to_sync = E.value.sync_nodes;
+ if (to_sync.is_empty()) {
+ continue; // Nothing to sync
+ }
+ uint16_t sync_net_time = ++E.value.last_sent_sync;
+ _send_sync(E.key, to_sync, sync_net_time, msec);
}
}
@@ -88,17 +155,44 @@ Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
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);
+ // Track node.
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);
+ TrackedNode &tobj = _track(oid);
+
+ // Spawn state needs to be callected after "ready", but the spawn order follows "enter_tree".
+ ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
+ tobj.spawner = spawner->get_instance_id();
+ spawn_queue.insert(oid);
+ node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &SceneReplicationInterface::_node_ready).bind(oid), Node::CONNECT_ONE_SHOT);
return OK;
}
+void SceneReplicationInterface::_node_ready(const ObjectID &p_oid) {
+ ERR_FAIL_COND(!spawn_queue.has(p_oid)); // Bug.
+
+ // If we are a nested spawn, we need to wait until the parent is ready.
+ if (p_oid != *(spawn_queue.begin())) {
+ return;
+ }
+
+ for (const ObjectID &oid : spawn_queue) {
+ ERR_CONTINUE(!tracked_nodes.has(oid));
+
+ TrackedNode &tobj = tracked_nodes[oid];
+ MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tobj.spawner);
+ ERR_CONTINUE(!spawner);
+
+ spawned_nodes.insert(oid);
+ if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) {
+ if (tobj.net_id == 0) {
+ tobj.net_id = ++last_net_id;
+ }
+ _update_spawn_visibility(0, oid);
+ }
+ }
+ spawn_queue.clear();
+}
+
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);
@@ -109,14 +203,22 @@ Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
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)) {
+ for (const KeyValue<int, PeerInfo> &E : peers_info) {
+ if (!E.value.spawn_nodes.has(oid)) {
continue;
}
- _send_raw(packet_cache.ptr(), len, pid, true);
+ _send_raw(packet_cache.ptr(), len, E.key, true);
}
// Also remove spawner tracking from the replication state.
- return rep_state->config_del_spawn(node, spawner);
+ ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.spawner != 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 SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
@@ -125,25 +227,40 @@ Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_c
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);
+ // Add to synchronizer list.
+ TrackedNode &tobj = _track(p_obj->get_instance_id());
+ const ObjectID sid = sync->get_instance_id();
+ tobj.synchronizers.insert(sid);
+ sync_nodes.insert(sid);
+
+ // Update visibility.
+ sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed).bind(sync->get_instance_id()));
+ _update_sync_visibility(0, sync);
+
+ if (pending_spawn == p_obj->get_instance_id() && sync->get_multiplayer_authority() == pending_spawn_remote) {
+ // Try to apply synchronizer Net ID
+ ERR_FAIL_COND_V_MSG(pending_sync_net_ids.is_empty(), ERR_INVALID_DATA, vformat("The MultiplayerSynchronizer at path \"%s\" is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the \"_ready\" callback. Make sure to only change the authority of multiplayer synchronizers during \"_enter_tree\" or the \"_spawn_custom\" callback of their multiplayer spawner.", sync->get_path()));
+ ERR_FAIL_COND_V(!peers_info.has(pending_spawn_remote), ERR_INVALID_DATA);
+ uint32_t net_id = pending_sync_net_ids[0];
+ pending_sync_net_ids.pop_front();
+ peers_info[pending_spawn_remote].recv_sync_ids[net_id] = sync->get_instance_id();
+
+ // Try to apply spawn state (before ready).
+ if (pending_buffer_size > 0) {
+ ERR_FAIL_COND_V(!node || sync->get_replication_config().is_null(), ERR_UNCONFIGURED);
+ int consumed = 0;
+ const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
+ Vector<Variant> vars;
+ vars.resize(props.size());
+ Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
+ ERR_FAIL_COND_V(err, err);
+ if (consumed > 0) {
+ pending_buffer += consumed;
+ pending_buffer_size -= consumed;
+ err = MultiplayerSynchronizer::set_state(props, node, vars);
+ ERR_FAIL_COND_V(err, err);
+ }
+ }
}
return OK;
}
@@ -154,59 +271,137 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
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);
+ // Untrack synchronizer.
+ const ObjectID oid = node->get_instance_id();
+ const ObjectID sid = sync->get_instance_id();
+ ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ tobj.synchronizers.erase(sid);
+ sync_nodes.erase(sid);
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
+ E.value.sync_nodes.erase(sid);
+ if (sync->get_net_id()) {
+ E.value.recv_sync_ids.erase(sync->get_net_id());
+ }
+ }
+ return OK;
}
-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);
+void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_sid) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(p_sid);
+ ERR_FAIL_COND(!sync); // Bug.
+ Node *node = sync->get_root_node();
+ ERR_FAIL_COND(!node); // Bug.
+ const ObjectID oid = node->get_instance_id();
+ if (spawned_nodes.has(oid) && p_peer != multiplayer->get_unique_id()) {
+ _update_spawn_visibility(p_peer, oid);
}
- if (rep_state->is_synced_node(p_oid)) {
- _update_sync_visibility(p_peer, p_oid);
+ _update_sync_visibility(p_peer, sync);
+}
+
+bool SceneReplicationInterface::is_rpc_visible(const ObjectID &p_oid, int p_peer) const {
+ if (!tracked_nodes.has(p_oid)) {
+ return true; // Untracked nodes are always visible to RPCs.
+ }
+ ERR_FAIL_COND_V(p_peer < 0, false);
+ const TrackedNode &tnode = tracked_nodes[p_oid];
+ if (tnode.synchronizers.is_empty()) {
+ return true; // No synchronizers means no visibility restrictions.
+ }
+ if (tnode.remote_peer && uint32_t(p_peer) == tnode.remote_peer) {
+ return true; // RPCs on spawned nodes are always visible to spawner.
+ } else if (spawned_nodes.has(p_oid)) {
+ // It's a spwaned node we control, this can be fast
+ if (p_peer) {
+ return peers_info.has(p_peer) && peers_info[p_peer].spawn_nodes.has(p_oid);
+ } else {
+ for (const KeyValue<int, PeerInfo> &E : peers_info) {
+ if (!E.value.spawn_nodes.has(p_oid)) {
+ return false; // Not public.
+ }
+ }
+ return true; // All peers have this node.
+ }
+ } else {
+ // Cycle object synchronizers to check visibility.
+ for (const ObjectID &sid : tnode.synchronizers) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid);
+ ERR_CONTINUE(!sync);
+ // RPC visibility is composed using OR when multiple synchronizers are present.
+ // Note that we don't really care about authority here which may lead to unexpected
+ // results when using multiple synchronizers to control the same node.
+ if (sync->is_visible_to(p_peer)) {
+ return true;
+ }
+ }
+ return false; // Not visible.
}
}
-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);
+Error SceneReplicationInterface::_update_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync) {
+ ERR_FAIL_COND_V(!p_sync, ERR_BUG);
+ if (!multiplayer->has_multiplayer_peer() || !p_sync->is_multiplayer_authority() || p_peer == multiplayer->get_unique_id()) {
+ return OK;
+ }
+
+ const ObjectID &sid = p_sync->get_instance_id();
+ bool is_visible = p_sync->is_visible_to(p_peer);
if (p_peer == 0) {
- for (int pid : rep_state->get_peers()) {
+ for (KeyValue<int, PeerInfo> &E : peers_info) {
// 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) {
+ bool is_visible_to_peer = is_visible || p_sync->is_visible_to(E.key);
+ if (is_visible_to_peer == E.value.sync_nodes.has(sid)) {
continue;
}
- if (is_visible) {
- rep_state->peer_add_sync(pid, p_oid);
+ if (is_visible_to_peer) {
+ E.value.sync_nodes.insert(sid);
} else {
- rep_state->peer_del_sync(pid, p_oid);
+ E.value.sync_nodes.erase(sid);
}
}
return OK;
} else {
- if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ if (is_visible == peers_info[p_peer].sync_nodes.has(sid)) {
return OK;
}
if (is_visible) {
- return rep_state->peer_add_sync(p_peer, p_oid);
+ peers_info[p_peer].sync_nodes.insert(sid);
} else {
- return rep_state->peer_del_sync(p_peer, p_oid);
+ peers_info[p_peer].sync_nodes.erase(sid);
}
+ return OK;
}
}
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));
+ const TrackedNode *tnode = tracked_nodes.getptr(p_oid);
+ ERR_FAIL_COND_V(!tnode, ERR_BUG);
+ MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tnode->spawner);
+ Node *node = get_id_as<Node>(p_oid);
ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG);
- bool is_visible = !sync || sync->is_visible_to(p_peer);
+ ERR_FAIL_COND_V(!tracked_nodes.has(p_oid), ERR_BUG);
+ const HashSet<ObjectID> synchronizers = tracked_nodes[p_oid].synchronizers;
+ bool is_visible = true;
+ for (const ObjectID &sid : synchronizers) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid);
+ ERR_CONTINUE(!sync);
+ if (!sync->is_multiplayer_authority()) {
+ continue;
+ }
+ // Spawn visibility is composed using OR when multiple synchronizers are present.
+ if (sync->is_visible_to(p_peer)) {
+ is_visible = true;
+ break;
+ }
+ is_visible = false;
+ }
// 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)) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ if (is_visible == peers_info[p_peer].spawn_nodes.has(p_oid)) {
return OK;
}
if (is_visible) {
@@ -216,33 +411,37 @@ Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const Obje
}
} 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);
+ for (const KeyValue<int, PeerInfo> &E : peers_info) {
+ if (is_visible) {
+ // This is fast, since the the object is visible to everyone, we don't need to check each peer.
+ if (E.value.spawn_nodes.has(p_oid)) {
+ // Already spawned.
+ continue;
+ }
+ to_spawn.insert(E.key);
} else {
- to_despawn.insert(pid);
+ // Need to check visibility for each peer.
+ _update_spawn_visibility(E.key, p_oid);
}
}
}
if (to_spawn.size()) {
int len = 0;
- _make_spawn_packet(node, len);
+ _make_spawn_packet(node, spawner, len);
for (int pid : to_spawn) {
+ ERR_CONTINUE(!peers_info.has(pid));
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);
+ peers_info[pid].spawn_nodes.insert(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);
+ ERR_CONTINUE(!peers_info.has(pid));
+ peers_info[pid].spawn_nodes.erase(p_oid);
_send_raw(packet_cache.ptr(), len, pid, true);
}
}
@@ -251,34 +450,28 @@ Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const Obje
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);
+ return multiplayer->send_command(p_peer, p_buffer, p_size);
}
-Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
- ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
+Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) {
+ ERR_FAIL_COND_V(!multiplayer || !p_node || !p_spawner, 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);
+ const TrackedNode *tnode = tracked_nodes.getptr(oid);
+ ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER);
- uint32_t nid = rep_state->get_net_id(oid);
+ uint32_t nid = tnode->net_id;
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);
+ uint8_t scene_id = p_spawner->find_spawnable_scene_index_from_object(oid);
bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
- Variant spawn_arg = spawner->get_spawn_argument(oid);
+ Variant spawn_arg = p_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);
@@ -286,31 +479,51 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
}
// Prepare spawn state.
+ List<NodePath> state_props;
+ List<uint32_t> sync_ids;
+ const HashSet<ObjectID> synchronizers = tnode->synchronizers;
+ for (const ObjectID &sid : synchronizers) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(sid);
+ if (!sync->is_multiplayer_authority()) {
+ continue;
+ }
+ ERR_CONTINUE(!sync);
+ ERR_FAIL_COND_V(sync->get_replication_config().is_null(), ERR_BUG);
+ for (const NodePath &prop : sync->get_replication_config()->get_spawn_properties()) {
+ state_props.push_back(prop);
+ }
+ // Ensure the synchronizer has an ID.
+ if (sync->get_net_id() == 0) {
+ sync->set_net_id(++last_net_id);
+ }
+ sync_ids.push_back(sync->get_net_id());
+ }
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);
+ if (state_props.size()) {
+ Error err = MultiplayerSynchronizer::get_state(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);
+ int path_id = multiplayer->get_path_cache()->make_object_cache(p_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);
+ MAKE_ROOM(1 + 1 + 4 + 4 + 4 + 4 * sync_ids.size() + 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(sync_ids.size(), &ptr[ofs]);
ofs += encode_uint32(nlen, &ptr[ofs]);
+ for (uint32_t snid : sync_ids) {
+ ofs += encode_uint32(snid, &ptr[ofs]);
+ }
ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
// Write args
if (is_custom) {
@@ -331,18 +544,20 @@ Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) {
Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) {
const ObjectID oid = p_node->get_instance_id();
+ const TrackedNode *tnode = tracked_nodes.getptr(oid);
+ ERR_FAIL_COND_V(!tnode, ERR_INVALID_PARAMETER);
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);
+ uint32_t nid = tnode->net_id;
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");
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 18, ERR_INVALID_DATA, "Invalid spawn packet received");
int ofs = 1; // The spawn/despawn command.
uint8_t scene_id = p_buffer[ofs];
ofs += 1;
@@ -354,9 +569,16 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
uint32_t net_id = decode_uint32(&p_buffer[ofs]);
ofs += 4;
+ uint32_t sync_len = 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 + (sync_len * 4) > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len + (sync_len * 4)));
+ List<uint32_t> sync_ids;
+ for (uint32_t i = 0; i < sync_len; i++) {
+ sync_ids.push_back(decode_uint32(&p_buffer[ofs]));
+ ofs += 4;
+ }
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.
@@ -387,18 +609,35 @@ Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_b
}
ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
node->set_name(name);
- rep_state->peer_add_remote(p_from, net_id, node, spawner);
+
+ // Add and track remote
+ ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(peers_info[p_from].recv_nodes.has(net_id), ERR_ALREADY_IN_USE);
+ ObjectID oid = node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ tobj.spawner = spawner->get_instance_id();
+ tobj.net_id = net_id;
+ tobj.remote_peer = p_from;
+ peers_info[p_from].recv_nodes[net_id] = oid;
+
// 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;
- }
+ pending_spawn = node->get_instance_id();
+ pending_spawn_remote = p_from;
+ pending_buffer_size = p_buffer_len - ofs;
+ pending_buffer = pending_buffer_size > 0 ? &p_buffer[ofs] : nullptr;
+ pending_sync_net_ids = sync_ids;
+
parent->add_child(node);
+ spawner->emit_signal(SNAME("spawned"), node);
+
pending_spawn = ObjectID();
+ pending_spawn_remote = 0;
pending_buffer = nullptr;
pending_buffer_size = 0;
+ if (pending_sync_net_ids.size()) {
+ pending_sync_net_ids.clear();
+ ERR_FAIL_V(ERR_INVALID_DATA); // Should have been consumed.
+ }
return OK;
}
@@ -407,38 +646,48 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p
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);
+
+ // Untrack remote
+ ERR_FAIL_COND_V(!peers_info.has(p_from), ERR_UNAUTHORIZED);
+ PeerInfo &pinfo = peers_info[p_from];
+ ERR_FAIL_COND_V(!pinfo.recv_nodes.has(net_id), ERR_UNAUTHORIZED);
+ Node *node = get_id_as<Node>(pinfo.recv_nodes[net_id]);
ERR_FAIL_COND_V(!node, ERR_BUG);
+ pinfo.recv_nodes.erase(net_id);
+
+ const ObjectID oid = node->get_instance_id();
+ ERR_FAIL_COND_V(!tracked_nodes.has(oid), ERR_BUG);
+ MultiplayerSpawner *spawner = get_id_as<MultiplayerSpawner>(tracked_nodes[oid].spawner);
+ ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
+ ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
+
if (node->get_parent() != nullptr) {
node->get_parent()->remove_child(node);
}
- node->queue_delete();
+ node->queue_free();
+ spawner->emit_signal(SNAME("despawned"), node);
+
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;
- }
+void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec) {
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]);
+ ofs += encode_uint16(p_sync_net_time, &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)) {
+ for (const ObjectID &oid : p_synchronizers) {
+ MultiplayerSynchronizer *sync = get_id_as<MultiplayerSynchronizer>(oid);
+ ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid() || !sync->is_multiplayer_authority());
+ if (!sync->update_outbound_sync_time(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);
+
+ Node *node = sync->get_root_node();
ERR_CONTINUE(!node);
- uint32_t net_id = rep_state->get_net_id(oid);
+ uint32_t net_id = sync->get_net_id();
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);
@@ -446,7 +695,7 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
if (net_id == 0) {
// First time path based ID.
net_id = path_id | 0x80000000;
- rep_state->set_net_id(oid, net_id | 0x80000000);
+ sync->set_net_id(net_id | 0x80000000);
}
if (!verified) {
// The path based sync is not yet confirmed, skipping.
@@ -469,11 +718,14 @@ void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
ofs = 3;
}
if (size) {
- ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
+ ofs += encode_uint32(sync->get_net_id(), &ptr[ofs]);
ofs += encode_uint32(size, &ptr[ofs]);
MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
ofs += size;
}
+#ifdef DEBUG_ENABLED
+ _profile_node_data("sync_out", oid, size);
+#endif
}
if (ofs > 3) {
// Got some left over to send.
@@ -485,33 +737,32 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
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;
+ MultiplayerSynchronizer *sync = 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);
+ sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_path_cache()->get_cached_object(p_from, net_id & 0x7FFFFFFF));
+ } else if (peers_info[p_from].recv_sync_ids.has(net_id)) {
+ const ObjectID &sid = peers_info[p_from].recv_sync_ids[net_id];
+ sync = get_id_as<MultiplayerSynchronizer>(sid);
}
- if (!node) {
+ if (!sync) {
// Not received yet.
ofs += size;
continue;
}
- const ObjectID oid = node->get_instance_id();
- if (!rep_state->update_last_node_sync(oid, time)) {
+ Node *node = sync->get_root_node();
+ if (sync->get_multiplayer_authority() != p_from || !node) {
+ ERR_CONTINUE(true);
+ }
+ if (!sync->update_inbound_sync_time(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;
@@ -522,6 +773,9 @@ Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_bu
err = MultiplayerSynchronizer::set_state(props, node, vars);
ERR_FAIL_COND_V(err, err);
ofs += size;
+#ifdef DEBUG_ENABLED
+ _profile_node_data("sync_in", sync->get_instance_id(), size);
+#endif
}
return OK;
}
diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h
index 8981647429..cf45db2138 100644
--- a/modules/multiplayer/scene_replication_interface.h
+++ b/modules/multiplayer/scene_replication_interface.h
@@ -1,39 +1,40 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_replication_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/object/ref_counted.h"
-#include "scene_replication_state.h"
+#include "multiplayer_spawner.h"
+#include "multiplayer_synchronizer.h"
class SceneMultiplayer;
@@ -41,25 +42,75 @@ 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);
+ struct TrackedNode {
+ ObjectID id;
+ uint32_t net_id = 0;
+ uint32_t remote_peer = 0;
+ ObjectID spawner;
+ HashSet<ObjectID> synchronizers;
+
+ bool operator==(const ObjectID &p_other) { return id == p_other; }
+
+ 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_sync_ids;
+ HashMap<uint32_t, ObjectID> recv_nodes;
+ uint16_t last_sent_sync = 0;
+ };
+
+ // Replication state.
+ HashMap<int, PeerInfo> peers_info;
+ uint32_t last_net_id = 0;
+ HashMap<ObjectID, TrackedNode> tracked_nodes;
+ HashSet<ObjectID> spawned_nodes;
+ HashSet<ObjectID> sync_nodes;
+
+ // Pending local spawn information (handles spawning nested nodes during ready).
+ HashSet<ObjectID> spawn_queue;
+
+ // Pending remote spawn information.
+ ObjectID pending_spawn;
+ int pending_spawn_remote = 0;
+ const uint8_t *pending_buffer = nullptr;
+ int pending_buffer_size = 0;
+ List<uint32_t> pending_sync_net_ids;
+
+ // Replicator config.
+ SceneMultiplayer *multiplayer = nullptr;
+ PackedByteArray packet_cache;
+ int sync_mtu = 1350; // Highly dependent on underlying protocol.
+
+ TrackedNode &_track(const ObjectID &p_id);
+ void _untrack(const ObjectID &p_id);
+ void _node_ready(const ObjectID &p_oid);
+
+ void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_msec);
+ Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, 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_sync_visibility(int p_peer, MultiplayerSynchronizer *p_sync);
Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid);
- void _free_remotes(int p_peer);
+ void _free_remotes(const PeerInfo &p_info);
- Ref<SceneReplicationState> rep_state;
- SceneMultiplayer *multiplayer = nullptr;
- PackedByteArray packet_cache;
- int sync_mtu = 1350; // Highly dependent on underlying protocol.
+ template <class T>
+ static T *get_id_as(const ObjectID &p_id) {
+ return p_id.is_valid() ? Object::cast_to<T>(ObjectDB::get_instance(p_id)) : nullptr;
+ }
- // An hack to apply the initial state before ready.
- ObjectID pending_spawn;
- const uint8_t *pending_buffer = nullptr;
- int pending_buffer_size = 0;
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id, int p_size);
+#endif
public:
static void make_default();
@@ -77,8 +128,9 @@ public:
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);
+ bool is_rpc_visible(const ObjectID &p_oid, int p_peer) const;
+
SceneReplicationInterface(SceneMultiplayer *p_multiplayer) {
- rep_state.instantiate();
multiplayer = p_multiplayer;
}
};
diff --git a/modules/multiplayer/scene_replication_state.cpp b/modules/multiplayer/scene_replication_state.cpp
deleted file mode 100644
index 25442bb7fa..0000000000
--- a/modules/multiplayer/scene_replication_state.cpp
+++ /dev/null
@@ -1,267 +0,0 @@
-/*************************************************************************/
-/* 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
deleted file mode 100644
index bdff6ae3b7..0000000000
--- a/modules/multiplayer/scene_replication_state.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*************************************************************************/
-/* 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
index 65090b9316..4e20e8fd3a 100644
--- a/modules/multiplayer/scene_rpc_interface.cpp
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_rpc_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -52,16 +52,15 @@
#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")) {
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) {
+ if (EngineDebugger::is_profiling("multiplayer:rpc")) {
Array values;
- values.push_back(p_id);
values.push_back(p_what);
- EngineDebugger::profiler_add_frame_data("rpc", values);
+ values.push_back(p_id);
+ values.push_back(p_size);
+ EngineDebugger::profiler_add_frame_data("multiplayer: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.
@@ -83,7 +82,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no
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);
+ ERR_CONTINUE(names[i].get_type() != Variant::STRING && names[i].get_type() != Variant::STRING_NAME);
String name = names[i].operator String();
ERR_CONTINUE(config[name].get_type() != Variant::DICTIONARY);
ERR_CONTINUE(!config[name].operator Dictionary().has("rpc_mode"));
@@ -277,7 +276,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
argp.resize(argc);
#ifdef DEBUG_ENABLED
- _profile_node_data("rpc_in", p_node->get_instance_id());
+ _profile_node_data("rpc_in", p_node->get_instance_id(), p_packet_len);
#endif
int out;
@@ -296,7 +295,7 @@ void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_i
}
}
-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) {
+void SceneRPCInterface::_send_rpc(Node *p_node, 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.");
@@ -312,12 +311,35 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
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);
+ // See if all peers have cached path (if so, call can be fast) while building the RPC target list.
+ HashSet<int> targets;
+ Ref<SceneCacheInterface> cache = multiplayer->get_path_cache();
+ int psc_id = -1;
+ bool has_all_peers = true;
+ const ObjectID oid = p_node->get_instance_id();
+ if (p_to > 0) {
+ ERR_FAIL_COND_MSG(!multiplayer->get_replicator()->is_rpc_visible(oid, p_to), "Attempt to call an RPC to a peer that cannot see this node. Peer ID: " + itos(p_to));
+ targets.insert(p_to);
+ has_all_peers = cache->send_object_cache(p_node, p_to, psc_id);
+ } else {
+ bool restricted = !multiplayer->get_replicator()->is_rpc_visible(oid, 0);
+ for (const int &P : multiplayer->get_connected_peers()) {
+ if (p_to < 0 && P == -p_to) {
+ continue; // Excluded peer.
+ }
+ if (restricted && !multiplayer->get_replicator()->is_rpc_visible(oid, P)) {
+ continue; // Not visible to this peer.
+ }
+ targets.insert(P);
+ bool has_peer = cache->send_object_cache(p_node, P, psc_id);
+ has_all_peers = has_all_peers && has_peer;
+ }
+ }
+ if (targets.is_empty()) {
+ return; // No one in sight.
+ }
// Create base packet, lots of hardcode because it must be tight.
-
int ofs = 0;
#define MAKE_ROOM(m_amount) \
@@ -399,21 +421,21 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
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);
+ _profile_node_data("rpc_out", p_node->get_instance_id(), ofs);
#endif
+ // 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);
+
// 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.
+ for (const int P : targets) {
+ multiplayer->send_command(P, packet_cache.ptr(), ofs);
+ }
} 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);
@@ -421,33 +443,23 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con
// 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());
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_node->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.
- }
-
+ // Not all verified path, so check which needs the longer packet.
+ for (const int P : targets) {
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);
+ multiplayer->send_command(P, 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);
+ multiplayer->send_command(P, packet_cache.ptr(), ofs + path_len);
}
}
}
@@ -480,10 +492,6 @@ Error SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_
}
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);
}
diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h
index aa9be525a2..b40169a63b 100644
--- a/modules/multiplayer/scene_rpc_interface.h
+++ b/modules/multiplayer/scene_rpc_interface.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* scene_rpc_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -81,8 +81,11 @@ private:
HashMap<ObjectID, RPCConfigCache> rpc_cache;
+#ifdef DEBUG_ENABLED
+ _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id, int p_size);
+#endif
+
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);
diff --git a/modules/navigation/SCsub b/modules/navigation/SCsub
index 24a6b12639..0b0822db2d 100644
--- a/modules/navigation/SCsub
+++ b/modules/navigation/SCsub
@@ -57,7 +57,7 @@ env.modules_sources += thirdparty_obj
module_obj = []
env_navigation.add_source_files(module_obj, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_navigation.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index c243e3e6e3..54f7abda8d 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* navigation_mesh_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* navigation_mesh_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "navigation_mesh_editor_plugin.h"
@@ -110,7 +110,7 @@ NavigationMeshEditor::NavigationMeshEditor() {
button_reset->set_flat(true);
bake_hbox->add_child(button_reset);
// No button text, we only use a revert icon which is set when entering the tree.
- button_reset->set_tooltip(TTR("Clear the navigation mesh."));
+ button_reset->set_tooltip_text(TTR("Clear the navigation mesh."));
button_reset->connect("pressed", callable_mp(this, &NavigationMeshEditor::_clear_pressed));
bake_info = memnew(Label);
@@ -145,7 +145,7 @@ void NavigationMeshEditorPlugin::make_visible(bool p_visible) {
NavigationMeshEditorPlugin::NavigationMeshEditorPlugin() {
navigation_mesh_editor = memnew(NavigationMeshEditor);
- EditorNode::get_singleton()->get_main_control()->add_child(navigation_mesh_editor);
+ EditorNode::get_singleton()->get_main_screen_control()->add_child(navigation_mesh_editor);
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox);
navigation_mesh_editor->hide();
navigation_mesh_editor->bake_hbox->hide();
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index bc9e4185b7..010be411d6 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* navigation_mesh_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* navigation_mesh_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAVIGATION_MESH_EDITOR_PLUGIN_H
#define NAVIGATION_MESH_EDITOR_PLUGIN_H
@@ -35,6 +35,8 @@
#include "editor/editor_plugin.h"
+class AcceptDialog;
+class HBoxContainer;
class NavigationRegion3D;
class NavigationMeshEditor : public Control {
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index 2cdb5b7cb4..9b5d78d465 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godot_navigation_server.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godot_navigation_server.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "godot_navigation_server.h"
@@ -36,6 +36,8 @@
#include "navigation_mesh_generator.h"
#endif
+using namespace NavigationUtilities;
+
/// Creates a struct for each function and a function that once called creates
/// an instance of that struct with the submitted parameters.
/// Then, that struct is stored in an array; the `sync` function consume that array.
@@ -50,7 +52,7 @@
server->MERGE(_cmd_, F_NAME)(d_0); \
} \
}; \
- void GodotNavigationServer::F_NAME(T_0 D_0) const { \
+ void GodotNavigationServer::F_NAME(T_0 D_0) { \
auto cmd = memnew(MERGE(F_NAME, _command)( \
D_0)); \
add_command(cmd); \
@@ -71,7 +73,7 @@
server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
} \
}; \
- void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) { \
auto cmd = memnew(MERGE(F_NAME, _command)( \
D_0, \
D_1)); \
@@ -79,34 +81,34 @@
} \
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
-#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
- struct MERGE(F_NAME, _command) : public SetCommand { \
- T_0 d_0; \
- T_1 d_1; \
- T_2 d_2; \
- T_3 d_3; \
- MERGE(F_NAME, _command) \
- ( \
- T_0 p_d_0, \
- T_1 p_d_1, \
- T_2 p_d_2, \
- T_3 p_d_3) : \
- d_0(p_d_0), \
- d_1(p_d_1), \
- d_2(p_d_2), \
- d_3(p_d_3) {} \
- virtual void exec(GodotNavigationServer *server) override { \
- server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
- } \
- }; \
- void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
- auto cmd = memnew(MERGE(F_NAME, _command)( \
- D_0, \
- D_1, \
- D_2, \
- D_3)); \
- add_command(cmd); \
- } \
+#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ T_2 d_2; \
+ T_3 d_3; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1, \
+ T_2 p_d_2, \
+ T_3 p_d_3) : \
+ d_0(p_d_0), \
+ d_1(p_d_1), \
+ d_2(p_d_2), \
+ d_3(p_d_3) {} \
+ virtual void exec(GodotNavigationServer *server) override { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
+ } \
+ }; \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1, \
+ D_2, \
+ D_3)); \
+ add_command(cmd); \
+ } \
void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
GodotNavigationServer::GodotNavigationServer() {}
@@ -115,16 +117,14 @@ GodotNavigationServer::~GodotNavigationServer() {
flush_queries();
}
-void GodotNavigationServer::add_command(SetCommand *command) const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- {
- MutexLock lock(commands_mutex);
- mut_this->commands.push_back(command);
- }
+void GodotNavigationServer::add_command(SetCommand *command) {
+ MutexLock lock(commands_mutex);
+
+ commands.push_back(command);
}
-Array GodotNavigationServer::get_maps() const {
- Array all_map_rids;
+TypedArray<RID> GodotNavigationServer::get_maps() const {
+ TypedArray<RID> all_map_rids;
List<RID> maps_owned;
map_owner.get_owned_list(&maps_owned);
if (maps_owned.size()) {
@@ -135,12 +135,12 @@ Array GodotNavigationServer::get_maps() const {
return all_map_rids;
}
-RID GodotNavigationServer::map_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::map_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = map_owner.make_rid();
- NavMap *space = map_owner.get_or_null(rid);
- space->set_self(rid);
+ NavMap *map = map_owner.get_or_null(rid);
+ map->set_self(rid);
return rid;
}
@@ -210,11 +210,25 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
return map->get_edge_connection_margin();
}
+COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ map->set_link_connection_radius(p_connection_radius);
+}
+
+real_t GodotNavigationServer::map_get_link_connection_radius(RID p_map) const {
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, 0);
+
+ return map->get_link_connection_radius();
+}
+
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_navigation_layers);
+ return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers, nullptr, nullptr, nullptr);
}
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 {
@@ -245,24 +259,42 @@ 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;
+TypedArray<RID> GodotNavigationServer::map_get_links(RID p_map) const {
+ TypedArray<RID> link_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, link_rids);
+
+ const LocalVector<NavLink *> links = map->get_links();
+ link_rids.resize(links.size());
+
+ for (uint32_t i = 0; i < links.size(); i++) {
+ link_rids[i] = links[i]->get_self();
+ }
+ return link_rids;
+}
+
+TypedArray<RID> GodotNavigationServer::map_get_regions(RID p_map) const {
+ TypedArray<RID> 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;
+TypedArray<RID> GodotNavigationServer::map_get_agents(RID p_map) const {
+ TypedArray<RID> 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();
}
@@ -272,6 +304,7 @@ Array GodotNavigationServer::map_get_agents(RID p_map) const {
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();
}
@@ -281,15 +314,16 @@ RID GodotNavigationServer::region_get_map(RID p_region) const {
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);
+RID GodotNavigationServer::region_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = region_owner.make_rid();
NavRegion *reg = region_owner.get_or_null(rid);
reg->set_self(rid);
@@ -355,9 +389,24 @@ real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
return region->get_travel_cost();
}
+COMMAND_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+
+ region->set_owner_id(p_owner_id);
+}
+
+ObjectID GodotNavigationServer::region_get_owner_id(RID p_region) const {
+ const NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, ObjectID());
+
+ return region->get_owner_id();
+}
+
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();
@@ -379,20 +428,20 @@ uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const
return region->get_navigation_layers();
}
-COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
+COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
- region->set_mesh(p_nav_mesh);
+ region->set_mesh(p_navigation_mesh);
}
-void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const {
- ERR_FAIL_COND(r_mesh.is_null());
- ERR_FAIL_COND(p_node == nullptr);
+void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) {
+ ERR_FAIL_COND(p_navigation_mesh.is_null());
+ ERR_FAIL_COND(p_root_node == nullptr);
#ifndef _3D_DISABLED
- NavigationMeshGenerator::get_singleton()->clear(r_mesh);
- NavigationMeshGenerator::get_singleton()->bake(r_mesh, p_node);
+ NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh);
+ NavigationMeshGenerator::get_singleton()->bake(p_navigation_mesh, p_root_node);
#endif
}
@@ -417,9 +466,148 @@ Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, i
return region->get_connection_pathway_end(p_connection_id);
}
-RID GodotNavigationServer::agent_create() const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
+RID GodotNavigationServer::link_create() {
+ MutexLock lock(operations_mutex);
+
+ RID rid = link_owner.make_rid();
+ NavLink *link = link_owner.get_or_null(rid);
+ link->set_self(rid);
+ return rid;
+}
+
+COMMAND_2(link_set_map, RID, p_link, RID, p_map) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ if (link->get_map() != nullptr) {
+ if (link->get_map()->get_self() == p_map) {
+ return; // Pointless
+ }
+
+ link->get_map()->remove_link(link);
+ link->set_map(nullptr);
+ }
+
+ if (p_map.is_valid()) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ map->add_link(link);
+ link->set_map(map);
+ }
+}
+
+RID GodotNavigationServer::link_get_map(const RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, RID());
+
+ if (link->get_map()) {
+ return link->get_map()->get_self();
+ }
+ return RID();
+}
+
+COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_bidirectional(p_bidirectional);
+}
+
+bool GodotNavigationServer::link_is_bidirectional(RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, false);
+
+ return link->is_bidirectional();
+}
+
+COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_navigation_layers(p_navigation_layers);
+}
+
+uint32_t GodotNavigationServer::link_get_navigation_layers(const RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, 0);
+
+ return link->get_navigation_layers();
+}
+
+COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_start_location(p_location);
+}
+
+Vector3 GodotNavigationServer::link_get_start_location(RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, Vector3());
+
+ return link->get_start_location();
+}
+
+COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_end_location(p_location);
+}
+
+Vector3 GodotNavigationServer::link_get_end_location(RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, Vector3());
+
+ return link->get_end_location();
+}
+
+COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_enter_cost(p_enter_cost);
+}
+
+real_t GodotNavigationServer::link_get_enter_cost(const RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, 0);
+
+ return link->get_enter_cost();
+}
+
+COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_travel_cost(p_travel_cost);
+}
+
+real_t GodotNavigationServer::link_get_travel_cost(const RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, 0);
+
+ return link->get_travel_cost();
+}
+
+COMMAND_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id) {
+ NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND(link == nullptr);
+
+ link->set_owner_id(p_owner_id);
+}
+
+ObjectID GodotNavigationServer::link_get_owner_id(RID p_link) const {
+ const NavLink *link = link_owner.get_or_null(p_link);
+ ERR_FAIL_COND_V(link == nullptr, ObjectID());
+
+ return link->get_owner_id();
+}
+
+RID GodotNavigationServer::agent_create() {
+ MutexLock lock(operations_mutex);
+
RID rid = agent_owner.make_rid();
RvoAgent *agent = agent_owner.get_or_null(rid);
agent->set_self(rid);
@@ -453,11 +641,11 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
}
}
-COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
+COMMAND_2(agent_set_neighbor_distance, RID, p_agent, real_t, p_distance) {
RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->get_agent()->neighborDist_ = p_dist;
+ agent->get_agent()->neighborDist_ = p_distance;
}
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
@@ -523,14 +711,14 @@ bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
return agent->is_map_changed();
}
-COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) {
+COMMAND_4(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata) {
RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
- agent->set_callback(p_receiver == nullptr ? ObjectID() : p_receiver->get_instance_id(), p_method, p_udata);
+ agent->set_callback(p_object_id, p_method, p_udata);
if (agent->get_map()) {
- if (p_receiver == nullptr) {
+ if (p_object_id == ObjectID()) {
agent->get_map()->remove_agent_as_controlled(agent);
} else {
agent->get_map()->set_agent_as_controlled(agent);
@@ -549,6 +737,13 @@ COMMAND_1(free, RID, p_object) {
regions[i]->set_map(nullptr);
}
+ // Removes any assigned links
+ LocalVector<NavLink *> links = map->get_links();
+ for (uint32_t i = 0; i < links.size(); i++) {
+ map->remove_link(links[i]);
+ links[i]->set_map(nullptr);
+ }
+
// Remove any assigned agent
LocalVector<RvoAgent *> agents = map->get_agents();
for (uint32_t i = 0; i < agents.size(); i++) {
@@ -572,6 +767,17 @@ COMMAND_1(free, RID, p_object) {
region_owner.free(p_object);
+ } else if (link_owner.owns(p_object)) {
+ NavLink *link = link_owner.get_or_null(p_object);
+
+ // Removes this link from the map if assigned
+ if (link->get_map() != nullptr) {
+ link->get_map()->remove_link(link);
+ link->set_map(nullptr);
+ }
+
+ link_owner.free(p_object);
+
} else if (agent_owner.owns(p_object)) {
RvoAgent *agent = agent_owner.get_or_null(p_object);
@@ -588,10 +794,10 @@ COMMAND_1(free, RID, p_object) {
}
}
-void GodotNavigationServer::set_active(bool p_active) const {
- GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
- MutexLock lock(mut_this->operations_mutex);
- mut_this->active = p_active;
+void GodotNavigationServer::set_active(bool p_active) {
+ MutexLock lock(operations_mutex);
+
+ active = p_active;
}
void GodotNavigationServer::flush_queries() {
@@ -599,6 +805,7 @@ void GodotNavigationServer::flush_queries() {
// even with mutable functions.
MutexLock lock(commands_mutex);
MutexLock lock2(operations_mutex);
+
for (size_t i(0); i < commands.size(); i++) {
commands[i]->exec(this);
memdelete(commands[i]);
@@ -622,6 +829,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
return;
}
+ int _new_pm_region_count = 0;
+ int _new_pm_agent_count = 0;
+ int _new_pm_link_count = 0;
+ int _new_pm_polygon_count = 0;
+ int _new_pm_edge_count = 0;
+ int _new_pm_edge_merge_count = 0;
+ int _new_pm_edge_connection_count = 0;
+ int _new_pm_edge_free_count = 0;
+
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
MutexLock lock(operations_mutex);
@@ -630,6 +846,15 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps[i]->step(p_delta_time);
active_maps[i]->dispatch_callbacks();
+ _new_pm_region_count += active_maps[i]->get_pm_region_count();
+ _new_pm_agent_count += active_maps[i]->get_pm_agent_count();
+ _new_pm_link_count += active_maps[i]->get_pm_link_count();
+ _new_pm_polygon_count += active_maps[i]->get_pm_polygon_count();
+ _new_pm_edge_count += active_maps[i]->get_pm_edge_count();
+ _new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
+ _new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
+ _new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
+
// Emit a signal if a map changed.
const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
if (new_map_update_id != active_maps_update_id[i]) {
@@ -637,6 +862,89 @@ void GodotNavigationServer::process(real_t p_delta_time) {
active_maps_update_id[i] = new_map_update_id;
}
}
+
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
+}
+
+PathQueryResult GodotNavigationServer::_query_path(const PathQueryParameters &p_parameters) const {
+ PathQueryResult r_query_result;
+
+ const NavMap *map = map_owner.get_or_null(p_parameters.map);
+ ERR_FAIL_COND_V(map == nullptr, r_query_result);
+
+ // run the pathfinding
+
+ if (p_parameters.pathfinding_algorithm == PathfindingAlgorithm::PATHFINDING_ALGORITHM_ASTAR) {
+ // while postprocessing is still part of map.get_path() need to check and route it here for the correct "optimize" post-processing
+ if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_CORRIDORFUNNEL) {
+ r_query_result.path = map->get_path(
+ p_parameters.start_position,
+ p_parameters.target_position,
+ true,
+ p_parameters.navigation_layers,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
+ } else if (p_parameters.path_postprocessing == PathPostProcessing::PATH_POSTPROCESSING_EDGECENTERED) {
+ r_query_result.path = map->get_path(
+ p_parameters.start_position,
+ p_parameters.target_position,
+ false,
+ p_parameters.navigation_layers,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES) ? &r_query_result.path_types : nullptr,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS) ? &r_query_result.path_rids : nullptr,
+ p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS) ? &r_query_result.path_owner_ids : nullptr);
+ }
+ } else {
+ return r_query_result;
+ }
+
+ // add path postprocessing
+
+ // add path stats
+
+ return r_query_result;
+}
+
+int GodotNavigationServer::get_process_info(ProcessInfo p_info) const {
+ switch (p_info) {
+ case INFO_ACTIVE_MAPS: {
+ return active_maps.size();
+ } break;
+ case INFO_REGION_COUNT: {
+ return pm_region_count;
+ } break;
+ case INFO_AGENT_COUNT: {
+ return pm_agent_count;
+ } break;
+ case INFO_LINK_COUNT: {
+ return pm_link_count;
+ } break;
+ case INFO_POLYGON_COUNT: {
+ return pm_polygon_count;
+ } break;
+ case INFO_EDGE_COUNT: {
+ return pm_edge_count;
+ } break;
+ case INFO_EDGE_MERGE_COUNT: {
+ return pm_edge_merge_count;
+ } break;
+ case INFO_EDGE_CONNECTION_COUNT: {
+ return pm_edge_connection_count;
+ } break;
+ case INFO_EDGE_FREE_COUNT: {
+ return pm_edge_free_count;
+ } break;
+ }
+
+ return 0;
}
#undef COMMAND_1
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index da1f8cba0b..a87a88d3bc 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godot_navigation_server.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godot_navigation_server.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GODOT_NAVIGATION_SERVER_H
#define GODOT_NAVIGATION_SERVER_H
@@ -36,6 +36,7 @@
#include "core/templates/rid_owner.h"
#include "servers/navigation_server_3d.h"
+#include "nav_link.h"
#include "nav_map.h"
#include "nav_region.h"
#include "rvo_agent.h"
@@ -45,16 +46,16 @@
#define MERGE_INTERNAL(A, B) A##B
#define MERGE(A, B) MERGE_INTERNAL(A, B)
-#define COMMAND_1(F_NAME, T_0, D_0) \
- virtual void F_NAME(T_0 D_0) const override; \
+#define COMMAND_1(F_NAME, T_0, D_0) \
+ virtual void F_NAME(T_0 D_0) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0)
-#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
- virtual void F_NAME(T_0 D_0, T_1 D_1) const override; \
+#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
-#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
- virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) const override; \
+#define COMMAND_4_DEF(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3, D_3_DEF) \
+ virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) override; \
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
class GodotNavigationServer;
@@ -71,6 +72,7 @@ class GodotNavigationServer : public NavigationServer3D {
LocalVector<SetCommand *> commands;
+ mutable RID_Owner<NavLink> link_owner;
mutable RID_Owner<NavMap> map_owner;
mutable RID_Owner<NavRegion> region_owner;
mutable RID_Owner<RvoAgent> agent_owner;
@@ -79,15 +81,25 @@ class GodotNavigationServer : public NavigationServer3D {
LocalVector<NavMap *> active_maps;
LocalVector<uint32_t> active_maps_update_id;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
GodotNavigationServer();
virtual ~GodotNavigationServer();
- void add_command(SetCommand *command) const;
+ void add_command(SetCommand *command);
- virtual Array get_maps() const override;
+ virtual TypedArray<RID> get_maps() const override;
- virtual RID map_create() const override;
+ virtual RID map_create() override;
COMMAND_2(map_set_active, RID, p_map, bool, p_active);
virtual bool map_is_active(RID p_map) const override;
@@ -100,6 +112,9 @@ 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;
+ COMMAND_2(map_set_link_connection_radius, RID, p_map, real_t, p_connection_radius);
+ virtual real_t map_get_link_connection_radius(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_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;
@@ -107,18 +122,22 @@ public:
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 TypedArray<RID> map_get_links(RID p_map) const override;
+ virtual TypedArray<RID> map_get_regions(RID p_map) const override;
+ virtual TypedArray<RID> map_get_agents(RID p_map) const override;
virtual void map_force_update(RID p_map) override;
- virtual RID region_create() const override;
+ virtual RID region_create() 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;
+ COMMAND_2(region_set_owner_id, RID, p_region, ObjectID, p_owner_id);
+ virtual ObjectID region_get_owner_id(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);
@@ -126,16 +145,34 @@ public:
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;
+ COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh);
+ virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override;
virtual int region_get_connections_count(RID p_region) const override;
virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override;
virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override;
- virtual RID agent_create() const override;
+ virtual RID link_create() override;
+ COMMAND_2(link_set_map, RID, p_link, RID, p_map);
+ virtual RID link_get_map(RID p_link) const override;
+ COMMAND_2(link_set_bidirectional, RID, p_link, bool, p_bidirectional);
+ virtual bool link_is_bidirectional(RID p_link) const override;
+ COMMAND_2(link_set_navigation_layers, RID, p_link, uint32_t, p_navigation_layers);
+ virtual uint32_t link_get_navigation_layers(RID p_link) const override;
+ COMMAND_2(link_set_start_location, RID, p_link, Vector3, p_location);
+ virtual Vector3 link_get_start_location(RID p_link) const override;
+ COMMAND_2(link_set_end_location, RID, p_link, Vector3, p_location);
+ virtual Vector3 link_get_end_location(RID p_link) const override;
+ COMMAND_2(link_set_enter_cost, RID, p_link, real_t, p_enter_cost);
+ virtual real_t link_get_enter_cost(RID p_link) const override;
+ COMMAND_2(link_set_travel_cost, RID, p_link, real_t, p_travel_cost);
+ virtual real_t link_get_travel_cost(RID p_link) const override;
+ COMMAND_2(link_set_owner_id, RID, p_link, ObjectID, p_owner_id);
+ virtual ObjectID link_get_owner_id(RID p_link) const override;
+
+ virtual RID agent_create() 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_neighbor_distance, RID, p_agent, real_t, p_distance);
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);
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius);
@@ -145,14 +182,18 @@ public:
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position);
COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore);
virtual bool agent_is_map_changed(RID p_agent) const override;
- COMMAND_4_DEF(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata, Variant());
+ COMMAND_4_DEF(agent_set_callback, RID, p_agent, ObjectID, p_object_id, StringName, p_method, Variant, p_udata, Variant());
COMMAND_1(free, RID, p_object);
- virtual void set_active(bool p_active) const override;
+ virtual void set_active(bool p_active) override;
void flush_queries();
virtual void process(real_t p_delta_time) override;
+
+ virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const override;
+
+ int get_process_info(ProcessInfo p_info) const override;
};
#undef COMMAND_1
diff --git a/modules/navigation/nav_base.h b/modules/navigation/nav_base.h
new file mode 100644
index 0000000000..e729f7d408
--- /dev/null
+++ b/modules/navigation/nav_base.h
@@ -0,0 +1,64 @@
+/**************************************************************************/
+/* nav_base.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_BASE_H
+#define NAV_BASE_H
+
+#include "nav_rid.h"
+#include "nav_utils.h"
+#include "servers/navigation/navigation_utilities.h"
+
+class NavMap;
+
+class NavBase : public NavRid {
+protected:
+ uint32_t navigation_layers = 1;
+ float enter_cost = 0.0;
+ float travel_cost = 1.0;
+ ObjectID owner_id;
+ NavigationUtilities::PathSegmentType type;
+
+public:
+ NavigationUtilities::PathSegmentType get_type() const { return type; }
+
+ void set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; }
+ uint32_t get_navigation_layers() const { return navigation_layers; }
+
+ 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_owner_id(ObjectID p_owner_id) { owner_id = p_owner_id; }
+ ObjectID get_owner_id() const { return owner_id; }
+};
+
+#endif // NAV_BASE_H
diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp
new file mode 100644
index 0000000000..05d2b21487
--- /dev/null
+++ b/modules/navigation/nav_link.cpp
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* nav_link.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "nav_link.h"
+
+#include "nav_map.h"
+
+void NavLink::set_map(NavMap *p_map) {
+ map = p_map;
+ link_dirty = true;
+}
+
+void NavLink::set_bidirectional(bool p_bidirectional) {
+ bidirectional = p_bidirectional;
+ link_dirty = true;
+}
+
+void NavLink::set_start_location(const Vector3 p_location) {
+ start_location = p_location;
+ link_dirty = true;
+}
+
+void NavLink::set_end_location(const Vector3 p_location) {
+ end_location = p_location;
+ link_dirty = true;
+}
+
+bool NavLink::check_dirty() {
+ const bool was_dirty = link_dirty;
+
+ link_dirty = false;
+ return was_dirty;
+}
diff --git a/modules/navigation/nav_link.h b/modules/navigation/nav_link.h
new file mode 100644
index 0000000000..47c1211db8
--- /dev/null
+++ b/modules/navigation/nav_link.h
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* nav_link.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_LINK_H
+#define NAV_LINK_H
+
+#include "nav_base.h"
+#include "nav_utils.h"
+
+class NavLink : public NavBase {
+ NavMap *map = nullptr;
+ bool bidirectional = true;
+ Vector3 start_location;
+ Vector3 end_location;
+
+ bool link_dirty = true;
+
+public:
+ NavLink() {
+ type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_LINK;
+ }
+
+ void set_map(NavMap *p_map);
+ NavMap *get_map() const {
+ return map;
+ }
+
+ void set_bidirectional(bool p_bidirectional);
+ bool is_bidirectional() const {
+ return bidirectional;
+ }
+
+ void set_start_location(Vector3 p_location);
+ Vector3 get_start_location() const {
+ return start_location;
+ }
+
+ void set_end_location(Vector3 p_location);
+ Vector3 get_end_location() const {
+ return end_location;
+ }
+
+ bool check_dirty();
+};
+
+#endif // NAV_LINK_H
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 49029b5513..fd735f8793 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -1,42 +1,55 @@
-/*************************************************************************/
-/* nav_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_map.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "nav_map.h"
#include "core/object/worker_thread_pool.h"
+#include "nav_link.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)))
+// Helper macro
+#define APPEND_METADATA(poly) \
+ if (r_path_types) { \
+ r_path_types->push_back(poly->owner->get_type()); \
+ } \
+ if (r_path_rids) { \
+ r_path_rids->push_back(poly->owner->get_self()); \
+ } \
+ if (r_path_owners) { \
+ r_path_owners->push_back(poly->owner->get_owner_id()); \
+ }
+
void NavMap::set_up(Vector3 p_up) {
up = p_up;
regenerate_polygons = true;
@@ -52,6 +65,11 @@ void NavMap::set_edge_connection_margin(float p_edge_connection_margin) {
regenerate_links = true;
}
+void NavMap::set_link_connection_radius(float p_link_connection_radius) {
+ link_connection_radius = p_link_connection_radius;
+ regenerate_links = true;
+}
+
gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
const int x = int(Math::floor(p_pos.x / cell_size));
const int y = int(Math::floor(p_pos.y / cell_size));
@@ -65,7 +83,18 @@ 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_navigation_layers) const {
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const {
+ // Clear metadata outputs.
+ if (r_path_types) {
+ r_path_types->clear();
+ }
+ if (r_path_rids) {
+ r_path_rids->clear();
+ }
+ if (r_path_owners) {
+ r_path_owners->clear();
+ }
+
// Find the start poly and the end poly on this map.
const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
@@ -109,6 +138,24 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
return Vector<Vector3>();
}
if (begin_poly == end_poly) {
+ if (r_path_types) {
+ r_path_types->resize(2);
+ r_path_types->write[0] = begin_poly->owner->get_type();
+ r_path_types->write[1] = end_poly->owner->get_type();
+ }
+
+ if (r_path_rids) {
+ r_path_rids->resize(2);
+ (*r_path_rids)[0] = begin_poly->owner->get_self();
+ (*r_path_rids)[1] = end_poly->owner->get_self();
+ }
+
+ if (r_path_owners) {
+ r_path_owners->resize(2);
+ r_path_owners->write[0] = begin_poly->owner->get_owner_id();
+ r_path_owners->write[1] = end_poly->owner->get_owner_id();
+ }
+
Vector<Vector3> path;
path.resize(2);
path.write[0] = begin_point;
@@ -134,20 +181,17 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// This is an implementation of the A* algorithm.
int least_cost_id = 0;
+ int prev_least_cost_id = -1;
bool found_route = false;
const gd::Polygon *reachable_end = nullptr;
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++) {
- gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
-
- const gd::Edge &edge = least_cost_poly->poly->edges[i];
+ const gd::Edge &edge = navigation_polys[least_cost_id].poly->edges[i];
// Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
for (int connection_index = 0; connection_index < edge.connections.size(); connection_index++) {
@@ -158,17 +202,18 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
continue;
}
- float region_enter_cost = 0.0;
- float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost();
+ const gd::NavigationPoly &least_cost_poly = navigation_polys[least_cost_id];
+ float poly_enter_cost = 0.0;
+ float poly_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();
+ if (prev_least_cost_id != -1 && (navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self())) {
+ poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost();
}
- prev_least_cost_poly = least_cost_poly;
+ prev_least_cost_id = least_cost_id;
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) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance;
+ 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) * poly_travel_cost) + poly_enter_cost + least_cost_poly.traveled_distance;
int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon));
@@ -234,6 +279,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
to_visit.clear();
to_visit.push_back(0);
least_cost_id = 0;
+ prev_least_cost_id = -1;
reachable_end = nullptr;
@@ -291,6 +337,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
gd::NavigationPoly *p = apex_poly;
path.push_back(end_point);
+ APPEND_METADATA(end_poly);
while (p) {
// Set left and right points of the pathway between polygons.
@@ -307,7 +354,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
left_poly = p;
left_portal = left;
} else {
- clip_path(navigation_polys, path, apex_poly, right_portal, right_poly);
+ clip_path(navigation_polys, path, apex_poly, right_portal, right_poly, r_path_types, r_path_rids, r_path_owners);
apex_point = right_portal;
p = right_poly;
@@ -315,7 +362,9 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
apex_poly = p;
left_portal = apex_point;
right_portal = apex_point;
+
path.push_back(apex_point);
+ APPEND_METADATA(apex_poly->poly);
skip = true;
}
}
@@ -326,7 +375,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
right_poly = p;
right_portal = right;
} else {
- clip_path(navigation_polys, path, apex_poly, left_portal, left_poly);
+ clip_path(navigation_polys, path, apex_poly, left_portal, left_poly, r_path_types, r_path_rids, r_path_owners);
apex_point = left_portal;
p = left_poly;
@@ -334,7 +383,9 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
apex_poly = p;
right_portal = apex_point;
left_portal = apex_point;
+
path.push_back(apex_point);
+ APPEND_METADATA(apex_poly->poly);
}
}
@@ -350,27 +401,62 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// If the last point is not the begin point, add it to the list.
if (path[path.size() - 1] != begin_point) {
path.push_back(begin_point);
+ APPEND_METADATA(begin_poly);
}
path.reverse();
+ if (r_path_types) {
+ r_path_types->reverse();
+ }
+ if (r_path_rids) {
+ r_path_rids->reverse();
+ }
+ if (r_path_owners) {
+ r_path_owners->reverse();
+ }
} else {
path.push_back(end_point);
+ APPEND_METADATA(end_poly);
// Add mid points
int np_id = least_cost_id;
while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -1) {
- int prev = navigation_polys[np_id].back_navigation_edge;
- int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size();
- Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5;
- path.push_back(point);
+ if (navigation_polys[np_id].back_navigation_edge != -1) {
+ int prev = navigation_polys[np_id].back_navigation_edge;
+ int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size();
+ Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5;
+
+ path.push_back(point);
+ APPEND_METADATA(navigation_polys[np_id].poly);
+ } else {
+ path.push_back(navigation_polys[np_id].entry);
+ APPEND_METADATA(navigation_polys[np_id].poly);
+ }
+
np_id = navigation_polys[np_id].back_navigation_poly_id;
}
path.push_back(begin_point);
+ APPEND_METADATA(begin_poly);
+
path.reverse();
+ if (r_path_types) {
+ r_path_types->reverse();
+ }
+ if (r_path_rids) {
+ r_path_rids->reverse();
+ }
+ if (r_path_owners) {
+ r_path_owners->reverse();
+ }
}
+ // Ensure post conditions (path arrays MUST match in size).
+ CRASH_COND(r_path_types && path.size() != r_path_types->size());
+ CRASH_COND(r_path_rids && path.size() != r_path_rids->size());
+ CRASH_COND(r_path_owners && path.size() != r_path_owners->size());
+
return path;
}
@@ -475,6 +561,19 @@ void NavMap::remove_region(NavRegion *p_region) {
}
}
+void NavMap::add_link(NavLink *p_link) {
+ links.push_back(p_link);
+ regenerate_links = true;
+}
+
+void NavMap::remove_link(NavLink *p_link) {
+ int64_t link_index = links.find(p_link);
+ if (link_index != -1) {
+ links.remove_at_unordered(link_index);
+ regenerate_links = true;
+ }
+}
+
bool NavMap::has_agent(RvoAgent *agent) const {
return (agents.find(agent) != -1);
}
@@ -512,6 +611,16 @@ void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
}
void NavMap::sync() {
+ // Performance Monitor
+ int _new_pm_region_count = regions.size();
+ int _new_pm_agent_count = agents.size();
+ int _new_pm_link_count = links.size();
+ int _new_pm_polygon_count = pm_polygon_count;
+ int _new_pm_edge_count = pm_edge_count;
+ int _new_pm_edge_merge_count = pm_edge_merge_count;
+ int _new_pm_edge_connection_count = pm_edge_connection_count;
+ int _new_pm_edge_free_count = pm_edge_free_count;
+
// Check if we need to update the links.
if (regenerate_polygons) {
for (uint32_t r = 0; r < regions.size(); r++) {
@@ -526,7 +635,19 @@ void NavMap::sync() {
}
}
+ for (uint32_t l = 0; l < links.size(); l++) {
+ if (links[l]->check_dirty()) {
+ regenerate_links = true;
+ }
+ }
+
if (regenerate_links) {
+ _new_pm_polygon_count = 0;
+ _new_pm_edge_count = 0;
+ _new_pm_edge_merge_count = 0;
+ _new_pm_edge_connection_count = 0;
+ _new_pm_edge_free_count = 0;
+
// Remove regions connections.
for (uint32_t r = 0; r < regions.size(); r++) {
regions[r]->get_connections().clear();
@@ -549,6 +670,8 @@ void NavMap::sync() {
count += regions[r]->get_polygons().size();
}
+ _new_pm_polygon_count = polygons.size();
+
// Group all edges per key.
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
for (uint32_t poly_id = 0; poly_id < polygons.size(); poly_id++) {
@@ -561,6 +684,7 @@ void NavMap::sync() {
HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
if (!connection) {
connections[ek] = Vector<gd::Edge::Connection>();
+ _new_pm_edge_count += 1;
}
if (connections[ek].size() <= 1) {
// Add the polygon/edge tuple to this key.
@@ -586,6 +710,7 @@ void NavMap::sync() {
c1.polygon->edges[c1.edge].connections.push_back(c2);
c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
+ _new_pm_edge_merge_count += 1;
} else {
CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
free_edges.push_back(E.value[0]);
@@ -599,6 +724,8 @@ void NavMap::sync() {
// to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during
// connection, integration and path finding.
+ _new_pm_edge_free_count = free_edges.size();
+
for (int i = 0; i < free_edges.size(); i++) {
const gd::Edge::Connection &free_edge = free_edges[i];
Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
@@ -651,7 +778,122 @@ void NavMap::sync() {
free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
// Add the connection to the region_connection map.
- free_edge.polygon->owner->get_connections().push_back(new_connection);
+ ((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection);
+ _new_pm_edge_connection_count += 1;
+ }
+ }
+
+ uint32_t link_poly_idx = 0;
+ link_polygons.resize(links.size());
+
+ // Search for polygons within range of a nav link.
+ for (uint32_t l = 0; l < links.size(); l++) {
+ const NavLink *link = links[l];
+ const Vector3 start = link->get_start_location();
+ const Vector3 end = link->get_end_location();
+
+ gd::Polygon *closest_start_polygon = nullptr;
+ real_t closest_start_distance = link_connection_radius;
+ Vector3 closest_start_point;
+
+ gd::Polygon *closest_end_polygon = nullptr;
+ real_t closest_end_distance = link_connection_radius;
+ Vector3 closest_end_point;
+
+ // Create link to any polygons within the search radius of the start point.
+ for (uint32_t start_index = 0; start_index < polygons.size(); start_index++) {
+ gd::Polygon &start_poly = polygons[start_index];
+
+ // For each face check the distance to the start
+ for (uint32_t start_point_id = 2; start_point_id < start_poly.points.size(); start_point_id += 1) {
+ const Face3 start_face(start_poly.points[0].pos, start_poly.points[start_point_id - 1].pos, start_poly.points[start_point_id].pos);
+ const Vector3 start_point = start_face.get_closest_point_to(start);
+ const real_t start_distance = start_point.distance_to(start);
+
+ // Pick the polygon that is within our radius and is closer than anything we've seen yet.
+ if (start_distance <= link_connection_radius && start_distance < closest_start_distance) {
+ closest_start_distance = start_distance;
+ closest_start_point = start_point;
+ closest_start_polygon = &start_poly;
+ }
+ }
+ }
+
+ // Find any polygons within the search radius of the end point.
+ for (uint32_t end_index = 0; end_index < polygons.size(); end_index++) {
+ gd::Polygon &end_poly = polygons[end_index];
+
+ // For each face check the distance to the end
+ for (uint32_t end_point_id = 2; end_point_id < end_poly.points.size(); end_point_id += 1) {
+ const Face3 end_face(end_poly.points[0].pos, end_poly.points[end_point_id - 1].pos, end_poly.points[end_point_id].pos);
+ const Vector3 end_point = end_face.get_closest_point_to(end);
+ const real_t end_distance = end_point.distance_to(end);
+
+ // Pick the polygon that is within our radius and is closer than anything we've seen yet.
+ if (end_distance <= link_connection_radius && end_distance < closest_end_distance) {
+ closest_end_distance = end_distance;
+ closest_end_point = end_point;
+ closest_end_polygon = &end_poly;
+ }
+ }
+ }
+
+ // If we have both a start and end point, then create a synthetic polygon to route through.
+ if (closest_start_polygon && closest_end_polygon) {
+ gd::Polygon &new_polygon = link_polygons[link_poly_idx++];
+ new_polygon.owner = link;
+
+ new_polygon.edges.clear();
+ new_polygon.edges.resize(4);
+ new_polygon.points.clear();
+ new_polygon.points.reserve(4);
+
+ // Build a set of vertices that create a thin polygon going from the start to the end point.
+ new_polygon.points.push_back({ closest_start_point, get_point_key(closest_start_point) });
+ new_polygon.points.push_back({ closest_start_point, get_point_key(closest_start_point) });
+ new_polygon.points.push_back({ closest_end_point, get_point_key(closest_end_point) });
+ new_polygon.points.push_back({ closest_end_point, get_point_key(closest_end_point) });
+
+ Vector3 center;
+ for (int p = 0; p < 4; ++p) {
+ center += new_polygon.points[p].pos;
+ }
+ new_polygon.center = center / real_t(new_polygon.points.size());
+ new_polygon.clockwise = true;
+
+ // Setup connections to go forward in the link.
+ {
+ gd::Edge::Connection entry_connection;
+ entry_connection.polygon = &new_polygon;
+ entry_connection.edge = -1;
+ entry_connection.pathway_start = new_polygon.points[0].pos;
+ entry_connection.pathway_end = new_polygon.points[1].pos;
+ closest_start_polygon->edges[0].connections.push_back(entry_connection);
+
+ gd::Edge::Connection exit_connection;
+ exit_connection.polygon = closest_end_polygon;
+ exit_connection.edge = -1;
+ exit_connection.pathway_start = new_polygon.points[2].pos;
+ exit_connection.pathway_end = new_polygon.points[3].pos;
+ new_polygon.edges[2].connections.push_back(exit_connection);
+ }
+
+ // If the link is bi-directional, create connections from the end to the start.
+ if (link->is_bidirectional()) {
+ gd::Edge::Connection entry_connection;
+ entry_connection.polygon = &new_polygon;
+ entry_connection.edge = -1;
+ entry_connection.pathway_start = new_polygon.points[2].pos;
+ entry_connection.pathway_end = new_polygon.points[3].pos;
+ closest_end_polygon->edges[0].connections.push_back(entry_connection);
+
+ gd::Edge::Connection exit_connection;
+ exit_connection.polygon = closest_start_polygon;
+ exit_connection.edge = -1;
+ exit_connection.pathway_start = new_polygon.points[0].pos;
+ exit_connection.pathway_end = new_polygon.points[1].pos;
+ new_polygon.edges[0].connections.push_back(exit_connection);
+ }
}
}
@@ -673,6 +915,16 @@ void NavMap::sync() {
regenerate_polygons = false;
regenerate_links = false;
agents_dirty = false;
+
+ // Performance Monitor
+ pm_region_count = _new_pm_region_count;
+ pm_agent_count = _new_pm_agent_count;
+ pm_link_count = _new_pm_link_count;
+ pm_polygon_count = _new_pm_polygon_count;
+ pm_edge_count = _new_pm_edge_count;
+ pm_edge_merge_count = _new_pm_edge_merge_count;
+ pm_edge_connection_count = _new_pm_edge_connection_count;
+ pm_edge_free_count = _new_pm_edge_free_count;
}
void NavMap::compute_single_step(uint32_t index, RvoAgent **agent) {
@@ -694,7 +946,7 @@ void NavMap::dispatch_callbacks() {
}
}
-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 {
+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, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const {
Vector3 from = path[path.size() - 1];
if (from.is_equal_approx(p_to_point)) {
@@ -720,6 +972,7 @@ void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys
if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) {
if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) {
path.push_back(inters);
+ APPEND_METADATA(from_poly->poly);
}
}
}
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index e50a1afbe9..fce7aff3ba 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* nav_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_map.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_MAP_H
#define NAV_MAP_H
@@ -40,9 +40,9 @@
#include <KdTree.h>
+class NavLink;
class NavRegion;
class RvoAgent;
-class NavRegion;
class NavMap : public NavRid {
/// Map Up
@@ -55,11 +55,19 @@ class NavMap : public NavRid {
/// This value is used to detect the near edges to connect.
real_t edge_connection_margin = 0.25;
+ /// This value is used to limit how far links search to find polygons to connect to.
+ real_t link_connection_radius = 1.0;
+
bool regenerate_polygons = true;
bool regenerate_links = true;
+ /// Map regions
LocalVector<NavRegion *> regions;
+ /// Map links
+ LocalVector<NavLink *> links;
+ LocalVector<gd::Polygon> link_polygons;
+
/// Map polygons
LocalVector<gd::Polygon> polygons;
@@ -81,6 +89,16 @@ class NavMap : public NavRid {
/// Change the id each time the map is updated.
uint32_t map_update_id = 0;
+ // Performance Monitor
+ int pm_region_count = 0;
+ int pm_agent_count = 0;
+ int pm_link_count = 0;
+ int pm_polygon_count = 0;
+ int pm_edge_count = 0;
+ int pm_edge_merge_count = 0;
+ int pm_edge_connection_count = 0;
+ int pm_edge_free_count = 0;
+
public:
NavMap();
~NavMap();
@@ -100,9 +118,14 @@ public:
return edge_connection_margin;
}
+ void set_link_connection_radius(float p_link_connection_radius);
+ float get_link_connection_radius() const {
+ return link_connection_radius;
+ }
+
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_navigation_layers = 1) const;
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) 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;
@@ -115,6 +138,12 @@ public:
return regions;
}
+ void add_link(NavLink *p_link);
+ void remove_link(NavLink *p_link);
+ const LocalVector<NavLink *> &get_links() const {
+ return links;
+ }
+
bool has_agent(RvoAgent *agent) const;
void add_agent(RvoAgent *agent);
void remove_agent(RvoAgent *agent);
@@ -133,9 +162,19 @@ public:
void step(real_t p_deltatime);
void dispatch_callbacks();
+ // Performance Monitor
+ int get_pm_region_count() const { return pm_region_count; }
+ int get_pm_agent_count() const { return pm_agent_count; }
+ int get_pm_link_count() const { return pm_link_count; }
+ int get_pm_polygon_count() const { return pm_polygon_count; }
+ int get_pm_edge_count() const { return pm_edge_count; }
+ int get_pm_edge_merge_count() const { return pm_edge_merge_count; }
+ int get_pm_edge_connection_count() const { return pm_edge_connection_count; }
+ int get_pm_edge_free_count() const { return pm_edge_free_count; }
+
private:
void compute_single_step(uint32_t index, RvoAgent **agent);
- 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;
+ 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, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const;
};
#endif // NAV_MAP_H
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index 88740807eb..797c523627 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* nav_region.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_region.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "nav_region.h"
@@ -40,14 +40,6 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
-void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
- navigation_layers = p_navigation_layers;
-}
-
-uint32_t NavRegion::get_navigation_layers() const {
- return navigation_layers;
-}
-
void NavRegion::set_transform(Transform3D p_transform) {
transform = p_transform;
polygons_dirty = true;
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index c9d2d80f6c..0942aa22f0 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -1,53 +1,45 @@
-/*************************************************************************/
-/* nav_region.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_region.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_REGION_H
#define NAV_REGION_H
#include "scene/resources/navigation_mesh.h"
-#include "nav_rid.h"
+#include "nav_base.h"
#include "nav_utils.h"
-#include <vector>
-
-class NavMap;
-class NavRegion;
-
-class NavRegion : public NavRid {
+class NavRegion : public NavBase {
NavMap *map = nullptr;
Transform3D transform;
Ref<NavigationMesh> mesh;
- uint32_t navigation_layers = 1;
- float enter_cost = 0.0;
- float travel_cost = 1.0;
Vector<gd::Edge::Connection> connections;
bool polygons_dirty = true;
@@ -56,7 +48,9 @@ class NavRegion : public NavRid {
LocalVector<gd::Polygon> polygons;
public:
- NavRegion() {}
+ NavRegion() {
+ type = NavigationUtilities::PathSegmentType::PATH_SEGMENT_TYPE_REGION;
+ }
void scratch_polygons() {
polygons_dirty = true;
@@ -67,15 +61,6 @@ public:
return map;
}
- 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 {
return transform;
diff --git a/modules/navigation/nav_rid.h b/modules/navigation/nav_rid.h
index 31e20440d2..6229985a8d 100644
--- a/modules/navigation/nav_rid.h
+++ b/modules/navigation/nav_rid.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* nav_rid.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_rid.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_RID_H
#define NAV_RID_H
diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h
index 47f04b6a75..50437469aa 100644
--- a/modules/navigation/nav_utils.h
+++ b/modules/navigation/nav_utils.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* nav_utils.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* nav_utils.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAV_UTILS_H
#define NAV_UTILS_H
@@ -35,9 +35,8 @@
#include "core/templates/hash_map.h"
#include "core/templates/hashfuncs.h"
#include "core/templates/local_vector.h"
-#include <vector>
-class NavRegion;
+class NavBase;
namespace gd {
struct Polygon;
@@ -79,26 +78,33 @@ struct Point {
};
struct Edge {
- /// This edge ID
- int this_edge = -1;
-
/// The gateway in the edge, as, in some case, the whole edge might not be navigable.
struct Connection {
+ /// Polygon that this connection leads to.
Polygon *polygon = nullptr;
+
+ /// Edge of the source polygon where this connection starts from.
int edge = -1;
+
+ /// Point on the edge where the gateway leading to the poly starts.
Vector3 pathway_start;
+
+ /// Point on the edge where the gateway leading to the poly ends.
Vector3 pathway_end;
};
+
+ /// Connections from this edge to other polygons.
Vector<Connection> connections;
};
struct Polygon {
- NavRegion *owner = nullptr;
+ /// Navigation region or link that contains this polygon.
+ const NavBase *owner = nullptr;
/// The points of this `Polygon`
LocalVector<Point> points;
- /// Are the points clockwise ?
+ /// Are the points clockwise?
bool clockwise;
/// The edges of this `Polygon`
@@ -115,7 +121,7 @@ struct NavigationPoly {
/// Those 4 variables are used to travel the path backwards.
int back_navigation_poly_id = -1;
- uint32_t back_navigation_edge = UINT32_MAX;
+ int back_navigation_edge = -1;
Vector3 back_navigation_edge_pathway_start;
Vector3 back_navigation_edge_pathway_end;
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 6e8ac77f79..fff7a02fc4 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* navigation_mesh_generator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* navigation_mesh_generator.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 _3D_DISABLED
@@ -42,6 +42,7 @@
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/cylinder_shape_3d.h"
+#include "scene/resources/height_map_shape_3d.h"
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
@@ -206,6 +207,9 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
List<uint32_t> shape_owners;
static_body->get_shape_owners(&shape_owners);
for (uint32_t shape_owner : shape_owners) {
+ if (static_body->is_shape_owner_disabled(shape_owner)) {
+ continue;
+ }
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);
@@ -262,10 +266,10 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
if (err == OK) {
PackedVector3Array faces;
- for (int j = 0; j < md.faces.size(); ++j) {
- Geometry3D::MeshData::Face face = md.faces[j];
+ for (uint32_t j = 0; j < md.faces.size(); ++j) {
+ const Geometry3D::MeshData::Face &face = md.faces[j];
- for (int k = 2; k < face.indices.size(); ++k) {
+ for (uint32_t k = 2; k < face.indices.size(); ++k) {
faces.push_back(md.vertices[face.indices[0]]);
faces.push_back(md.vertices[face.indices[k - 1]]);
faces.push_back(md.vertices[face.indices[k]]);
@@ -275,6 +279,50 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
_add_faces(faces, transform, p_vertices, p_indices);
}
}
+
+ HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s);
+ if (heightmap_shape) {
+ int heightmap_depth = heightmap_shape->get_map_depth();
+ int heightmap_width = heightmap_shape->get_map_width();
+
+ if (heightmap_depth >= 2 && heightmap_width >= 2) {
+ const Vector<real_t> &map_data = heightmap_shape->get_map_data();
+
+ Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
+ Vector2 start = heightmap_gridsize * -0.5;
+
+ Vector<Vector3> vertex_array;
+ vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
+ int map_data_current_index = 0;
+
+ for (int d = 0; d < heightmap_depth - 1; d++) {
+ for (int w = 0; w < heightmap_width - 1; w++) {
+ if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
+ float top_left_height = map_data[map_data_current_index];
+ float top_right_height = map_data[map_data_current_index + 1];
+ float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
+ float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
+
+ Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
+ Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
+ Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
+ Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
+
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_left);
+ vertex_array.push_back(top_left);
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_right);
+ vertex_array.push_back(bottom_left);
+ }
+ map_data_current_index += 1;
+ }
+ }
+ if (vertex_array.size() > 0) {
+ _add_faces(vertex_array, transform, p_vertices, p_indices);
+ }
+ }
+ }
}
}
}
@@ -344,10 +392,10 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
if (err == OK) {
PackedVector3Array faces;
- for (int j = 0; j < md.faces.size(); ++j) {
- Geometry3D::MeshData::Face face = md.faces[j];
+ for (uint32_t j = 0; j < md.faces.size(); ++j) {
+ const Geometry3D::MeshData::Face &face = md.faces[j];
- for (int k = 2; k < face.indices.size(); ++k) {
+ for (uint32_t k = 2; k < face.indices.size(); ++k) {
faces.push_back(md.vertices[face.indices[0]]);
faces.push_back(md.vertices[face.indices[k - 1]]);
faces.push_back(md.vertices[face.indices[k]]);
@@ -362,6 +410,50 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
PackedVector3Array faces = Variant(dict["faces"]);
_add_faces(faces, shapes[i], p_vertices, p_indices);
} break;
+ case PhysicsServer3D::SHAPE_HEIGHTMAP: {
+ Dictionary dict = data;
+ ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights"
+ int heightmap_depth = dict["depth"];
+ int heightmap_width = dict["width"];
+
+ if (heightmap_depth >= 2 && heightmap_width >= 2) {
+ const Vector<real_t> &map_data = dict["heights"];
+
+ Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
+ Vector2 start = heightmap_gridsize * -0.5;
+
+ Vector<Vector3> vertex_array;
+ vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
+ int map_data_current_index = 0;
+
+ for (int d = 0; d < heightmap_depth - 1; d++) {
+ for (int w = 0; w < heightmap_width - 1; w++) {
+ if (map_data_current_index + 1 + heightmap_depth < map_data.size()) {
+ float top_left_height = map_data[map_data_current_index];
+ float top_right_height = map_data[map_data_current_index + 1];
+ float bottom_left_height = map_data[map_data_current_index + heightmap_depth];
+ float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth];
+
+ Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d);
+ Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d);
+ Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0);
+ Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0);
+
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_left);
+ vertex_array.push_back(top_left);
+ vertex_array.push_back(top_right);
+ vertex_array.push_back(bottom_right);
+ vertex_array.push_back(bottom_left);
+ }
+ map_data_current_index += 1;
+ }
+ }
+ if (vertex_array.size() > 0) {
+ _add_faces(vertex_array, shapes[i], p_vertices, p_indices);
+ }
+ }
+ } break;
default: {
WARN_PRINT("Unsupported collision shape type.");
} break;
@@ -378,14 +470,14 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
}
}
-void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
+void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_navigation_mesh) {
Vector<Vector3> nav_vertices;
for (int i = 0; i < p_detail_mesh->nverts; i++) {
const float *v = &p_detail_mesh->verts[i * 3];
nav_vertices.push_back(Vector3(v[0], v[1], v[2]));
}
- p_nav_mesh->set_vertices(nav_vertices);
+ p_navigation_mesh->set_vertices(nav_vertices);
for (int i = 0; i < p_detail_mesh->nmeshes; i++) {
const unsigned int *m = &p_detail_mesh->meshes[i * 4];
@@ -400,13 +492,13 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con
nav_indices.write[0] = ((int)(bverts + tris[j * 4 + 0]));
nav_indices.write[1] = ((int)(bverts + tris[j * 4 + 2]));
nav_indices.write[2] = ((int)(bverts + tris[j * 4 + 1]));
- p_nav_mesh->add_polygon(nav_indices);
+ p_navigation_mesh->add_polygon(nav_indices);
}
}
}
void NavigationMeshGenerator::_build_recast_navigation_mesh(
- Ref<NavigationMesh> p_nav_mesh,
+ Ref<NavigationMesh> p_navigation_mesh,
#ifdef TOOLS_ENABLED
EditorProgress *ep,
#endif
@@ -436,42 +528,42 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
rcConfig cfg;
memset(&cfg, 0, sizeof(cfg));
- cfg.cs = p_nav_mesh->get_cell_size();
- cfg.ch = p_nav_mesh->get_cell_height();
- cfg.walkableSlopeAngle = p_nav_mesh->get_agent_max_slope();
- cfg.walkableHeight = (int)Math::ceil(p_nav_mesh->get_agent_height() / cfg.ch);
- cfg.walkableClimb = (int)Math::floor(p_nav_mesh->get_agent_max_climb() / cfg.ch);
- cfg.walkableRadius = (int)Math::ceil(p_nav_mesh->get_agent_radius() / cfg.cs);
- cfg.maxEdgeLen = (int)(p_nav_mesh->get_edge_max_length() / p_nav_mesh->get_cell_size());
- cfg.maxSimplificationError = p_nav_mesh->get_edge_max_error();
- 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 = 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())) {
+ cfg.cs = p_navigation_mesh->get_cell_size();
+ cfg.ch = p_navigation_mesh->get_cell_height();
+ cfg.walkableSlopeAngle = p_navigation_mesh->get_agent_max_slope();
+ cfg.walkableHeight = (int)Math::ceil(p_navigation_mesh->get_agent_height() / cfg.ch);
+ cfg.walkableClimb = (int)Math::floor(p_navigation_mesh->get_agent_max_climb() / cfg.ch);
+ cfg.walkableRadius = (int)Math::ceil(p_navigation_mesh->get_agent_radius() / cfg.cs);
+ cfg.maxEdgeLen = (int)(p_navigation_mesh->get_edge_max_length() / p_navigation_mesh->get_cell_size());
+ cfg.maxSimplificationError = p_navigation_mesh->get_edge_max_error();
+ cfg.minRegionArea = (int)(p_navigation_mesh->get_region_min_size() * p_navigation_mesh->get_region_min_size());
+ cfg.mergeRegionArea = (int)(p_navigation_mesh->get_region_merge_size() * p_navigation_mesh->get_region_merge_size());
+ cfg.maxVertsPerPoly = (int)p_navigation_mesh->get_vertices_per_polygon();
+ cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
+ cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
+
+ if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_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())) {
+ if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_navigation_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())) {
+ if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_navigation_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())) {
+ if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_navigation_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())) {
+ if (!Math::is_equal_approx((float)cfg.minRegionArea, p_navigation_mesh->get_region_min_size() * p_navigation_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())) {
+ if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_navigation_mesh->get_region_merge_size() * p_navigation_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 (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_navigation_mesh->get_vertices_per_polygon())) {
+ WARN_PRINT("Property vertices_per_polygon is converted to int and loses precision.");
}
- if (p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance() < 0.1f) {
+ if (p_navigation_mesh->get_cell_size() * p_navigation_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.");
}
@@ -482,13 +574,9 @@ 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();
-
+ AABB baking_aabb = p_navigation_mesh->get_filter_baking_aabb();
+ if (baking_aabb.has_volume()) {
+ Vector3 baking_aabb_offset = p_navigation_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;
@@ -539,13 +627,13 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb));
}
- if (p_nav_mesh->get_filter_low_hanging_obstacles()) {
+ if (p_navigation_mesh->get_filter_low_hanging_obstacles()) {
rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf);
}
- if (p_nav_mesh->get_filter_ledge_spans()) {
+ if (p_navigation_mesh->get_filter_ledge_spans()) {
rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf);
}
- if (p_nav_mesh->get_filter_walkable_low_height_spans()) {
+ if (p_navigation_mesh->get_filter_walkable_low_height_spans()) {
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
}
@@ -577,10 +665,10 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
}
#endif
- if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
+ if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
ERR_FAIL_COND(!rcBuildRegions(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
- } else if (p_nav_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) {
+ } else if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_MONOTONE) {
ERR_FAIL_COND(!rcBuildRegionsMonotone(&ctx, *chf, 0, cfg.minRegionArea, cfg.mergeRegionArea));
} else {
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, 0, cfg.minRegionArea));
@@ -622,7 +710,7 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
}
#endif
- _convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_nav_mesh);
+ _convert_detail_mesh_to_native_navigation_mesh(detail_mesh, p_navigation_mesh);
rcFreePolyMesh(poly_mesh);
poly_mesh = nullptr;
@@ -641,8 +729,8 @@ NavigationMeshGenerator::NavigationMeshGenerator() {
NavigationMeshGenerator::~NavigationMeshGenerator() {
}
-void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
- ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid navigation mesh.");
+void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) {
+ ERR_FAIL_COND_MSG(!p_navigation_mesh.is_valid(), "Invalid navigation mesh.");
#ifdef TOOLS_ENABLED
EditorProgress *ep(nullptr);
@@ -667,17 +755,17 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
List<Node *> parse_nodes;
- if (p_nav_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_NAVMESH_CHILDREN) {
- parse_nodes.push_back(p_node);
+ if (p_navigation_mesh->get_source_geometry_mode() == NavigationMesh::SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) {
+ parse_nodes.push_back(p_root_node);
} else {
- p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes);
+ p_root_node->get_tree()->get_nodes_in_group(p_navigation_mesh->get_source_group_name(), &parse_nodes);
}
- Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_global_transform().affine_inverse();
+ Transform3D navmesh_xform = Object::cast_to<Node3D>(p_root_node)->get_global_transform().affine_inverse();
for (Node *E : parse_nodes) {
- NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
- uint32_t collision_mask = p_nav_mesh->get_collision_mask();
- bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
+ NavigationMesh::ParsedGeometryType geometry_type = p_navigation_mesh->get_parsed_geometry_type();
+ uint32_t collision_mask = p_navigation_mesh->get_collision_mask();
+ bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
_parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children);
}
@@ -689,7 +777,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
rcPolyMeshDetail *detail_mesh = nullptr;
_build_recast_navigation_mesh(
- p_nav_mesh,
+ p_navigation_mesh,
#ifdef TOOLS_ENABLED
ep,
#endif
@@ -728,16 +816,16 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
#endif
}
-void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
- if (p_nav_mesh.is_valid()) {
- p_nav_mesh->clear_polygons();
- p_nav_mesh->set_vertices(Vector<Vector3>());
+void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_navigation_mesh) {
+ if (p_navigation_mesh.is_valid()) {
+ p_navigation_mesh->clear_polygons();
+ p_navigation_mesh->set_vertices(Vector<Vector3>());
}
}
void NavigationMeshGenerator::_bind_methods() {
- ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &NavigationMeshGenerator::bake);
- ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &NavigationMeshGenerator::clear);
+ ClassDB::bind_method(D_METHOD("bake", "navigation_mesh", "root_node"), &NavigationMeshGenerator::bake);
+ ClassDB::bind_method(D_METHOD("clear", "navigation_mesh"), &NavigationMeshGenerator::clear);
}
#endif
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 8cc1531b53..4399c422e2 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* navigation_mesh_generator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* navigation_mesh_generator.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAVIGATION_MESH_GENERATOR_H
#define NAVIGATION_MESH_GENERATOR_H
@@ -55,9 +55,9 @@ protected:
static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
static void _parse_geometry(const Transform3D &p_navmesh_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
- static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
+ static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_navigation_mesh);
static void _build_recast_navigation_mesh(
- Ref<NavigationMesh> p_nav_mesh,
+ Ref<NavigationMesh> p_navigation_mesh,
#ifdef TOOLS_ENABLED
EditorProgress *ep,
#endif
@@ -75,8 +75,8 @@ public:
NavigationMeshGenerator();
~NavigationMeshGenerator();
- void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
- void clear(Ref<NavigationMesh> p_nav_mesh);
+ void bake(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node);
+ void clear(Ref<NavigationMesh> p_navigation_mesh);
};
#endif
diff --git a/modules/navigation/register_types.cpp b/modules/navigation/register_types.cpp
index 62ae2c7f02..6ba5e90c2e 100644
--- a/modules/navigation/register_types.cpp
+++ b/modules/navigation/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/navigation/register_types.h b/modules/navigation/register_types.h
index c4dbd19ed3..b07c3b9308 100644
--- a/modules/navigation/register_types.h
+++ b/modules/navigation/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NAVIGATION_REGISTER_TYPES_H
#define NAVIGATION_REGISTER_TYPES_H
diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp
index 4ec72ad43f..979ef0d917 100644
--- a/modules/navigation/rvo_agent.cpp
+++ b/modules/navigation/rvo_agent.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* rvo_agent.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* rvo_agent.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "rvo_agent.h"
diff --git a/modules/navigation/rvo_agent.h b/modules/navigation/rvo_agent.h
index 54baab404e..7b19907b2b 100644
--- a/modules/navigation/rvo_agent.h
+++ b/modules/navigation/rvo_agent.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* rvo_agent.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* rvo_agent.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 RVO_AGENT_H
#define RVO_AGENT_H
diff --git a/modules/noise/config.py b/modules/noise/config.py
index 74db20f2a4..2318d28c53 100644
--- a/modules/noise/config.py
+++ b/modules/noise/config.py
@@ -10,7 +10,7 @@ def get_doc_classes():
return [
"FastNoiseLite",
"Noise",
- "NoiseTexture",
+ "NoiseTexture2D",
]
diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml
index 5af204575c..735ca388de 100644
--- a/modules/noise/doc_classes/Noise.xml
+++ b/modules/noise/doc_classes/Noise.xml
@@ -13,59 +13,59 @@
<methods>
<method name="get_image" qualifiers="const">
<return type="Image" />
- <argument index="0" name="width" type="int" />
- <argument index="1" name="height" type="int" />
- <argument index="2" name="invert" type="bool" default="false" />
- <argument index="3" name="in_3d_space" type="bool" default="false" />
+ <param index="0" name="width" type="int" />
+ <param index="1" name="height" type="int" />
+ <param index="2" name="invert" type="bool" default="false" />
+ <param index="3" name="in_3d_space" type="bool" default="false" />
<description>
Returns a 2D [Image] noise image.
</description>
</method>
<method name="get_noise_1d" qualifiers="const">
<return type="float" />
- <argument index="0" name="x" type="float" />
+ <param index="0" name="x" type="float" />
<description>
Returns the 1D noise value at the given (x) coordinate.
</description>
</method>
<method name="get_noise_2d" qualifiers="const">
<return type="float" />
- <argument index="0" name="x" type="float" />
- <argument index="1" name="y" type="float" />
+ <param index="0" name="x" type="float" />
+ <param index="1" name="y" type="float" />
<description>
Returns the 2D noise value at the given position.
</description>
</method>
<method name="get_noise_2dv" qualifiers="const">
<return type="float" />
- <argument index="0" name="v" type="Vector2" />
+ <param index="0" name="v" type="Vector2" />
<description>
Returns the 2D noise value at the given position.
</description>
</method>
<method name="get_noise_3d" qualifiers="const">
<return type="float" />
- <argument index="0" name="x" type="float" />
- <argument index="1" name="y" type="float" />
- <argument index="2" name="z" type="float" />
+ <param index="0" name="x" type="float" />
+ <param index="1" name="y" type="float" />
+ <param index="2" name="z" type="float" />
<description>
Returns the 3D noise value at the given position.
</description>
</method>
<method name="get_noise_3dv" qualifiers="const">
<return type="float" />
- <argument index="0" name="v" type="Vector3" />
+ <param index="0" name="v" type="Vector3" />
<description>
Returns the 3D noise value at the given position.
</description>
</method>
<method name="get_seamless_image" qualifiers="const">
<return type="Image" />
- <argument index="0" name="width" type="int" />
- <argument index="1" name="height" type="int" />
- <argument index="2" name="invert" type="bool" default="false" />
- <argument index="3" name="in_3d_space" type="bool" default="false" />
- <argument index="4" name="skirt" type="float" default="0.1" />
+ <param index="0" name="width" type="int" />
+ <param index="1" name="height" type="int" />
+ <param index="2" name="invert" type="bool" default="false" />
+ <param index="3" name="in_3d_space" type="bool" default="false" />
+ <param index="4" name="skirt" type="float" default="0.1" />
<description>
Returns a seamless 2D [Image] noise image.
</description>
diff --git a/modules/noise/doc_classes/NoiseTexture.xml b/modules/noise/doc_classes/NoiseTexture2D.xml
index 62a223b387..0a800a143b 100644
--- a/modules/noise/doc_classes/NoiseTexture.xml
+++ b/modules/noise/doc_classes/NoiseTexture2D.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NoiseTexture" inherits="Texture2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="NoiseTexture2D" inherits="Texture2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size.
- NoiseTexture can also generate normalmap textures.
+ NoiseTexture2D can also generate normalmap textures.
The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data:
[codeblock]
- var texture = NoiseTexture.new()
+ var texture = NoiseTexture2D.new()
texture.noise = FastNoiseLite.new()
await texture.changed
var image = texture.get_image()
@@ -44,6 +44,7 @@
<member name="noise" type="Noise" setter="set_noise" getter="get_noise">
The instance of the [Noise] object.
</member>
+ <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
If [code]true[/code], a seamless texture is requested from the [Noise] resource.
[b]Note:[/b] Seamless noise textures may take longer to generate and/or can have a lower contrast compared to non-seamless noise depending on the used [Noise] resource. This is because some implementations use higher dimensions for generating seamless noise.
diff --git a/modules/noise/editor/noise_editor_plugin.cpp b/modules/noise/editor/noise_editor_plugin.cpp
index 27a86f45b5..462cc50112 100644
--- a/modules/noise/editor/noise_editor_plugin.cpp
+++ b/modules/noise/editor/noise_editor_plugin.cpp
@@ -1,41 +1,44 @@
-/*************************************************************************/
-/* noise_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* noise_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "noise_editor_plugin.h"
#ifdef TOOLS_ENABLED
+#include "editor/editor_inspector.h"
#include "editor/editor_scale.h"
+#include "scene/gui/button.h"
+#include "scene/gui/texture_rect.h"
#include "modules/noise/noise.h"
-#include "modules/noise/noise_texture.h"
+#include "modules/noise/noise_texture_2d.h"
class NoisePreview : public Control {
GDCLASS(NoisePreview, Control)
@@ -60,7 +63,7 @@ public:
_3d_space_switch = memnew(Button);
_3d_space_switch->set_text(TTR("3D"));
- _3d_space_switch->set_tooltip(TTR("Toggles whether the noise preview is computed in 3D space."));
+ _3d_space_switch->set_tooltip_text(TTR("Toggles whether the noise preview is computed in 3D space."));
_3d_space_switch->set_toggle_mode(true);
_3d_space_switch->set_offset(SIDE_LEFT, PADDING_3D_SPACE_SWITCH);
_3d_space_switch->set_offset(SIDE_TOP, PADDING_3D_SPACE_SWITCH);
@@ -102,7 +105,7 @@ private:
void update_preview() {
if (MIN(_preview_texture_size.width, _preview_texture_size.height) > 0) {
- Ref<NoiseTexture> tex;
+ Ref<NoiseTexture2D> tex;
tex.instantiate();
tex->set_width(_preview_texture_size.width);
tex->set_height(_preview_texture_size.height);
diff --git a/modules/noise/editor/noise_editor_plugin.h b/modules/noise/editor/noise_editor_plugin.h
index 55a01deb2d..948ccba29b 100644
--- a/modules/noise/editor/noise_editor_plugin.h
+++ b/modules/noise/editor/noise_editor_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* noise_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* noise_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NOISE_EDITOR_PLUGIN_H
#define NOISE_EDITOR_PLUGIN_H
diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp
index b21e3247d7..224c082c0f 100644
--- a/modules/noise/fastnoise_lite.cpp
+++ b/modules/noise/fastnoise_lite.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* fastnoise_lite.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* fastnoise_lite.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "fastnoise_lite.h"
@@ -476,24 +476,24 @@ void FastNoiseLite::_bind_methods() {
BIND_ENUM_CONSTANT(DOMAIN_WARP_FRACTAL_INDEPENDENT);
}
-void FastNoiseLite::_validate_property(PropertyInfo &property) const {
- if (property.name.begins_with("cellular") && get_noise_type() != TYPE_CELLULAR) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+void FastNoiseLite::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name.begins_with("cellular") && get_noise_type() != TYPE_CELLULAR) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
- if (property.name != "fractal_type" && property.name.begins_with("fractal") && get_fractal_type() == FRACTAL_NONE) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+ if (p_property.name != "fractal_type" && p_property.name.begins_with("fractal") && get_fractal_type() == FRACTAL_NONE) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
- if (property.name == "fractal_ping_pong_strength" && get_fractal_type() != FRACTAL_PING_PONG) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+ if (p_property.name == "fractal_ping_pong_strength" && get_fractal_type() != FRACTAL_PING_PONG) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
- if (property.name != "domain_warp_enabled" && property.name.begins_with("domain_warp") && !domain_warp_enabled) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+ if (p_property.name != "domain_warp_enabled" && p_property.name.begins_with("domain_warp") && !domain_warp_enabled) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
}
diff --git a/modules/noise/fastnoise_lite.h b/modules/noise/fastnoise_lite.h
index fe8cd7ce6e..0e6a4606ac 100644
--- a/modules/noise/fastnoise_lite.h
+++ b/modules/noise/fastnoise_lite.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* fastnoise_lite.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* fastnoise_lite.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 FASTNOISE_LITE_H
#define FASTNOISE_LITE_H
@@ -92,7 +92,7 @@ public:
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
+ void _validate_property(PropertyInfo &p_property) const;
private:
_FastNoiseLite _noise;
diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp
index d14b63f7c8..5a901cb6e1 100644
--- a/modules/noise/noise.cpp
+++ b/modules/noise/noise.cpp
@@ -1,40 +1,42 @@
-/*************************************************************************/
-/* noise.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* noise.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "noise.h"
+#include <float.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 skirt_width = MAX(1, p_width * p_blend_skirt);
+ int skirt_height = MAX(1, p_height * p_blend_skirt);
int src_width = p_width + skirt_width;
int src_height = p_height + skirt_height;
@@ -67,8 +69,8 @@ Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_
// Get all values and identify min/max values.
Vector<real_t> values;
values.resize(p_width * p_height);
- real_t min_val = 1000;
- real_t max_val = -1000;
+ real_t min_val = FLT_MAX;
+ real_t max_val = -FLT_MAX;
for (int y = 0, i = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++, i++) {
diff --git a/modules/noise/noise.h b/modules/noise/noise.h
index 8083334388..8f8ecf29a5 100644
--- a/modules/noise/noise.h
+++ b/modules/noise/noise.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* noise.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* noise.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NOISE_H
#define NOISE_H
@@ -107,8 +107,8 @@ class Noise : public Resource {
int skirt_height = MAX(1, p_height * p_blend_skirt);
int src_width = p_width + skirt_width;
int src_height = p_height + skirt_height;
- int half_width = p_width * .5;
- int half_height = p_height * .5;
+ int half_width = p_width * 0.5;
+ int half_height = p_height * 0.5;
int skirt_edge_x = half_width + skirt_width;
int skirt_edge_y = half_height + skirt_height;
@@ -146,7 +146,7 @@ class Noise : public Resource {
// Blend the vertical skirt over the middle seam.
for (int x = half_width; x < skirt_edge_x; x++) {
- int alpha = 255 * (1 - Math::smoothstep(.1f, .9f, float(x - half_width) / float(skirt_width)));
+ int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
for (int y = 0; y < p_height; y++) {
// Skip the center square
if (y == half_height) {
@@ -160,7 +160,7 @@ class Noise : public Resource {
// Blend the horizontal skirt over the middle seam.
for (int y = half_height; y < skirt_edge_y; y++) {
- int alpha = 255 * (1 - Math::smoothstep(.1f, .9f, float(y - half_height) / float(skirt_height)));
+ int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
for (int x = 0; x < p_width; x++) {
// Skip the center square
if (x == half_width) {
@@ -175,8 +175,8 @@ class Noise : public Resource {
// Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used.
for (int y = half_height; y < skirt_edge_y; y++) {
for (int x = half_width; x < skirt_edge_x; x++) {
- int xpos = 255 * (1 - Math::smoothstep(.1f, .9f, float(x - half_width) / float(skirt_width)));
- int ypos = 255 * (1 - Math::smoothstep(.1f, .9f, float(y - half_height) / float(skirt_height)));
+ int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
+ int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
// Blend s3(Q1) onto s5(Q2) for the top half.
T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos);
diff --git a/modules/noise/noise_texture.cpp b/modules/noise/noise_texture_2d.cpp
index ca55d3b96d..0eedb286bd 100644
--- a/modules/noise/noise_texture.cpp
+++ b/modules/noise/noise_texture_2d.cpp
@@ -1,85 +1,86 @@
-/*************************************************************************/
-/* noise_texture.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 "noise_texture.h"
+/**************************************************************************/
+/* noise_texture_2d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "noise_texture_2d.h"
#include "core/core_string_names.h"
#include "noise.h"
-NoiseTexture::NoiseTexture() {
+NoiseTexture2D::NoiseTexture2D() {
noise = Ref<Noise>();
_queue_update();
}
-NoiseTexture::~NoiseTexture() {
+NoiseTexture2D::~NoiseTexture2D() {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
noise_thread.wait_to_finish();
}
-void NoiseTexture::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture::_update_texture);
- ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture::_generate_texture);
- ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture::_thread_done);
+void NoiseTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture2D::_update_texture);
+ ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture2D::_generate_texture);
+ ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture2D::_thread_done);
- ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture::set_width);
- ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture::set_height);
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture2D::set_width);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture2D::set_height);
- ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture::set_invert);
- ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture::get_invert);
+ ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture2D::set_invert);
+ ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture2D::get_invert);
- ClassDB::bind_method(D_METHOD("set_in_3d_space", "enable"), &NoiseTexture::set_in_3d_space);
- ClassDB::bind_method(D_METHOD("is_in_3d_space"), &NoiseTexture::is_in_3d_space);
+ ClassDB::bind_method(D_METHOD("set_in_3d_space", "enable"), &NoiseTexture2D::set_in_3d_space);
+ ClassDB::bind_method(D_METHOD("is_in_3d_space"), &NoiseTexture2D::is_in_3d_space);
- ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "invert"), &NoiseTexture::set_generate_mipmaps);
- ClassDB::bind_method(D_METHOD("is_generating_mipmaps"), &NoiseTexture::is_generating_mipmaps);
+ ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "invert"), &NoiseTexture2D::set_generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("is_generating_mipmaps"), &NoiseTexture2D::is_generating_mipmaps);
- ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless);
- ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless);
+ ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture2D::set_seamless);
+ ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture2D::get_seamless);
- ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture::set_seamless_blend_skirt);
- ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture::get_seamless_blend_skirt);
+ ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture2D::set_seamless_blend_skirt);
+ ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture2D::get_seamless_blend_skirt);
- ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture::set_as_normal_map);
- ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture::is_normal_map);
+ ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture2D::set_as_normal_map);
+ ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture2D::is_normal_map);
- ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture::set_bump_strength);
- ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength);
+ ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture2D::set_bump_strength);
+ ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture2D::get_bump_strength);
- ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture::set_color_ramp);
- ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture::get_color_ramp);
+ ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture2D::set_color_ramp);
+ ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture2D::get_color_ramp);
- ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
- ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
+ ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture2D::set_noise);
+ ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture2D::get_noise);
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");
@@ -87,28 +88,28 @@ void NoiseTexture::_bind_methods() {
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0.05,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
}
-void NoiseTexture::_validate_property(PropertyInfo &property) const {
- if (property.name == "bump_strength") {
+void NoiseTexture2D::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "bump_strength") {
if (!as_normal_map) {
- property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
- if (property.name == "seamless_blend_skirt") {
+ if (p_property.name == "seamless_blend_skirt") {
if (!seamless) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
}
-void NoiseTexture::_set_texture_image(const Ref<Image> &p_image) {
+void NoiseTexture2D::_set_texture_image(const Ref<Image> &p_image) {
image = p_image;
if (image.is_valid()) {
if (texture.is_valid()) {
@@ -121,7 +122,7 @@ void NoiseTexture::_set_texture_image(const Ref<Image> &p_image) {
emit_changed();
}
-void NoiseTexture::_thread_done(const Ref<Image> &p_image) {
+void NoiseTexture2D::_thread_done(const Ref<Image> &p_image) {
_set_texture_image(p_image);
noise_thread.wait_to_finish();
if (regen_queued) {
@@ -130,12 +131,12 @@ void NoiseTexture::_thread_done(const Ref<Image> &p_image) {
}
}
-void NoiseTexture::_thread_function(void *p_ud) {
- NoiseTexture *tex = static_cast<NoiseTexture *>(p_ud);
+void NoiseTexture2D::_thread_function(void *p_ud) {
+ NoiseTexture2D *tex = static_cast<NoiseTexture2D *>(p_ud);
tex->call_deferred(SNAME("_thread_done"), tex->_generate_texture());
}
-void NoiseTexture::_queue_update() {
+void NoiseTexture2D::_queue_update() {
if (update_queued) {
return;
}
@@ -144,7 +145,7 @@ void NoiseTexture::_queue_update() {
call_deferred(SNAME("_update_texture"));
}
-Ref<Image> NoiseTexture::_generate_texture() {
+Ref<Image> NoiseTexture2D::_generate_texture() {
// Prevent memdelete due to unref() on other thread.
Ref<Noise> ref_noise = noise;
@@ -152,38 +153,36 @@ Ref<Image> NoiseTexture::_generate_texture() {
return Ref<Image>();
}
- Ref<Image> image;
+ Ref<Image> new_image;
if (seamless) {
- image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt);
+ new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt);
} else {
- image = ref_noise->get_image(size.x, size.y, invert, in_3d_space);
+ new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space);
}
if (color_ramp.is_valid()) {
- image = _modulate_with_gradient(image, color_ramp);
+ new_image = _modulate_with_gradient(new_image, color_ramp);
}
if (as_normal_map) {
- image->bump_map_to_normal_map(bump_strength);
+ new_image->bump_map_to_normal_map(bump_strength);
}
if (generate_mipmaps) {
- image->generate_mipmaps();
+ new_image->generate_mipmaps();
}
- return image;
+ return new_image;
}
-Ref<Image> NoiseTexture::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
+Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
int width = p_image->get_width();
int height = p_image->get_height();
- Ref<Image> new_image;
- new_image.instantiate();
- new_image->create(width, height, false, Image::FORMAT_RGBA8);
+ Ref<Image> new_image = Image::create_empty(width, height, false, Image::FORMAT_RGBA8);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
Color pixel_color = p_image->get_pixel(col, row);
- Color ramp_color = color_ramp->get_color_at_offset(pixel_color.get_luminance());
+ Color ramp_color = p_gradient->get_color_at_offset(pixel_color.get_luminance());
new_image->set_pixel(col, row, ramp_color);
}
}
@@ -191,15 +190,12 @@ Ref<Image> NoiseTexture::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradien
return new_image;
}
-void NoiseTexture::_update_texture() {
+void NoiseTexture2D::_update_texture() {
bool use_thread = true;
if (first_time) {
use_thread = false;
first_time = false;
}
-#ifdef NO_THREADS
- use_thread = false;
-#endif
if (use_thread) {
if (!noise_thread.is_started()) {
noise_thread.start(_thread_function, this);
@@ -209,31 +205,31 @@ void NoiseTexture::_update_texture() {
}
} else {
- Ref<Image> image = _generate_texture();
- _set_texture_image(image);
+ Ref<Image> new_image = _generate_texture();
+ _set_texture_image(new_image);
}
update_queued = false;
}
-void NoiseTexture::set_noise(Ref<Noise> p_noise) {
+void NoiseTexture2D::set_noise(Ref<Noise> p_noise) {
if (p_noise == noise) {
return;
}
if (noise.is_valid()) {
- noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
+ noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
- noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
+ noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
-Ref<Noise> NoiseTexture::get_noise() {
+Ref<Noise> NoiseTexture2D::get_noise() {
return noise;
}
-void NoiseTexture::set_width(int p_width) {
+void NoiseTexture2D::set_width(int p_width) {
ERR_FAIL_COND(p_width <= 0);
if (p_width == size.x) {
return;
@@ -242,7 +238,7 @@ void NoiseTexture::set_width(int p_width) {
_queue_update();
}
-void NoiseTexture::set_height(int p_height) {
+void NoiseTexture2D::set_height(int p_height) {
ERR_FAIL_COND(p_height <= 0);
if (p_height == size.y) {
return;
@@ -251,7 +247,7 @@ void NoiseTexture::set_height(int p_height) {
_queue_update();
}
-void NoiseTexture::set_invert(bool p_invert) {
+void NoiseTexture2D::set_invert(bool p_invert) {
if (p_invert == invert) {
return;
}
@@ -259,22 +255,22 @@ void NoiseTexture::set_invert(bool p_invert) {
_queue_update();
}
-bool NoiseTexture::get_invert() const {
+bool NoiseTexture2D::get_invert() const {
return invert;
}
-void NoiseTexture::set_in_3d_space(bool p_enable) {
+void NoiseTexture2D::set_in_3d_space(bool p_enable) {
if (p_enable == in_3d_space) {
return;
}
in_3d_space = p_enable;
_queue_update();
}
-bool NoiseTexture::is_in_3d_space() const {
+bool NoiseTexture2D::is_in_3d_space() const {
return in_3d_space;
}
-void NoiseTexture::set_generate_mipmaps(bool p_enable) {
+void NoiseTexture2D::set_generate_mipmaps(bool p_enable) {
if (p_enable == generate_mipmaps) {
return;
}
@@ -282,11 +278,11 @@ void NoiseTexture::set_generate_mipmaps(bool p_enable) {
_queue_update();
}
-bool NoiseTexture::is_generating_mipmaps() const {
+bool NoiseTexture2D::is_generating_mipmaps() const {
return generate_mipmaps;
}
-void NoiseTexture::set_seamless(bool p_seamless) {
+void NoiseTexture2D::set_seamless(bool p_seamless) {
if (p_seamless == seamless) {
return;
}
@@ -295,12 +291,12 @@ void NoiseTexture::set_seamless(bool p_seamless) {
notify_property_list_changed();
}
-bool NoiseTexture::get_seamless() {
+bool NoiseTexture2D::get_seamless() {
return seamless;
}
-void NoiseTexture::set_seamless_blend_skirt(real_t p_blend_skirt) {
- ERR_FAIL_COND(p_blend_skirt < 0.05 || p_blend_skirt > 1);
+void NoiseTexture2D::set_seamless_blend_skirt(real_t p_blend_skirt) {
+ ERR_FAIL_COND(p_blend_skirt < 0 || p_blend_skirt > 1);
if (p_blend_skirt == seamless_blend_skirt) {
return;
@@ -308,11 +304,11 @@ void NoiseTexture::set_seamless_blend_skirt(real_t p_blend_skirt) {
seamless_blend_skirt = p_blend_skirt;
_queue_update();
}
-real_t NoiseTexture::get_seamless_blend_skirt() {
+real_t NoiseTexture2D::get_seamless_blend_skirt() {
return seamless_blend_skirt;
}
-void NoiseTexture::set_as_normal_map(bool p_as_normal_map) {
+void NoiseTexture2D::set_as_normal_map(bool p_as_normal_map) {
if (p_as_normal_map == as_normal_map) {
return;
}
@@ -321,11 +317,11 @@ void NoiseTexture::set_as_normal_map(bool p_as_normal_map) {
notify_property_list_changed();
}
-bool NoiseTexture::is_normal_map() {
+bool NoiseTexture2D::is_normal_map() {
return as_normal_map;
}
-void NoiseTexture::set_bump_strength(float p_bump_strength) {
+void NoiseTexture2D::set_bump_strength(float p_bump_strength) {
if (p_bump_strength == bump_strength) {
return;
}
@@ -335,37 +331,37 @@ void NoiseTexture::set_bump_strength(float p_bump_strength) {
}
}
-float NoiseTexture::get_bump_strength() {
+float NoiseTexture2D::get_bump_strength() {
return bump_strength;
}
-void NoiseTexture::set_color_ramp(const Ref<Gradient> &p_gradient) {
+void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) {
if (p_gradient == color_ramp) {
return;
}
if (color_ramp.is_valid()) {
- color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
+ color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
}
color_ramp = p_gradient;
if (color_ramp.is_valid()) {
- color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update));
+ color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
-Ref<Gradient> NoiseTexture::get_color_ramp() const {
+Ref<Gradient> NoiseTexture2D::get_color_ramp() const {
return color_ramp;
}
-int NoiseTexture::get_width() const {
+int NoiseTexture2D::get_width() const {
return size.x;
}
-int NoiseTexture::get_height() const {
+int NoiseTexture2D::get_height() const {
return size.y;
}
-RID NoiseTexture::get_rid() const {
+RID NoiseTexture2D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
}
@@ -373,6 +369,6 @@ RID NoiseTexture::get_rid() const {
return texture;
}
-Ref<Image> NoiseTexture::get_image() const {
+Ref<Image> NoiseTexture2D::get_image() const {
return image;
}
diff --git a/modules/noise/noise_texture.h b/modules/noise/noise_texture_2d.h
index 6c088562a1..cda14df6c2 100644
--- a/modules/noise/noise_texture.h
+++ b/modules/noise/noise_texture_2d.h
@@ -1,43 +1,43 @@
-/*************************************************************************/
-/* noise_texture.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 NOISE_TEXTURE_H
-#define NOISE_TEXTURE_H
+/**************************************************************************/
+/* noise_texture_2d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NOISE_TEXTURE_2D_H
+#define NOISE_TEXTURE_2D_H
#include "noise.h"
#include "core/object/ref_counted.h"
#include "scene/resources/texture.h"
-class NoiseTexture : public Texture2D {
- GDCLASS(NoiseTexture, Texture2D);
+class NoiseTexture2D : public Texture2D {
+ GDCLASS(NoiseTexture2D, Texture2D);
private:
Ref<Image> image;
@@ -75,7 +75,7 @@ private:
protected:
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
+ void _validate_property(PropertyInfo &p_property) const;
public:
void set_noise(Ref<Noise> p_noise);
@@ -116,8 +116,8 @@ public:
virtual Ref<Image> get_image() const override;
- NoiseTexture();
- virtual ~NoiseTexture();
+ NoiseTexture2D();
+ virtual ~NoiseTexture2D();
};
-#endif // NOISE_TEXTURE_H
+#endif // NOISE_TEXTURE_2D_H
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index d0cfc4e944..1298d72e98 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -1,38 +1,38 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "fastnoise_lite.h"
#include "noise.h"
-#include "noise_texture.h"
+#include "noise_texture_2d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_plugin.h"
@@ -41,9 +41,10 @@
void initialize_noise_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
- GDREGISTER_CLASS(NoiseTexture);
+ GDREGISTER_CLASS(NoiseTexture2D);
GDREGISTER_ABSTRACT_CLASS(Noise);
GDREGISTER_CLASS(FastNoiseLite);
+ ClassDB::add_compatibility_class("NoiseTexture", "NoiseTexture2D");
}
#ifdef TOOLS_ENABLED
diff --git a/modules/noise/register_types.h b/modules/noise/register_types.h
index dfe5a480de..a6086e90e9 100644
--- a/modules/noise/register_types.h
+++ b/modules/noise/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NOISE_REGISTER_TYPES_H
#define NOISE_REGISTER_TYPES_H
diff --git a/modules/noise/tests/test_fastnoise_lite.h b/modules/noise/tests/test_fastnoise_lite.h
new file mode 100644
index 0000000000..0a435c6a5c
--- /dev/null
+++ b/modules/noise/tests/test_fastnoise_lite.h
@@ -0,0 +1,637 @@
+/**************************************************************************/
+/* test_fastnoise_lite.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_FASTNOISE_LITE_H
+#define TEST_FASTNOISE_LITE_H
+
+#include "tests/test_macros.h"
+
+#include "modules/noise/fastnoise_lite.h"
+
+namespace TestFastNoiseLite {
+
+// Uitility functions for finding differences in noise generation
+
+bool all_equal_approx(const Vector<real_t> &p_values_1, const Vector<real_t> &p_values_2) {
+ ERR_FAIL_COND_V_MSG(p_values_1.size() != p_values_2.size(), false, "Arrays must be the same size. This is a error in the test code.");
+
+ for (int i = 0; i < p_values_1.size(); i++) {
+ if (!Math::is_equal_approx(p_values_1[i], p_values_2[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+Vector<Pair<size_t, size_t>> find_approx_equal_vec_pairs(std::initializer_list<Vector<real_t>> inputs) {
+ Vector<Vector<real_t>> p_array = Vector<Vector<real_t>>(inputs);
+
+ Vector<Pair<size_t, size_t>> result;
+ for (int i = 0; i < p_array.size(); i++) {
+ for (int j = i + 1; j < p_array.size(); j++) {
+ if (all_equal_approx(p_array[i], p_array[j])) {
+ result.push_back(Pair<size_t, size_t>(i, j));
+ }
+ }
+ }
+ return result;
+}
+
+#define CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(...) \
+ { \
+ Vector<Pair<size_t, size_t>> equal_pairs = find_approx_equal_vec_pairs({ __VA_ARGS__ }); \
+ for (Pair<size_t, size_t> p : equal_pairs) { \
+ MESSAGE("Argument with index ", p.first, " is approximately equal to argument with index ", p.second); \
+ } \
+ CHECK_MESSAGE(equal_pairs.size() == 0, "All arguments should be pairwise distinct."); \
+ }
+
+Vector<real_t> get_noise_samples_1d(const FastNoiseLite &p_noise, size_t p_count = 32) {
+ Vector<real_t> result;
+ result.resize(p_count);
+ for (size_t i = 0; i < p_count; i++) {
+ result.write[i] = p_noise.get_noise_1d(i);
+ }
+ return result;
+}
+
+Vector<real_t> get_noise_samples_2d(const FastNoiseLite &p_noise, size_t p_count = 32) {
+ Vector<real_t> result;
+ result.resize(p_count);
+ for (size_t i = 0; i < p_count; i++) {
+ result.write[i] = p_noise.get_noise_2d(i, i);
+ }
+ return result;
+}
+
+Vector<real_t> get_noise_samples_3d(const FastNoiseLite &p_noise, size_t p_count = 32) {
+ Vector<real_t> result;
+ result.resize(p_count);
+ for (size_t i = 0; i < p_count; i++) {
+ result.write[i] = p_noise.get_noise_3d(i, i, i);
+ }
+ return result;
+}
+
+// The following test suite is rather for testing the wrapper code than the actual noise generation.
+
+TEST_CASE("[FastNoiseLite] Getter and setter") {
+ FastNoiseLite noise;
+
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
+ CHECK(noise.get_noise_type() == FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
+
+ noise.set_seed(123);
+ CHECK(noise.get_seed() == 123);
+
+ noise.set_frequency(0.123);
+ CHECK(noise.get_frequency() == doctest::Approx(0.123));
+
+ noise.set_offset(Vector3(1, 2, 3));
+ CHECK(noise.get_offset() == Vector3(1, 2, 3));
+
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
+ CHECK(noise.get_fractal_type() == FastNoiseLite::FractalType::FRACTAL_PING_PONG);
+
+ noise.set_fractal_octaves(2);
+ CHECK(noise.get_fractal_octaves() == 2);
+
+ noise.set_fractal_lacunarity(1.123);
+ CHECK(noise.get_fractal_lacunarity() == doctest::Approx(1.123));
+
+ noise.set_fractal_gain(0.123);
+ CHECK(noise.get_fractal_gain() == doctest::Approx(0.123));
+
+ noise.set_fractal_weighted_strength(0.123);
+ CHECK(noise.get_fractal_weighted_strength() == doctest::Approx(0.123));
+
+ noise.set_fractal_ping_pong_strength(0.123);
+ CHECK(noise.get_fractal_ping_pong_strength() == doctest::Approx(0.123));
+
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
+ CHECK(noise.get_cellular_distance_function() == FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
+
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
+ CHECK(noise.get_cellular_return_type() == FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
+
+ noise.set_cellular_jitter(0.123);
+ CHECK(noise.get_cellular_jitter() == doctest::Approx(0.123));
+
+ noise.set_domain_warp_enabled(true);
+ CHECK(noise.is_domain_warp_enabled() == true);
+ noise.set_domain_warp_enabled(false);
+ CHECK(noise.is_domain_warp_enabled() == false);
+
+ noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
+ CHECK(noise.get_domain_warp_type() == FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
+
+ noise.set_domain_warp_amplitude(0.123);
+ CHECK(noise.get_domain_warp_amplitude() == doctest::Approx(0.123));
+
+ noise.set_domain_warp_frequency(0.123);
+ CHECK(noise.get_domain_warp_frequency() == doctest::Approx(0.123));
+
+ noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
+ CHECK(noise.get_domain_warp_fractal_type() == FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
+
+ noise.set_domain_warp_fractal_octaves(2);
+ CHECK(noise.get_domain_warp_fractal_octaves() == 2);
+
+ noise.set_domain_warp_fractal_lacunarity(1.123);
+ CHECK(noise.get_domain_warp_fractal_lacunarity() == doctest::Approx(1.123));
+
+ noise.set_domain_warp_fractal_gain(0.123);
+ CHECK(noise.get_domain_warp_fractal_gain() == doctest::Approx(0.123));
+}
+
+TEST_CASE("[FastNoiseLite] Basic noise generation") {
+ FastNoiseLite noise;
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
+ noise.set_seed(123);
+ noise.set_offset(Vector3(10, 10, 10));
+
+ // 1D noise will be checked just in the cases where there's the possibility of
+ // finding a bug/regression in the wrapper function.
+ // (since it uses FastNoise's 2D noise generator with the Y coordinate set to 0).
+
+ SUBCASE("Determinacy of noise generation (all noise types)") {
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_PERLIN);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE_CUBIC);
+ CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
+ CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
+ }
+
+ SUBCASE("Different seeds should produce different noise") {
+ noise.set_seed(456);
+ Vector<real_t> noise_seed_1_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_seed_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_seed_1_3d = get_noise_samples_3d(noise);
+ noise.set_seed(123);
+ Vector<real_t> noise_seed_2_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_seed_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_seed_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(noise_seed_1_1d, noise_seed_2_1d));
+ CHECK_FALSE(all_equal_approx(noise_seed_1_2d, noise_seed_2_2d));
+ CHECK_FALSE(all_equal_approx(noise_seed_1_3d, noise_seed_2_3d));
+ }
+
+ SUBCASE("Different frequencies should produce different noise") {
+ noise.set_frequency(0.1);
+ Vector<real_t> noise_frequency_1_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_frequency_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_frequency_1_3d = get_noise_samples_3d(noise);
+ noise.set_frequency(1.0);
+ Vector<real_t> noise_frequency_2_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_frequency_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_frequency_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(noise_frequency_1_1d, noise_frequency_2_1d));
+ CHECK_FALSE(all_equal_approx(noise_frequency_1_2d, noise_frequency_2_2d));
+ CHECK_FALSE(all_equal_approx(noise_frequency_1_3d, noise_frequency_2_3d));
+ }
+
+ SUBCASE("Noise should be offset by the offset parameter") {
+ noise.set_offset(Vector3(1, 2, 3));
+ Vector<real_t> noise_offset_1_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_offset_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_offset_1_3d = get_noise_samples_3d(noise);
+ noise.set_offset(Vector3(4, 5, 6));
+ Vector<real_t> noise_offset_2_1d = get_noise_samples_1d(noise);
+ Vector<real_t> noise_offset_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_offset_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(noise_offset_1_1d, noise_offset_2_1d));
+ CHECK_FALSE(all_equal_approx(noise_offset_1_2d, noise_offset_2_2d));
+ CHECK_FALSE(all_equal_approx(noise_offset_1_3d, noise_offset_2_3d));
+ }
+
+ SUBCASE("Different noise types should produce different noise") {
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
+ Vector<real_t> noise_type_simplex_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_simplex_3d = get_noise_samples_3d(noise);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
+ Vector<real_t> noise_type_simplex_smooth_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_simplex_smooth_3d = get_noise_samples_3d(noise);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
+ Vector<real_t> noise_type_cellular_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_cellular_3d = get_noise_samples_3d(noise);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_PERLIN);
+ Vector<real_t> noise_type_perlin_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_perlin_3d = get_noise_samples_3d(noise);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE);
+ Vector<real_t> noise_type_value_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_value_3d = get_noise_samples_3d(noise);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE_CUBIC);
+ Vector<real_t> noise_type_value_cubic_2d = get_noise_samples_2d(noise);
+ Vector<real_t> noise_type_value_cubic_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(noise_type_simplex_2d,
+ noise_type_simplex_smooth_2d,
+ noise_type_cellular_2d,
+ noise_type_perlin_2d,
+ noise_type_value_2d,
+ noise_type_value_cubic_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(noise_type_simplex_3d,
+ noise_type_simplex_smooth_3d,
+ noise_type_cellular_3d,
+ noise_type_perlin_3d,
+ noise_type_value_3d,
+ noise_type_value_cubic_3d);
+ }
+}
+
+TEST_CASE("[FastNoiseLite] Fractal noise") {
+ FastNoiseLite noise;
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
+ noise.set_offset(Vector3(10, 10, 10));
+ noise.set_frequency(0.01);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_FBM);
+ noise.set_fractal_octaves(4);
+ noise.set_fractal_lacunarity(2.0);
+ noise.set_fractal_gain(0.5);
+ noise.set_fractal_weighted_strength(0.5);
+ noise.set_fractal_ping_pong_strength(2.0);
+
+ SUBCASE("Different fractal types should produce different results") {
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
+ Vector<real_t> fractal_type_none_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_type_none_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_FBM);
+ Vector<real_t> fractal_type_fbm_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_type_fbm_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_RIDGED);
+ Vector<real_t> fractal_type_ridged_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_type_ridged_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
+ Vector<real_t> fractal_type_ping_pong_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_type_ping_pong_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(fractal_type_none_2d,
+ fractal_type_fbm_2d,
+ fractal_type_ridged_2d,
+ fractal_type_ping_pong_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(fractal_type_none_3d,
+ fractal_type_fbm_3d,
+ fractal_type_ridged_3d,
+ fractal_type_ping_pong_3d);
+ }
+
+ SUBCASE("Different octaves should produce different results") {
+ noise.set_fractal_octaves(1.0);
+ Vector<real_t> fractal_octaves_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_octaves_1_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_octaves(8.0);
+ Vector<real_t> fractal_octaves_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_octaves_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(fractal_octaves_1_2d, fractal_octaves_2_2d));
+ CHECK_FALSE(all_equal_approx(fractal_octaves_1_3d, fractal_octaves_2_3d));
+ }
+
+ SUBCASE("Different lacunarity should produce different results") {
+ noise.set_fractal_lacunarity(1.0);
+ Vector<real_t> fractal_lacunarity_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_lacunarity_1_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_lacunarity(2.0);
+ Vector<real_t> fractal_lacunarity_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_lacunarity_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(fractal_lacunarity_1_2d, fractal_lacunarity_2_2d));
+ CHECK_FALSE(all_equal_approx(fractal_lacunarity_1_3d, fractal_lacunarity_2_3d));
+ }
+
+ SUBCASE("Different gain should produce different results") {
+ noise.set_fractal_gain(0.5);
+ Vector<real_t> fractal_gain_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_gain_1_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_gain(0.75);
+ Vector<real_t> fractal_gain_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_gain_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(fractal_gain_1_2d, fractal_gain_2_2d));
+ CHECK_FALSE(all_equal_approx(fractal_gain_1_3d, fractal_gain_2_3d));
+ }
+
+ SUBCASE("Different weights should produce different results") {
+ noise.set_fractal_weighted_strength(0.5);
+ Vector<real_t> fractal_weighted_strength_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_weighted_strength_1_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_weighted_strength(0.75);
+ Vector<real_t> fractal_weighted_strength_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_weighted_strength_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(fractal_weighted_strength_1_2d, fractal_weighted_strength_2_2d));
+ CHECK_FALSE(all_equal_approx(fractal_weighted_strength_1_3d, fractal_weighted_strength_2_3d));
+ }
+
+ SUBCASE("Different ping pong strength should produce different results") {
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
+ noise.set_fractal_ping_pong_strength(0.5);
+ Vector<real_t> fractal_ping_pong_strength_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_ping_pong_strength_1_3d = get_noise_samples_3d(noise);
+ noise.set_fractal_ping_pong_strength(0.75);
+ Vector<real_t> fractal_ping_pong_strength_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> fractal_ping_pong_strength_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(fractal_ping_pong_strength_1_2d, fractal_ping_pong_strength_2_2d));
+ CHECK_FALSE(all_equal_approx(fractal_ping_pong_strength_1_3d, fractal_ping_pong_strength_2_3d));
+ }
+}
+
+TEST_CASE("[FastNoiseLite] Cellular noise") {
+ FastNoiseLite noise;
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE);
+ noise.set_frequency(1.0);
+
+ SUBCASE("Different distance functions should produce different results") {
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
+ Vector<real_t> cellular_distance_function_euclidean_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_distance_function_euclidean_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN_SQUARED);
+ Vector<real_t> cellular_distance_function_euclidean_squared_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_distance_function_euclidean_squared_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
+ Vector<real_t> cellular_distance_function_manhattan_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_distance_function_manhattan_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_HYBRID);
+ Vector<real_t> cellular_distance_function_hybrid_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_distance_function_hybrid_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_distance_function_euclidean_2d,
+ cellular_distance_function_euclidean_squared_2d,
+ cellular_distance_function_manhattan_2d,
+ cellular_distance_function_hybrid_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_distance_function_euclidean_3d,
+ cellular_distance_function_euclidean_squared_3d,
+ cellular_distance_function_manhattan_3d,
+ cellular_distance_function_hybrid_3d);
+ }
+
+ SUBCASE("Different return function types should produce different results") {
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_CELL_VALUE);
+ Vector<real_t> cellular_return_type_cell_value_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_cell_value_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE);
+ Vector<real_t> cellular_return_type_distance_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2);
+ Vector<real_t> cellular_return_type_distance2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance2_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_ADD);
+ Vector<real_t> cellular_return_type_distance2_add_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance2_add_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
+ Vector<real_t> cellular_return_type_distance2_sub_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance2_sub_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_MUL);
+ Vector<real_t> cellular_return_type_distance2_mul_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance2_mul_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_DIV);
+ Vector<real_t> cellular_return_type_distance2_div_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_return_type_distance2_div_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_return_type_cell_value_2d,
+ cellular_return_type_distance_2d,
+ cellular_return_type_distance2_2d,
+ cellular_return_type_distance2_add_2d,
+ cellular_return_type_distance2_sub_2d,
+ cellular_return_type_distance2_mul_2d,
+ cellular_return_type_distance2_div_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_return_type_cell_value_3d,
+ cellular_return_type_distance_3d,
+ cellular_return_type_distance2_3d,
+ cellular_return_type_distance2_add_3d,
+ cellular_return_type_distance2_sub_3d,
+ cellular_return_type_distance2_mul_3d,
+ cellular_return_type_distance2_div_3d);
+ }
+
+ SUBCASE("Different cellular jitter should produce different results") {
+ noise.set_cellular_jitter(0.0);
+ Vector<real_t> cellular_jitter_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_jitter_1_3d = get_noise_samples_3d(noise);
+ noise.set_cellular_jitter(0.5);
+ Vector<real_t> cellular_jitter_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> cellular_jitter_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(cellular_jitter_1_2d, cellular_jitter_2_2d));
+ CHECK_FALSE(all_equal_approx(cellular_jitter_1_3d, cellular_jitter_2_3d));
+ }
+}
+
+TEST_CASE("[FastNoiseLite] Domain warp") {
+ FastNoiseLite noise;
+ noise.set_frequency(1.0);
+ noise.set_domain_warp_amplitude(200.0);
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
+ noise.set_domain_warp_enabled(true);
+
+ SUBCASE("Different domain warp types should produce different results") {
+ noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX);
+ Vector<real_t> domain_warp_type_simplex_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_type_simplex_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
+ Vector<real_t> domain_warp_type_simplex_reduced_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_type_simplex_reduced_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_BASIC_GRID);
+ Vector<real_t> domain_warp_type_basic_grid_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_type_basic_grid_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_type_simplex_2d,
+ domain_warp_type_simplex_reduced_2d,
+ domain_warp_type_basic_grid_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_type_simplex_3d,
+ domain_warp_type_simplex_reduced_3d,
+ domain_warp_type_basic_grid_3d);
+ }
+
+ SUBCASE("Different domain warp amplitude should produce different results") {
+ noise.set_domain_warp_amplitude(0.0);
+ Vector<real_t> domain_warp_amplitude_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_amplitude_1_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_amplitude(100.0);
+ Vector<real_t> domain_warp_amplitude_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_amplitude_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(domain_warp_amplitude_1_2d, domain_warp_amplitude_2_2d));
+ CHECK_FALSE(all_equal_approx(domain_warp_amplitude_1_3d, domain_warp_amplitude_2_3d));
+ }
+
+ SUBCASE("Different domain warp frequency should produce different results") {
+ noise.set_domain_warp_frequency(0.1);
+ Vector<real_t> domain_warp_frequency_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_frequency_1_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_frequency(2.0);
+ Vector<real_t> domain_warp_frequency_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_frequency_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(domain_warp_frequency_1_2d, domain_warp_frequency_2_2d));
+ CHECK_FALSE(all_equal_approx(domain_warp_frequency_1_3d, domain_warp_frequency_2_3d));
+ }
+
+ SUBCASE("Different domain warp fractal type should produce different results") {
+ noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_NONE);
+ Vector<real_t> domain_warp_fractal_type_none_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_type_none_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_PROGRESSIVE);
+ Vector<real_t> domain_warp_fractal_type_progressive_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_type_progressive_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
+ Vector<real_t> domain_warp_fractal_type_independent_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_type_independent_3d = get_noise_samples_3d(noise);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_fractal_type_none_2d,
+ domain_warp_fractal_type_progressive_2d,
+ domain_warp_fractal_type_independent_2d);
+
+ CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_fractal_type_none_3d,
+ domain_warp_fractal_type_progressive_3d,
+ domain_warp_fractal_type_independent_3d);
+ }
+
+ SUBCASE("Different domain warp fractal octaves should produce different results") {
+ noise.set_domain_warp_fractal_octaves(1);
+ Vector<real_t> domain_warp_fractal_octaves_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_octaves_1_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_fractal_octaves(6);
+ Vector<real_t> domain_warp_fractal_octaves_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_octaves_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_octaves_1_2d, domain_warp_fractal_octaves_2_2d));
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_octaves_1_3d, domain_warp_fractal_octaves_2_3d));
+ }
+
+ SUBCASE("Different domain warp fractal lacunarity should produce different results") {
+ noise.set_domain_warp_fractal_lacunarity(0.5);
+ Vector<real_t> domain_warp_fractal_lacunarity_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_lacunarity_1_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_fractal_lacunarity(5.0);
+ Vector<real_t> domain_warp_fractal_lacunarity_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_lacunarity_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_lacunarity_1_2d, domain_warp_fractal_lacunarity_2_2d));
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_lacunarity_1_3d, domain_warp_fractal_lacunarity_2_3d));
+ }
+
+ SUBCASE("Different domain warp fractal gain should produce different results") {
+ noise.set_domain_warp_fractal_gain(0.1);
+ Vector<real_t> domain_warp_fractal_gain_1_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_gain_1_3d = get_noise_samples_3d(noise);
+ noise.set_domain_warp_fractal_gain(0.9);
+ Vector<real_t> domain_warp_fractal_gain_2_2d = get_noise_samples_2d(noise);
+ Vector<real_t> domain_warp_fractal_gain_2_3d = get_noise_samples_3d(noise);
+
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_gain_1_2d, domain_warp_fractal_gain_2_2d));
+ CHECK_FALSE(all_equal_approx(domain_warp_fractal_gain_1_3d, domain_warp_fractal_gain_2_3d));
+ }
+}
+
+// Raw image data for the reference images used in the regression tests.
+// Generated with the following code:
+// for (int y = 0; y < img->get_data().size(); y++) {
+// printf("0x%x,", img->get_data()[y]);
+// }
+
+const Vector<uint8_t> ref_img_1_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
+const Vector<uint8_t> ref_img_2_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
+const Vector<uint8_t> ref_img_3_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
+
+// Utiliy function to compare two images pixel by pixel (for easy debugging of regressions)
+void compare_image_with_reference(const Ref<Image> &p_img, const Ref<Image> &p_reference_img) {
+ for (int y = 0; y < p_img->get_height(); y++) {
+ for (int x = 0; x < p_img->get_width(); x++) {
+ CHECK(p_img->get_pixel(x, y) == p_reference_img->get_pixel(x, y));
+ }
+ }
+}
+
+TEST_CASE("[FastNoiseLite] Generating seamless 2D images (11x11px) and compare to reference images") {
+ FastNoiseLite noise;
+ noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
+ noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
+ noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
+ noise.set_frequency(0.1);
+ noise.set_cellular_jitter(0.0);
+
+ SUBCASE("Blend skirt 0.0") {
+ Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.0);
+
+ Ref<Image> ref_img_1 = memnew(Image);
+ ref_img_1->set_data(11, 11, false, Image::FORMAT_L8, ref_img_1_data);
+
+ compare_image_with_reference(img, ref_img_1);
+ }
+
+ SUBCASE("Blend skirt 0.1") {
+ Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1);
+
+ Ref<Image> ref_img_2 = memnew(Image);
+ ref_img_2->set_data(11, 11, false, Image::FORMAT_L8, ref_img_2_data);
+
+ compare_image_with_reference(img, ref_img_2);
+ }
+
+ SUBCASE("Blend skirt 1.0") {
+ Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1);
+
+ Ref<Image> ref_img_3 = memnew(Image);
+ ref_img_3->set_data(11, 11, false, Image::FORMAT_L8, ref_img_3_data);
+
+ compare_image_with_reference(img, ref_img_3);
+ }
+}
+
+} //namespace TestFastNoiseLite
+
+#endif // TEST_FASTNOISE_LITE_H
diff --git a/modules/noise/tests/test_noise_texture_2d.h b/modules/noise/tests/test_noise_texture_2d.h
new file mode 100644
index 0000000000..9e280b5d97
--- /dev/null
+++ b/modules/noise/tests/test_noise_texture_2d.h
@@ -0,0 +1,267 @@
+/**************************************************************************/
+/* test_noise_texture_2d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_NOISE_TEXTURE_2D_H
+#define TEST_NOISE_TEXTURE_2D_H
+
+#include "tests/test_macros.h"
+
+#include "modules/noise/noise_texture_2d.h"
+
+namespace TestNoiseTexture2D {
+
+class NoiseTextureTester : public RefCounted {
+ GDCLASS(NoiseTextureTester, RefCounted);
+
+ const NoiseTexture2D *const texture;
+
+public:
+ NoiseTextureTester(const NoiseTexture2D *const p_texture) :
+ texture{ p_texture } {};
+
+ Color compute_average_color(const Ref<Image> &p_noise_image) {
+ Color r_avg_color{};
+
+ for (int i = 0; i < p_noise_image->get_width(); ++i) {
+ for (int j = 0; j < p_noise_image->get_height(); ++j) {
+ const Color pixel = p_noise_image->get_pixel(i, j);
+ r_avg_color += pixel;
+ }
+ }
+
+ int pixel_count = p_noise_image->get_width() * p_noise_image->get_height();
+ r_avg_color /= pixel_count;
+ return r_avg_color;
+ }
+
+ void check_mip_and_color_ramp() {
+ const Ref<Image> noise_image = texture->get_image();
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
+ CHECK(noise_image->has_mipmaps());
+
+ Color avg_color = compute_average_color(noise_image);
+
+ // Check that the noise texture is modulated correctly by the color ramp (Gradient).
+ CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(0.0), "The noise texture should not be all black");
+ CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(noise_image->get_width() * noise_image->get_height() * 3.0), "The noise texture should not be all white");
+ CHECK_MESSAGE(avg_color.g == doctest::Approx(0.0), "The noise texture should not have any green when modulated correctly by the color ramp");
+ }
+
+ void check_normal_map() {
+ const Ref<Image> noise_image = texture->get_image();
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
+ CHECK_FALSE(noise_image->has_mipmaps());
+
+ Color avg_color = compute_average_color(noise_image);
+
+ // Check for the characteristic color distribution (for tangent space) of a normal map.
+ CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.b == doctest::Approx(1.0).epsilon(0.05));
+ }
+
+ void check_seamless_texture_grayscale() {
+ const Ref<Image> noise_image = texture->get_image();
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_L8);
+
+ Color avg_color = compute_average_color(noise_image);
+
+ // Since it's a grayscale image and every channel except the alpha channel has the
+ // same values (conversion happens in Image::get_pixel) we only need to test one channel.
+ CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
+ }
+
+ void check_seamless_texture_rgba() {
+ const Ref<Image> noise_image = texture->get_image();
+ CHECK(noise_image.is_valid());
+ CHECK(noise_image->get_width() == texture->get_width());
+ CHECK(noise_image->get_height() == texture->get_height());
+
+ CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
+
+ // Check that the noise texture is modulated correctly by the color ramp (Gradient).
+ Color avg_color = compute_average_color(noise_image);
+
+ // We use a default (black to white) gradient, so the average of the red, green and blue channels should be the same.
+ CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
+ CHECK(avg_color.b == doctest::Approx(0.5).epsilon(0.05));
+ }
+};
+
+TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") {
+ Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise_texture->set_noise(noise);
+ CHECK(noise_texture->get_noise() == noise);
+ noise_texture->set_noise(nullptr);
+ CHECK(noise_texture->get_noise() == nullptr);
+
+ noise_texture->set_width(8);
+ noise_texture->set_height(4);
+ CHECK(noise_texture->get_width() == 8);
+ CHECK(noise_texture->get_height() == 4);
+
+ ERR_PRINT_OFF;
+ noise_texture->set_width(-1);
+ noise_texture->set_height(-1);
+ ERR_PRINT_ON;
+ CHECK(noise_texture->get_width() == 8);
+ CHECK(noise_texture->get_height() == 4);
+
+ noise_texture->set_invert(true);
+ CHECK(noise_texture->get_invert() == true);
+ noise_texture->set_invert(false);
+ CHECK(noise_texture->get_invert() == false);
+
+ noise_texture->set_in_3d_space(true);
+ CHECK(noise_texture->is_in_3d_space() == true);
+ noise_texture->set_in_3d_space(false);
+ CHECK(noise_texture->is_in_3d_space() == false);
+
+ noise_texture->set_generate_mipmaps(true);
+ CHECK(noise_texture->is_generating_mipmaps() == true);
+ noise_texture->set_generate_mipmaps(false);
+ CHECK(noise_texture->is_generating_mipmaps() == false);
+
+ noise_texture->set_seamless(true);
+ CHECK(noise_texture->get_seamless() == true);
+ noise_texture->set_seamless(false);
+ CHECK(noise_texture->get_seamless() == false);
+
+ noise_texture->set_seamless_blend_skirt(0.45);
+ CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
+
+ ERR_PRINT_OFF;
+ noise_texture->set_seamless_blend_skirt(-1.0);
+ noise_texture->set_seamless_blend_skirt(2.0);
+ CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
+ ERR_PRINT_ON;
+
+ noise_texture->set_as_normal_map(true);
+ CHECK(noise_texture->is_normal_map() == true);
+ noise_texture->set_as_normal_map(false);
+ CHECK(noise_texture->is_normal_map() == false);
+
+ noise_texture->set_bump_strength(0.168);
+ CHECK(noise_texture->get_bump_strength() == doctest::Approx(0.168));
+
+ Ref<Gradient> gradient = memnew(Gradient);
+ noise_texture->set_color_ramp(gradient);
+ CHECK(noise_texture->get_color_ramp() == gradient);
+ noise_texture->set_color_ramp(nullptr);
+ CHECK(noise_texture->get_color_ramp() == nullptr);
+}
+
+TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") {
+ Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise_texture->set_noise(noise);
+
+ Ref<Gradient> gradient = memnew(Gradient);
+ Vector<Gradient::Point> points;
+ points.push_back({ 0.0, Color(1, 0, 0) });
+ points.push_back({ 1.0, Color(0, 0, 1) });
+ gradient->set_points(points);
+ noise_texture->set_color_ramp(gradient);
+ noise_texture->set_width(16);
+ noise_texture->set_height(16);
+ noise_texture->set_generate_mipmaps(true);
+
+ Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
+ noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp));
+ MessageQueue::get_singleton()->flush();
+}
+
+TEST_CASE("[NoiseTexture2D][SceneTree] Generating a normal map without mipmaps") {
+ Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise->set_frequency(0.5);
+ noise_texture->set_noise(noise);
+ noise_texture->set_width(16);
+ noise_texture->set_height(16);
+ noise_texture->set_as_normal_map(true);
+ noise_texture->set_bump_strength(0.5);
+ noise_texture->set_generate_mipmaps(false);
+
+ Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
+ noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map));
+ MessageQueue::get_singleton()->flush();
+}
+
+TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") {
+ Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
+
+ Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
+ noise->set_frequency(0.5);
+ noise_texture->set_noise(noise);
+ noise_texture->set_width(16);
+ noise_texture->set_height(16);
+ noise_texture->set_seamless(true);
+
+ Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
+
+ SUBCASE("Grayscale(L8) 16x16, with seamless blend skirt of 0.05") {
+ noise_texture->set_seamless_blend_skirt(0.05);
+ noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale));
+ MessageQueue::get_singleton()->flush();
+ }
+
+ SUBCASE("16x16 modulated with default (transparent)black and white gradient (RGBA8), with seamless blend skirt of 1.0") {
+ Ref<Gradient> gradient = memnew(Gradient);
+ Vector<Gradient::Point> points;
+ points.push_back({ 0.0, Color(0, 0, 0, 0) });
+ points.push_back({ 1.0, Color(1, 1, 1, 1) });
+ gradient->set_points(points);
+ noise_texture->set_color_ramp(gradient);
+ noise_texture->set_seamless_blend_skirt(1.0);
+ noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba));
+ MessageQueue::get_singleton()->flush();
+ }
+}
+
+} //namespace TestNoiseTexture2D
+
+#endif // TEST_NOISE_TEXTURE_2D_H
diff --git a/modules/ogg/doc_classes/OggPacketSequence.xml b/modules/ogg/doc_classes/OggPacketSequence.xml
index d3bd4455d8..1148b38602 100644
--- a/modules/ogg/doc_classes/OggPacketSequence.xml
+++ b/modules/ogg/doc_classes/OggPacketSequence.xml
@@ -17,10 +17,10 @@
</method>
</methods>
<members>
- <member name="granule_positions" type="Array" setter="set_packet_granule_positions" getter="get_packet_granule_positions" default="[]">
+ <member name="granule_positions" type="PackedInt64Array" setter="set_packet_granule_positions" getter="get_packet_granule_positions" default="PackedInt64Array()">
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="[]">
+ <member name="packet_data" type="Array[]" setter="set_packet_data" getter="get_packet_data" default="[]">
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">
diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp
index de8bf4a087..d473f3b4a0 100644
--- a/modules/ogg/ogg_packet_sequence.cpp
+++ b/modules/ogg/ogg_packet_sequence.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* ogg_packet_sequence.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* ogg_packet_sequence.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "ogg_packet_sequence.h"
#include "core/variant/typed_array.h"
@@ -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 TypedArray<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,8 +54,8 @@ void OggPacketSequence::set_packet_data(const Array &p_data) {
}
}
-Array OggPacketSequence::get_packet_data() const {
- Array ret;
+TypedArray<Array> OggPacketSequence::get_packet_data() const {
+ TypedArray<Array> ret;
for (const Vector<PackedByteArray> &page : page_data) {
Array page_variant;
for (const PackedByteArray &packet : page) {
@@ -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 PackedInt64Array &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,8 +75,8 @@ void OggPacketSequence::set_packet_granule_positions(const Array &p_granule_posi
}
}
-Array OggPacketSequence::get_packet_granule_positions() const {
- Array ret;
+PackedInt64Array OggPacketSequence::get_packet_granule_positions() const {
+ PackedInt64Array ret;
for (int64_t granule_pos : page_granule_positions) {
ret.push_back(granule_pos);
}
@@ -127,8 +127,8 @@ void OggPacketSequence::_bind_methods() {
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::ARRAY, "packet_data", PROPERTY_HINT_ARRAY_TYPE, "PackedByteArray", PROPERTY_USAGE_NO_EDITOR), "set_packet_data", "get_packet_data");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT64_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");
}
@@ -136,6 +136,8 @@ 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);
+ ERR_FAIL_COND_V(page_cursor >= ogg_packet_sequence->page_data.size(), false);
+
// Move on to the next page if need be. This happens first to help simplify seek logic.
while (packet_cursor >= ogg_packet_sequence->page_data[page_cursor].size()) {
packet_cursor = 0;
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index efd3b64a39..4aa5ffa254 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* ogg_packet_sequence.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* ogg_packet_sequence.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 OGG_PACKET_SEQUENCE_H
#define OGG_PACKET_SEQUENCE_H
@@ -67,11 +67,11 @@ public:
// This should be called for each page, even for pages that no packets ended on.
void push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data);
- void set_packet_data(const Array &p_data);
- Array get_packet_data() const;
+ void set_packet_data(const TypedArray<Array> &p_data);
+ TypedArray<Array> get_packet_data() const;
- void set_packet_granule_positions(const Array &p_granule_positions);
- Array get_packet_granule_positions() const;
+ void set_packet_granule_positions(const PackedInt64Array &p_granule_positions);
+ PackedInt64Array get_packet_granule_positions() const;
// Sets a sampling rate associated with this object. OggPacketSequence doesn't understand codecs,
// so this value is naively stored as a convenience.
diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp
index aca6d1e1f8..218a253542 100644
--- a/modules/ogg/register_types.cpp
+++ b/modules/ogg/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/ogg/register_types.h b/modules/ogg/register_types.h
index 9065d26d07..a23b86c741 100644
--- a/modules/ogg/register_types.h
+++ b/modules/ogg/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 OGG_REGISTER_TYPES_H
#define OGG_REGISTER_TYPES_H
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index 593d1ff3c1..3b39967ba4 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -18,38 +18,36 @@ env_openxr.Prepend(
thirdparty_dir + "/src",
thirdparty_dir + "/src/common",
thirdparty_dir + "/src/external/jsoncpp/include",
- thirdparty_dir + "/src/loader",
]
)
-# may need to check and set:
-# - XR_USE_TIMESPEC
-
-env_thirdparty = env_openxr.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
-
if env["platform"] == "android":
# may need to set OPENXR_ANDROID_VERSION_SUFFIX
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
+ env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"])
+ env_openxr.AppendUnique(CPPDEFINES=["JSON_USE_EXCEPTION=0"])
# may need to include java parts of the openxr loader
elif env["platform"] == "linuxbsd":
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
+ env_openxr.AppendUnique(CPPDEFINES=["XR_OS_LINUX"])
if env["x11"]:
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
+ env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])
# FIXME: Review what needs to be set for Android and macOS.
- env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
+ env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
- env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
+ env_openxr.AppendUnique(CPPDEFINES=["XR_OS_WINDOWS", "NOMINMAX", "XR_USE_PLATFORM_WIN32"])
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+# may need to check and set:
+# - XR_USE_TIMESPEC
-# add in common files (hope these don't clash with us)
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/common/object_info.cpp")
+env_thirdparty = env_openxr.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"])
+
+if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]:
+ env_thirdparty["CXXFLAGS"].remove("-fno-exceptions")
+env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"])
# add in external jsoncpp dependency
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp")
@@ -57,17 +55,24 @@ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp")
# add in load
-if env["platform"] == "android":
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/android_utilities.cpp")
-
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+if env["platform"] != "android":
+ # On Android the openxr_loader is provided by separate plugins for each device
+ # Build the engine using object files
+ khrloader_obj = []
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c")
+
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp")
+
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp")
+ env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp")
+ env.modules_sources += khrloader_obj
env.modules_sources += thirdparty_obj
@@ -78,18 +83,30 @@ module_obj = []
env_openxr.add_source_files(module_obj, "*.cpp")
env_openxr.add_source_files(module_obj, "action_map/*.cpp")
+env_openxr.add_source_files(module_obj, "scene/*.cpp")
# We're a little more targeted with our extensions
if env["platform"] == "android":
env_openxr.add_source_files(module_obj, "extensions/openxr_android_extension.cpp")
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
+if env["opengl3"]:
+ env_openxr.add_source_files(module_obj, "extensions/openxr_opengl_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_palm_pose_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_depth_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_htc_controller_extension.cpp")
env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_huawei_controller_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_pico_controller_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_wmr_controller_extension.cpp")
env.modules_sources += module_obj
-if env["tools"]:
+if env.editor_build:
SConscript("editor/SCsub")
# Needed to force rebuilding the module files when the thirdparty library is updated.
diff --git a/modules/openxr/action_map/openxr_action.cpp b/modules/openxr/action_map/openxr_action.cpp
index 359975a480..a768eb2326 100644
--- a/modules/openxr/action_map/openxr_action.cpp
+++ b/modules/openxr/action_map/openxr_action.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action.h"
#include "openxr_action_set.h"
@@ -42,7 +42,7 @@ void OpenXRAction::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_toplevel_paths", "toplevel_paths"), &OpenXRAction::set_toplevel_paths);
ClassDB::bind_method(D_METHOD("get_toplevel_paths"), &OpenXRAction::get_toplevel_paths);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "toplevel_paths", PROPERTY_HINT_ARRAY_TYPE, "STRING"), "set_toplevel_paths", "get_toplevel_paths");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "toplevel_paths"), "set_toplevel_paths", "get_toplevel_paths");
BIND_ENUM_CONSTANT(OPENXR_ACTION_BOOL);
BIND_ENUM_CONSTANT(OPENXR_ACTION_FLOAT);
@@ -64,17 +64,18 @@ Ref<OpenXRAction> OpenXRAction::new_action(const char *p_name, const char *p_loc
}
String OpenXRAction::get_name_with_set() const {
- String name = get_name();
+ String action_name = get_name();
if (action_set != nullptr) {
- name = action_set->get_name() + "/" + name;
+ action_name = action_set->get_name() + "/" + action_name;
}
- return name;
+ return action_name;
}
void OpenXRAction::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
+ emit_changed();
}
String OpenXRAction::get_localized_name() const {
@@ -83,6 +84,7 @@ String OpenXRAction::get_localized_name() const {
void OpenXRAction::set_action_type(const OpenXRAction::ActionType p_action_type) {
action_type = p_action_type;
+ emit_changed();
}
OpenXRAction::ActionType OpenXRAction::get_action_type() const {
@@ -91,6 +93,7 @@ OpenXRAction::ActionType OpenXRAction::get_action_type() const {
void OpenXRAction::set_toplevel_paths(const PackedStringArray p_toplevel_paths) {
toplevel_paths = p_toplevel_paths;
+ emit_changed();
}
PackedStringArray OpenXRAction::get_toplevel_paths() const {
@@ -100,15 +103,18 @@ PackedStringArray OpenXRAction::get_toplevel_paths() const {
void OpenXRAction::add_toplevel_path(const String p_toplevel_path) {
if (!toplevel_paths.has(p_toplevel_path)) {
toplevel_paths.push_back(p_toplevel_path);
+ emit_changed();
}
}
void OpenXRAction::rem_toplevel_path(const String p_toplevel_path) {
if (toplevel_paths.has(p_toplevel_path)) {
toplevel_paths.erase(p_toplevel_path);
+ emit_changed();
}
}
void OpenXRAction::parse_toplevel_paths(const String p_toplevel_paths) {
toplevel_paths = p_toplevel_paths.split(",", false);
+ emit_changed();
}
diff --git a/modules/openxr/action_map/openxr_action.h b/modules/openxr/action_map/openxr_action.h
index a7c1c9988c..156a9833a7 100644
--- a/modules/openxr/action_map/openxr_action.h
+++ b/modules/openxr/action_map/openxr_action.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_H
#define OPENXR_ACTION_H
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index 0eb5302442..669c395b3e 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_map.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action_map.h"
@@ -95,6 +95,7 @@ void OpenXRActionMap::add_action_set(Ref<OpenXRActionSet> p_action_set) {
if (action_sets.find(p_action_set) == -1) {
action_sets.push_back(p_action_set);
+ emit_changed();
}
}
@@ -102,6 +103,7 @@ void OpenXRActionMap::remove_action_set(Ref<OpenXRActionSet> p_action_set) {
int idx = action_sets.find(p_action_set);
if (idx != -1) {
action_sets.remove_at(idx);
+ emit_changed();
}
}
@@ -146,6 +148,7 @@ void OpenXRActionMap::add_interaction_profile(Ref<OpenXRInteractionProfile> p_in
if (interaction_profiles.find(p_interaction_profile) == -1) {
interaction_profiles.push_back(p_interaction_profile);
+ emit_changed();
}
}
@@ -153,11 +156,16 @@ void OpenXRActionMap::remove_interaction_profile(Ref<OpenXRInteractionProfile> p
int idx = interaction_profiles.find(p_interaction_profile);
if (idx != -1) {
interaction_profiles.remove_at(idx);
+ emit_changed();
}
}
void OpenXRActionMap::create_default_action_sets() {
- // Note, if you make changes here make sure to delete your default_action_map.tres file of it will load an old version.
+ // Note:
+ // - if you make changes here make sure to delete your default_action_map.tres file of it will load an old version.
+ // - our palm pose is only available if the relevant extension is supported,
+ // we still want it to be part of our action map as we may deploy the same game to platforms that do and don't support it.
+ // - the same applies for interaction profiles that are only supported if the relevant extension is supported.
// Create our Godot action set
Ref<OpenXRActionSet> action_set = OpenXRActionSet::new_action_set("godot", "Godot action set");
@@ -200,6 +208,7 @@ void OpenXRActionMap::create_default_action_sets() {
"/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> palm_pose = action_set->add_new_action("palm_pose", "Palm 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,"
@@ -219,9 +228,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our interaction profiles
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/select/click,/user/hand/right/input/select/click");
// generic has no support for triggers, grip, A/B buttons, nor joystick/trackpad inputs
@@ -230,9 +240,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Vive controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
// wmr controller has no a/b/x/y buttons
@@ -250,9 +261,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our WMR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// wmr controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// wmr controller has no a/b/x/y buttons
@@ -272,9 +284,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Meta touch controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// touch controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/system/click"); // right hand system click may not be available
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
@@ -294,11 +307,37 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
+ // Create our Pico 4 / Neo 3 controller profile
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/pico/neo3_controller");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
+ profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
+ profile->add_new_binding(select_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click"); // system click may not be available
+ profile->add_new_binding(menu_button, "/user/hand/left/input/back/click,/user/hand/right/input/back/click"); // right hand back click may not be available
+ profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
+ profile->add_new_binding(ax_touch, "/user/hand/left/input/x/touch,/user/hand/right/input/a/touch");
+ profile->add_new_binding(by_button, "/user/hand/left/input/y/click,/user/hand/right/input/b/click"); // y on left hand, b on right hand
+ profile->add_new_binding(by_touch, "/user/hand/left/input/y/touch,/user/hand/right/input/b/touch");
+ profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
+ profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value"); // should be converted to boolean
+ profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
+ profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // should be converted to boolean
+ profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
+ // primary on our pico controller is our thumbstick
+ profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
+ profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
+ profile->add_new_binding(primary_touch, "/user/hand/left/input/thumbstick/touch,/user/hand/right/input/thumbstick/touch");
+ // pico controller has no secondary input
+ profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
+ add_interaction_profile(profile);
+
// Create our Valve index controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// index controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/system/click,/user/hand/right/input/system/click");
profile->add_new_binding(ax_button, "/user/hand/left/input/a/click,/user/hand/right/input/a/click"); // a on both controllers
@@ -321,16 +360,12 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
add_interaction_profile(profile);
- // Note, the following profiles are all part of extensions.
- // We include these regardless of whether the extension is active.
- // We want our action map to be as complete as possible so our game is as portable as possible.
- // It is very possible these will in due time become core.
-
// Create our HP MR controller profile
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// hpmr controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// hpmr controllers only register click, not touch, on our a/b/x/y buttons
@@ -350,9 +385,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Samsung Odyssey controller profile,
// Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller.
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
// Odyssey controllers have no select button we can use
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click,/user/hand/right/input/menu/click");
// Odyssey controller has no a/b/x/y buttons
@@ -372,9 +408,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Vive Cosmos controller
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
@@ -395,9 +432,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Note, Vive Focus 3 currently is not yet supported as a stand alone device
// however HTC currently has a beta OpenXR runtime in testing we may support in the near future
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/menu/click");
profile->add_new_binding(select_button, "/user/hand/left/input/system/click"); // we'll map system to select
profile->add_new_binding(ax_button, "/user/hand/left/input/x/click,/user/hand/right/input/a/click"); // x on left hand, a on right hand
@@ -418,9 +456,10 @@ void OpenXRActionMap::create_default_action_sets() {
// Create our Huawei controller
profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller");
- profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose");
profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose");
+ profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose");
profile->add_new_binding(menu_button, "/user/hand/left/input/home/click,/user/hand/right/input/home/click");
profile->add_new_binding(trigger, "/user/hand/left/input/trigger/value,/user/hand/right/input/trigger/value");
profile->add_new_binding(trigger_click, "/user/hand/left/input/trigger/click,/user/hand/right/input/trigger/click");
@@ -479,30 +518,34 @@ Ref<OpenXRAction> OpenXRActionMap::get_action(const String p_path) const {
return Ref<OpenXRAction>();
}
-void OpenXRActionMap::remove_action(const String p_path) {
+void OpenXRActionMap::remove_action(const String p_path, bool p_remove_interaction_profiles) {
Ref<OpenXRAction> action = get_action(p_path);
if (action.is_valid()) {
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
+
+ if (p_remove_interaction_profiles) {
+ // Remove any bindings for this action
+ interaction_profile->remove_binding_for_action(action);
+ } else {
+ ERR_FAIL_COND(interaction_profile->has_binding_for_action(action));
+ }
+ }
+
OpenXRActionSet *action_set = action->get_action_set();
if (action_set != nullptr) {
// Remove the action from this action set
action_set->remove_action(action);
}
-
- for (int i = 0; i < interaction_profiles.size(); i++) {
- Ref<OpenXRInteractionProfile> interaction_profile = interaction_profiles[i];
-
- // Remove any bindings for this action
- interaction_profile->remove_binding_for_action(action);
- }
}
}
-PackedStringArray OpenXRActionMap::get_top_level_paths(Ref<OpenXRAction> p_action) {
+PackedStringArray OpenXRActionMap::get_top_level_paths(const Ref<OpenXRAction> p_action) {
PackedStringArray arr;
for (int i = 0; i < interaction_profiles.size(); i++) {
Ref<OpenXRInteractionProfile> ip = interaction_profiles[i];
- const OpenXRDefs::InteractionProfile *profile = OpenXRDefs::get_profile(ip->get_interaction_profile_path());
+ const OpenXRInteractionProfileMetaData::InteractionProfile *profile = OpenXRInteractionProfileMetaData::get_singleton()->get_profile(ip->get_interaction_profile_path());
if (profile != nullptr) {
for (int j = 0; j < ip->get_binding_count(); j++) {
@@ -511,9 +554,9 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(Ref<OpenXRAction> p_actio
PackedStringArray paths = binding->get_paths();
for (int k = 0; k < paths.size(); k++) {
- const OpenXRDefs::IOPath *io_path = profile->get_io_path(paths[k]);
+ const OpenXRInteractionProfileMetaData::IOPath *io_path = profile->get_io_path(paths[k]);
if (io_path != nullptr) {
- String top_path = String(io_path->top_level_path->openxr_path);
+ String top_path = io_path->top_level_path;
if (!arr.has(top_path)) {
arr.push_back(top_path);
@@ -525,7 +568,7 @@ PackedStringArray OpenXRActionMap::get_top_level_paths(Ref<OpenXRAction> p_actio
}
}
- print_line("Toplevel paths for", p_action->get_name_with_set(), "are", arr);
+ // print_line("Toplevel paths for", p_action->get_name_with_set(), "are", arr);
return arr;
}
diff --git a/modules/openxr/action_map/openxr_action_map.h b/modules/openxr/action_map/openxr_action_map.h
index 8659cd3942..0c88fac5ec 100644
--- a/modules/openxr/action_map/openxr_action_map.h
+++ b/modules/openxr/action_map/openxr_action_map.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_map.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_map.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_MAP_H
#define OPENXR_ACTION_MAP_H
@@ -71,8 +71,8 @@ public:
// Helper functions for editor
Ref<OpenXRAction> get_action(const String p_path) const; // Retrieve an action using <action name>/<action> as our parameter
- void remove_action(const String p_path); // Remove action from action set, also removes it from interaction profiles
- PackedStringArray get_top_level_paths(Ref<OpenXRAction> p_action); // Determines the top level paths based on where an action is bound in interaction profiles
+ void remove_action(const String p_path, bool p_remove_interaction_profiles = false); // Remove action from action set, also removes it from interaction profiles
+ PackedStringArray get_top_level_paths(const Ref<OpenXRAction> p_action); // Determines the top level paths based on where an action is bound in interaction profiles
// TODO add validation to display in the interface that checks if we have action sets with the same name or if we have interaction profiles for the same path
diff --git a/modules/openxr/action_map/openxr_action_set.cpp b/modules/openxr/action_map/openxr_action_set.cpp
index be45218300..4855f9e4b7 100644
--- a/modules/openxr/action_map/openxr_action_set.cpp
+++ b/modules/openxr/action_map/openxr_action_set.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_set.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_set.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action_set.h"
@@ -62,6 +62,7 @@ Ref<OpenXRActionSet> OpenXRActionSet::new_action_set(const char *p_name, const c
void OpenXRActionSet::set_localized_name(const String p_localized_name) {
localized_name = p_localized_name;
+ emit_changed();
}
String OpenXRActionSet::get_localized_name() const {
@@ -70,6 +71,7 @@ String OpenXRActionSet::get_localized_name() const {
void OpenXRActionSet::set_priority(const int p_priority) {
priority = p_priority;
+ emit_changed();
}
int OpenXRActionSet::get_priority() const {
@@ -82,11 +84,16 @@ int OpenXRActionSet::get_action_count() const {
void OpenXRActionSet::clear_actions() {
// Actions held within our action set should be released and destroyed but just in case they are still used some where else
+ if (actions.size() == 0) {
+ return;
+ }
+
for (int i = 0; i < actions.size(); i++) {
Ref<OpenXRAction> action = actions[i];
action->action_set = nullptr;
}
actions.clear();
+ emit_changed();
}
void OpenXRActionSet::set_actions(Array p_actions) {
@@ -125,6 +132,7 @@ void OpenXRActionSet::add_action(Ref<OpenXRAction> p_action) {
p_action->action_set = this;
actions.push_back(p_action);
+ emit_changed();
}
}
@@ -135,6 +143,8 @@ void OpenXRActionSet::remove_action(Ref<OpenXRAction> p_action) {
ERR_FAIL_COND_MSG(p_action->action_set != this, "Removing action that belongs to this action set but had incorrect action set pointer."); // this should never happen!
p_action->action_set = nullptr;
+
+ emit_changed();
}
}
diff --git a/modules/openxr/action_map/openxr_action_set.h b/modules/openxr/action_map/openxr_action_set.h
index 2ef7ba4c32..0cb342784a 100644
--- a/modules/openxr/action_map/openxr_action_set.h
+++ b/modules/openxr/action_map/openxr_action_set.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_set.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_set.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_SET_H
#define OPENXR_ACTION_SET_H
diff --git a/modules/openxr/action_map/openxr_defs.cpp b/modules/openxr/action_map/openxr_defs.cpp
deleted file mode 100644
index 89860199be..0000000000
--- a/modules/openxr/action_map/openxr_defs.cpp
+++ /dev/null
@@ -1,651 +0,0 @@
-/*************************************************************************/
-/* openxr_defs.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_defs.h"
-
-// 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
-OpenXRDefs::IOPath OpenXRDefs::simple_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Select click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/select/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Select click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/select/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Original HTC Vive wands
-OpenXRDefs::IOPath OpenXRDefs::vive_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Microsoft motion controller (original WMR controllers)
-OpenXRDefs::IOPath OpenXRDefs::motion_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// HP MR controller (newer G2 controllers)
-OpenXRDefs::IOPath OpenXRDefs::hpmr_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Meta touch controller (original touch controllers, Quest 1 and Quest 2 controllers)
-OpenXRDefs::IOPath OpenXRDefs::touch_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "X touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Y touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Valve index controller
-OpenXRDefs::IOPath OpenXRDefs::index_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Squeeze", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/value", OpenXRAction::OPENXR_ACTION_FLOAT },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad force", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/force", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad force", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/force", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Samsung odyssey controller
-OpenXRDefs::IOPath OpenXRDefs::odyssey_io_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Vive Cosmos controller
-OpenXRDefs::IOPath OpenXRDefs::vive_cosmos_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Shoulder click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/right/input/shoulder/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Shoulder click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/shoulder/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Vive Focus 3 controller
-OpenXRDefs::IOPath OpenXRDefs::vive_focus3_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "System click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/system/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "X click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/x/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Y click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/y/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "A click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/a/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "B click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/b/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/touch ", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/squeeze/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Squeeze touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/squeeze/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Thumbstick click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Thumbstick touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbstick/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Thumbrest touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/thumbrest/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
-};
-
-// Huawei controller
-OpenXRDefs::IOPath OpenXRDefs::huawei_controller_paths[] = {
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
- { "Aim pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/aim/pose", OpenXRAction::OPENXR_ACTION_POSE },
-
- { "Home click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/home/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Home click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/home/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Back click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/back/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Back click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/back/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Volume up click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/volume_up/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Volume up click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/volume_up/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Volume down click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/volume_down/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Volume down click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/volume_down/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
- { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
- { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
- { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
-
- { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_LEFT_HAND], "/user/hand/left/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
- { "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
- "/interaction_profiles/khr/simple_controller", // openxr_path
- simple_io_paths, // io_paths
- sizeof(simple_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "HTC Vive wand", // display_name
- "/interaction_profiles/htc/vive_controller", // openxr_path
- vive_io_paths, // io_paths
- sizeof(vive_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "MS Motion controller", // display_name
- "/interaction_profiles/microsoft/motion_controller", // openxr_path
- motion_io_paths, // io_paths
- sizeof(motion_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "HPMR controller", // display_name
- "/interaction_profiles/hp/mixed_reality_controller", // openxr_path
- hpmr_io_paths, // io_paths
- sizeof(hpmr_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Touch controller", // display_name
- "/interaction_profiles/oculus/touch_controller", // openxr_path
- touch_io_paths, // io_paths
- sizeof(touch_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Index controller", // display_name
- "/interaction_profiles/valve/index_controller", // openxr_path
- index_io_paths, // io_paths
- sizeof(index_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Samsung Odyssey controller", // display_name
- "/interaction_profiles/samsung/odyssey_controller", // openxr_path
- odyssey_io_paths, // io_paths
- sizeof(odyssey_io_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Vive Cosmos controller", // display_name
- "/interaction_profiles/htc/vive_cosmos_controller", // openxr_path
- vive_cosmos_paths, // io_paths
- sizeof(vive_cosmos_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Vive Focus 3 controller", // display_name
- "/interaction_profiles/htc/vive_focus3_controller", // openxr_path
- vive_focus3_paths, // io_paths
- sizeof(vive_focus3_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
- },
- {
- "Huawei controller", // display_name
- "/interaction_profiles/huawei/controller", // openxr_path
- 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);
-
-const OpenXRDefs::TopLevelPath *OpenXRDefs::get_top_level_path(const String p_top_level_path) {
- for (int i = 0; i < OPENXR_TOP_LEVEL_PATH_MAX; i++) {
- if (available_top_level_paths[i].openxr_path == p_top_level_path) {
- return &OpenXRDefs::available_top_level_paths[i];
- }
- }
-
- return nullptr;
-}
-
-const OpenXRDefs::InteractionProfile *OpenXRDefs::get_profile(const String p_interaction_profile_path) {
- for (int i = 0; i < available_interaction_profile_count; i++) {
- if (available_interaction_profiles[i].openxr_path == p_interaction_profile_path) {
- return &available_interaction_profiles[i];
- }
- }
-
- return nullptr;
-}
-
-const OpenXRDefs::IOPath *OpenXRDefs::InteractionProfile::get_io_path(const String p_io_path) const {
- for (int i = 0; i < available_interaction_profiles[i].io_path_count; i++) {
- if (io_paths[i].openxr_path == p_io_path) {
- return &io_paths[i];
- }
- }
-
- return nullptr;
-}
-
-const OpenXRDefs::IOPath *OpenXRDefs::get_io_path(const String p_interaction_profile_path, const String p_io_path) {
- const OpenXRDefs::InteractionProfile *profile = OpenXRDefs::get_profile(p_interaction_profile_path);
- if (profile != nullptr) {
- return profile->get_io_path(p_io_path);
- }
-
- return nullptr;
-}
-
-PackedStringArray OpenXRDefs::get_interaction_profile_paths() {
- PackedStringArray arr;
-
- for (int i = 0; i < available_interaction_profile_count; i++) {
- arr.push_back(available_interaction_profiles[i].openxr_path);
- }
-
- return arr;
-}
diff --git a/modules/openxr/action_map/openxr_defs.h b/modules/openxr/action_map/openxr_defs.h
deleted file mode 100644
index 446e6eb9c6..0000000000
--- a/modules/openxr/action_map/openxr_defs.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*************************************************************************/
-/* openxr_defs.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_DEFS_H
-#define OPENXR_DEFS_H
-
-#include "openxr_action.h"
-
-///////////////////////////////////////////////////////////////////////////
-// Stores available interaction profiles
-//
-// OpenXR defines and hardcodes all the supported input devices and their
-// paths as part of the OpenXR spec. When support for new devices is
-// introduced this often starts life as extensions that need to be enabled
-// until they are adopted into the core. As there is no interface to
-// enumerate the possibly paths, and that any OpenXR runtime would likely
-// limit such enumeration to those input devices supported by that runtime
-// there is no other option than to hardcode this.
-//
-// Note on action type that automatic conversions between boolean and float
-// are supported but otherwise action types should match between action and
-// input/output paths.
-
-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
- };
-
- struct TopLevelPath {
- const char *display_name; // User friendly display name (i.e. Left controller)
- const char *openxr_path; // Path in OpenXR (i.e. /user/hand/left)
- };
-
- struct IOPath {
- const char *display_name; // User friendly display name (i.e. Grip pose (left controller))
- const TopLevelPath *top_level_path; // Top level path identifying the usage of the device in relation to this input/output
- const char *openxr_path; // Path in OpenXR (i.e. /user/hand/left/input/grip/pose)
- const OpenXRAction::ActionType action_type; // Type of input/output
- };
-
- struct InteractionProfile {
- const char *display_name; // User friendly display name (i.e. Simple controller)
- const char *openxr_path; // Path in OpenXR (i.e. /interaction_profiles/khr/simple_controller)
- const IOPath *io_paths; // Inputs and outputs for this device
- const int io_path_count; // Number of inputs and outputs for this device
-
- const IOPath *get_io_path(const String p_io_path) const;
- };
-
-private:
- static TopLevelPath available_top_level_paths[OPENXR_TOP_LEVEL_PATH_MAX];
- static IOPath simple_io_paths[];
- static IOPath vive_io_paths[];
- static IOPath motion_io_paths[];
- static IOPath hpmr_io_paths[];
- static IOPath touch_io_paths[];
- static IOPath index_io_paths[];
- static IOPath odyssey_io_paths[];
- 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;
-
-public:
- static const TopLevelPath *get_top_level_path(const String p_top_level_path);
- static const InteractionProfile *get_profile(const String p_interaction_profile_path);
- static const IOPath *get_io_path(const String p_interaction_profile_path, const String p_io_path);
-
- static PackedStringArray get_interaction_profile_paths();
-};
-
-#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 99d7a17acf..07490f5841 100644
--- a/modules/openxr/action_map/openxr_interaction_profile.cpp
+++ b/modules/openxr/action_map/openxr_interaction_profile.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_interaction_profile.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interaction_profile.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_interaction_profile.h"
@@ -38,7 +38,7 @@ void OpenXRIPBinding::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_path_count"), &OpenXRIPBinding::get_path_count);
ClassDB::bind_method(D_METHOD("set_paths", "paths"), &OpenXRIPBinding::set_paths);
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");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths"), "set_paths", "get_paths");
ClassDB::bind_method(D_METHOD("has_path", "path"), &OpenXRIPBinding::has_path);
ClassDB::bind_method(D_METHOD("add_path", "path"), &OpenXRIPBinding::add_path);
@@ -58,6 +58,7 @@ Ref<OpenXRIPBinding> OpenXRIPBinding::new_binding(const Ref<OpenXRAction> p_acti
void OpenXRIPBinding::set_action(const Ref<OpenXRAction> p_action) {
action = p_action;
+ emit_changed();
}
Ref<OpenXRAction> OpenXRIPBinding::get_action() const {
@@ -70,6 +71,7 @@ int OpenXRIPBinding::get_path_count() const {
void OpenXRIPBinding::set_paths(const PackedStringArray p_paths) {
paths = p_paths;
+ emit_changed();
}
PackedStringArray OpenXRIPBinding::get_paths() const {
@@ -78,6 +80,7 @@ PackedStringArray OpenXRIPBinding::get_paths() const {
void OpenXRIPBinding::parse_paths(const String p_paths) {
paths = p_paths.split(",", false);
+ emit_changed();
}
bool OpenXRIPBinding::has_path(const String p_path) const {
@@ -87,12 +90,14 @@ bool OpenXRIPBinding::has_path(const String p_path) const {
void OpenXRIPBinding::add_path(const String p_path) {
if (!paths.has(p_path)) {
paths.push_back(p_path);
+ emit_changed();
}
}
void OpenXRIPBinding::remove_path(const String p_path) {
if (paths.has(p_path)) {
paths.erase(p_path);
+ emit_changed();
}
}
@@ -122,6 +127,7 @@ Ref<OpenXRInteractionProfile> OpenXRInteractionProfile::new_profile(const char *
void OpenXRInteractionProfile::set_interaction_profile_path(const String p_input_profile_path) {
interaction_profile_path = p_input_profile_path;
+ emit_changed();
}
String OpenXRInteractionProfile::get_interaction_profile_path() const {
@@ -139,9 +145,10 @@ Ref<OpenXRIPBinding> OpenXRInteractionProfile::get_binding(int p_index) const {
}
void OpenXRInteractionProfile::set_bindings(Array p_bindings) {
- bindings = p_bindings;
-
// TODO add check here that our bindings don't contain duplicate actions
+
+ bindings = p_bindings;
+ emit_changed();
}
Array OpenXRInteractionProfile::get_bindings() const {
@@ -166,6 +173,7 @@ void OpenXRInteractionProfile::add_binding(Ref<OpenXRIPBinding> p_binding) {
ERR_FAIL_COND_MSG(get_binding_for_action(p_binding->get_action()).is_valid(), "There is already a binding for this action in this interaction profile");
bindings.push_back(p_binding);
+ emit_changed();
}
}
@@ -173,6 +181,7 @@ void OpenXRInteractionProfile::remove_binding(Ref<OpenXRIPBinding> p_binding) {
int idx = bindings.find(p_binding);
if (idx != -1) {
bindings.remove_at(idx);
+ emit_changed();
}
}
@@ -192,6 +201,17 @@ void OpenXRInteractionProfile::remove_binding_for_action(const Ref<OpenXRAction>
}
}
+bool OpenXRInteractionProfile::has_binding_for_action(const Ref<OpenXRAction> p_action) {
+ for (int i = bindings.size() - 1; i >= 0; i--) {
+ Ref<OpenXRIPBinding> binding = bindings[i];
+ if (binding->get_action() == p_action) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
OpenXRInteractionProfile::~OpenXRInteractionProfile() {
bindings.clear();
}
diff --git a/modules/openxr/action_map/openxr_interaction_profile.h b/modules/openxr/action_map/openxr_interaction_profile.h
index c77fd490bb..d7253af316 100644
--- a/modules/openxr/action_map/openxr_interaction_profile.h
+++ b/modules/openxr/action_map/openxr_interaction_profile.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_interaction_profile.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interaction_profile.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_INTERACTION_PROFILE_H
#define OPENXR_INTERACTION_PROFILE_H
@@ -34,7 +34,7 @@
#include "core/io/resource.h"
#include "openxr_action.h"
-#include "openxr_defs.h"
+#include "openxr_interaction_profile_meta_data.h"
class OpenXRIPBinding : public Resource {
GDCLASS(OpenXRIPBinding, Resource);
@@ -94,6 +94,7 @@ public:
void add_new_binding(const Ref<OpenXRAction> p_action, const char *p_paths); // Create a new binding for this profile
void remove_binding_for_action(const Ref<OpenXRAction> p_action); // Remove all bindings for this action
+ bool has_binding_for_action(const Ref<OpenXRAction> p_action); // Returns true if we have a binding for this action
~OpenXRInteractionProfile();
};
diff --git a/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp b/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp
new file mode 100644
index 0000000000..1118b53d65
--- /dev/null
+++ b/modules/openxr/action_map/openxr_interaction_profile_meta_data.cpp
@@ -0,0 +1,397 @@
+/**************************************************************************/
+/* openxr_interaction_profile_meta_data.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_interaction_profile_meta_data.h"
+#include "../openxr_api.h"
+
+OpenXRInteractionProfileMetaData *OpenXRInteractionProfileMetaData::singleton = nullptr;
+
+OpenXRInteractionProfileMetaData::OpenXRInteractionProfileMetaData() {
+ singleton = this;
+
+ _register_core_metadata();
+ OpenXRAPI::register_extension_metadata();
+}
+
+OpenXRInteractionProfileMetaData::~OpenXRInteractionProfileMetaData() {
+ singleton = nullptr;
+}
+
+void OpenXRInteractionProfileMetaData::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("register_top_level_path", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetaData::register_top_level_path);
+ ClassDB::bind_method(D_METHOD("register_interaction_profile", "display_name", "openxr_path", "openxr_extension_name"), &OpenXRInteractionProfileMetaData::register_interaction_profile);
+ ClassDB::bind_method(D_METHOD("register_io_path", "interaction_profile", "display_name", "toplevel_path", "openxr_path", "openxr_extension_name", "action_type"), &OpenXRInteractionProfileMetaData::register_io_path);
+}
+
+void OpenXRInteractionProfileMetaData::register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) {
+ ERR_FAIL_COND_MSG(has_top_level_path(p_openxr_path), p_openxr_path + " had already been registered");
+
+ TopLevelPath new_toplevel_path = {
+ p_display_name,
+ p_openxr_path,
+ p_openxr_extension_name
+ };
+
+ top_level_paths.push_back(new_toplevel_path);
+}
+
+void OpenXRInteractionProfileMetaData::register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name) {
+ ERR_FAIL_COND_MSG(has_interaction_profile(p_openxr_path), p_openxr_path + " has already been registered");
+
+ InteractionProfile new_profile;
+ new_profile.display_name = p_display_name;
+ new_profile.openxr_path = p_openxr_path;
+ new_profile.openxr_extension_name = p_openxr_extension_name;
+
+ interaction_profiles.push_back(new_profile);
+}
+
+void OpenXRInteractionProfileMetaData::register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type) {
+ ERR_FAIL_COND_MSG(!has_interaction_profile(p_interaction_profile), "Unknown interaction profile " + p_interaction_profile);
+ ERR_FAIL_COND_MSG(!has_top_level_path(p_toplevel_path), "Unknown top level path " + p_toplevel_path);
+
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ if (interaction_profiles[i].openxr_path == p_interaction_profile) {
+ ERR_FAIL_COND_MSG(interaction_profiles[i].has_io_path(p_openxr_path), p_interaction_profile + " already has io path " + p_openxr_path + " registered!");
+
+ IOPath new_io_path = {
+ p_display_name,
+ p_toplevel_path,
+ p_openxr_path,
+ p_openxr_extension_name,
+ p_action_type
+ };
+
+ interaction_profiles.ptrw()[i].io_paths.push_back(new_io_path);
+ }
+ }
+}
+
+bool OpenXRInteractionProfileMetaData::has_top_level_path(const String p_openxr_path) const {
+ for (int i = 0; i < top_level_paths.size(); i++) {
+ if (top_level_paths[i].openxr_path == p_openxr_path) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+String OpenXRInteractionProfileMetaData::get_top_level_name(const String p_openxr_path) const {
+ for (int i = 0; i < top_level_paths.size(); i++) {
+ if (top_level_paths[i].openxr_path == p_openxr_path) {
+ return top_level_paths[i].display_name;
+ }
+ }
+
+ return String();
+}
+
+String OpenXRInteractionProfileMetaData::get_top_level_extension(const String p_openxr_path) const {
+ for (int i = 0; i < top_level_paths.size(); i++) {
+ if (top_level_paths[i].openxr_path == p_openxr_path) {
+ return top_level_paths[i].openxr_extension_name;
+ }
+ }
+
+ return XR_PATH_UNSUPPORTED_NAME;
+}
+
+bool OpenXRInteractionProfileMetaData::has_interaction_profile(const String p_openxr_path) const {
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ if (interaction_profiles[i].openxr_path == p_openxr_path) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+String OpenXRInteractionProfileMetaData::get_interaction_profile_extension(const String p_openxr_path) const {
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ if (interaction_profiles[i].openxr_path == p_openxr_path) {
+ return interaction_profiles[i].openxr_extension_name;
+ }
+ }
+
+ return XR_PATH_UNSUPPORTED_NAME;
+}
+
+const OpenXRInteractionProfileMetaData::InteractionProfile *OpenXRInteractionProfileMetaData::get_profile(const String p_openxr_path) const {
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ if (interaction_profiles[i].openxr_path == p_openxr_path) {
+ return &interaction_profiles[i];
+ }
+ }
+
+ return nullptr;
+}
+
+bool OpenXRInteractionProfileMetaData::InteractionProfile::has_io_path(const String p_io_path) const {
+ for (int i = 0; i < io_paths.size(); i++) {
+ if (io_paths[i].openxr_path == p_io_path) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData::InteractionProfile::get_io_path(const String p_io_path) const {
+ for (int i = 0; i < io_paths.size(); i++) {
+ if (io_paths[i].openxr_path == p_io_path) {
+ return &io_paths[i];
+ }
+ }
+
+ return nullptr;
+}
+
+const OpenXRInteractionProfileMetaData::IOPath *OpenXRInteractionProfileMetaData::get_io_path(const String p_interaction_profile, const String p_io_path) const {
+ const OpenXRInteractionProfileMetaData::InteractionProfile *profile = get_profile(p_interaction_profile);
+ if (profile != nullptr) {
+ return profile->get_io_path(p_io_path);
+ }
+
+ return nullptr;
+}
+
+PackedStringArray OpenXRInteractionProfileMetaData::get_interaction_profile_paths() const {
+ PackedStringArray arr;
+
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ arr.push_back(interaction_profiles[i].openxr_path);
+ }
+
+ return arr;
+}
+
+void OpenXRInteractionProfileMetaData::_register_core_metadata() {
+ // Note, currently we add definitions that belong in extensions.
+ // Extensions are registered when our OpenXRAPI is instantiated
+ // however this does not happen in the editor.
+ // We are changing this in another PR, once that is accepted we
+ // can make the changes to move code into extensions where needed.
+
+ // Note that we'll make an exception for XR_EXT_palm_pose, which is used everywhere
+
+ // Our core toplevel paths
+ register_top_level_path("Left hand controller", "/user/hand/left", "");
+ register_top_level_path("Right hand controller", "/user/hand/right", "");
+ register_top_level_path("Head", "/user/head", "");
+ register_top_level_path("Gamepad", "/user/gamepad", "");
+ register_top_level_path("Treadmill", "/user/treadmill", "");
+
+ // TODO move this into OpenXRHTCViveTrackerExtension once this is supported.
+ // register_top_level_path("Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Left foot tracker", "/user/vive_tracker_htcx/role/left_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Right foot tracker", "/user/vive_tracker_htcx/role/right_foot", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Left knee tracker", "/user/vive_tracker_htcx/role/left_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Right knee tracker", "/user/vive_tracker_htcx/role/right_knee", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Waist tracker", "/user/vive_tracker_htcx/role/waist", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Chest tracker", "/user/vive_tracker_htcx/role/chest", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Camera tracker", "/user/vive_tracker_htcx/role/camera", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ register_top_level_path("Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+
+ // Fallback Khronos simple controller
+ register_interaction_profile("Simple controller", "/interaction_profiles/khr/simple_controller", "");
+ register_io_path("/interaction_profiles/khr/simple_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ register_io_path("/interaction_profiles/khr/simple_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Menu click", "/user/hand/right", "/user/hand/right/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Select click", "/user/hand/left", "/user/hand/left/input/select/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Select click", "/user/hand/right", "/user/hand/right/input/select/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/khr/simple_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ register_io_path("/interaction_profiles/khr/simple_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // Original HTC Vive wands
+ register_interaction_profile("HTC Vive wand", "/interaction_profiles/htc/vive_controller", "");
+ register_io_path("/interaction_profiles/htc/vive_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ register_io_path("/interaction_profiles/htc/vive_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Menu click", "/user/hand/right", "/user/hand/right/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/htc/vive_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Squeeze click", "/user/hand/right", "/user/hand/right/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad", "/user/hand/left", "/user/hand/left/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad click", "/user/hand/left", "/user/hand/left/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad touch", "/user/hand/left", "/user/hand/left/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad", "/user/hand/right", "/user/hand/right/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad click", "/user/hand/right", "/user/hand/right/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Trackpad touch", "/user/hand/right", "/user/hand/right/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/htc/vive_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ register_io_path("/interaction_profiles/htc/vive_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // Microsoft motion controller (original WMR controllers)
+ register_interaction_profile("MS Motion controller", "/interaction_profiles/microsoft/motion_controller", "");
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Menu click", "/user/hand/right", "/user/hand/right/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Squeeze click", "/user/hand/right", "/user/hand/right/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad", "/user/hand/left", "/user/hand/left/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad click", "/user/hand/left", "/user/hand/left/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad touch", "/user/hand/left", "/user/hand/left/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad", "/user/hand/right", "/user/hand/right/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad click", "/user/hand/right", "/user/hand/right/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Trackpad touch", "/user/hand/right", "/user/hand/right/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ register_io_path("/interaction_profiles/microsoft/motion_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // Meta touch controller (original touch controllers, Quest 1 and Quest 2 controllers)
+ register_interaction_profile("Touch controller", "/interaction_profiles/oculus/touch_controller", "");
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ register_io_path("/interaction_profiles/oculus/touch_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // Valve Index controller
+ register_interaction_profile("Index controller", "/interaction_profiles/valve/index_controller", "");
+ register_io_path("/interaction_profiles/valve/index_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/valve/index_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/valve/index_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/valve/index_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/valve/index_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ register_io_path("/interaction_profiles/valve/index_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "A click", "/user/hand/left", "/user/hand/left/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "A touch", "/user/hand/left", "/user/hand/left/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "B click", "/user/hand/left", "/user/hand/left/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "B touch", "/user/hand/left", "/user/hand/left/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad", "/user/hand/left", "/user/hand/left/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad force", "/user/hand/left", "/user/hand/left/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad touch", "/user/hand/left", "/user/hand/left/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad", "/user/hand/right", "/user/hand/right/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad force", "/user/hand/right", "/user/hand/right/input/trackpad/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ register_io_path("/interaction_profiles/valve/index_controller", "Trackpad touch", "/user/hand/right", "/user/hand/right/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ register_io_path("/interaction_profiles/valve/index_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ register_io_path("/interaction_profiles/valve/index_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/action_map/openxr_interaction_profile_meta_data.h b/modules/openxr/action_map/openxr_interaction_profile_meta_data.h
new file mode 100644
index 0000000000..16ee88bbf9
--- /dev/null
+++ b/modules/openxr/action_map/openxr_interaction_profile_meta_data.h
@@ -0,0 +1,118 @@
+/**************************************************************************/
+/* openxr_interaction_profile_meta_data.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_INTERACTION_PROFILE_META_DATA_H
+#define OPENXR_INTERACTION_PROFILE_META_DATA_H
+
+#include "openxr_action.h"
+
+///////////////////////////////////////////////////////////////////////////
+// Stores available interaction profile meta data
+//
+// OpenXR defines and hardcodes all the supported input devices and their
+// paths as part of the OpenXR spec. When support for new devices is
+// introduced this often starts life as an extension that needs to be enabled
+// until it's adopted into the core. As there is no interface to
+// enumerate the possibly paths, and that any OpenXR runtime would likely
+// limit such enumeration to those input devices supported by that runtime
+// there is no other option than to hardcode this.
+//
+// Note that we need to include paths of our extensions in our action map
+// regardless of whether the developers machine supports the extension or
+// not. Unsupported paths are filtered out when the action map is submitted
+// to the OpenXR runtime.
+//
+// Note on action type that automatic conversions between boolean and float
+// are supported but otherwise action types should match between action and
+// input/output paths.
+
+#include "core/object/object.h"
+
+#define XR_PATH_UNSUPPORTED_NAME "unsupported"
+
+class OpenXRInteractionProfileMetaData : public Object {
+public:
+ struct TopLevelPath {
+ String display_name; // User friendly display name (i.e. Left controller)
+ String openxr_path; // Path in OpenXR (i.e. /user/hand/left)
+ String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_HTCX_vive_tracker_interaction)
+ };
+
+ struct IOPath {
+ String display_name; // User friendly display name (i.e. Grip pose (left controller))
+ String top_level_path; // Top level path identifying the usage of the device in relation to this input/output
+ String openxr_path; // Path in OpenXR (i.e. /user/hand/left/input/grip/pose)
+ String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_EXT_palm_pose)
+ OpenXRAction::ActionType action_type; // Type of input/output
+ };
+
+ struct InteractionProfile {
+ String display_name; // User friendly display name (i.e. Simple controller)
+ String openxr_path; // Path in OpenXR (i.e. /interaction_profiles/khr/simple_controller)
+ String openxr_extension_name; // If set, only available if extension is enabled (i.e. XR_HTCX_vive_tracker_interaction)
+ Vector<IOPath> io_paths; // Inputs and outputs for this device
+
+ bool has_io_path(const String p_io_path) const;
+ const IOPath *get_io_path(const String p_io_path) const;
+ };
+
+private:
+ static OpenXRInteractionProfileMetaData *singleton;
+
+ Vector<TopLevelPath> top_level_paths;
+ Vector<InteractionProfile> interaction_profiles;
+
+ void _register_core_metadata();
+
+protected:
+ static void _bind_methods();
+
+public:
+ static OpenXRInteractionProfileMetaData *get_singleton() { return singleton; }
+
+ OpenXRInteractionProfileMetaData();
+ ~OpenXRInteractionProfileMetaData();
+
+ void register_top_level_path(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name);
+ bool has_top_level_path(const String p_openxr_path) const;
+ String get_top_level_name(const String p_openxr_path) const;
+ String get_top_level_extension(const String p_openxr_path) const;
+
+ void register_interaction_profile(const String &p_display_name, const String &p_openxr_path, const String &p_openxr_extension_name);
+ bool has_interaction_profile(const String p_openxr_path) const;
+ String get_interaction_profile_extension(const String p_openxr_path) const;
+ const InteractionProfile *get_profile(const String p_openxr_path) const;
+ PackedStringArray get_interaction_profile_paths() const;
+
+ void register_io_path(const String &p_interaction_profile, const String &p_display_name, const String &p_toplevel_path, const String &p_openxr_path, const String &p_openxr_extension_name, OpenXRAction::ActionType p_action_type);
+ const IOPath *get_io_path(const String p_interaction_profile, const String p_io_path) const;
+};
+
+#endif // OPENXR_INTERACTION_PROFILE_META_DATA_H
diff --git a/modules/openxr/config.py b/modules/openxr/config.py
index f91cb1359f..e503f12739 100644
--- a/modules/openxr/config.py
+++ b/modules/openxr/config.py
@@ -1,8 +1,6 @@
def can_build(env, platform):
- if (
- platform == "linuxbsd" or platform == "windows"
- ): # or platform == "android" -- temporarily disabled android support
- return env["openxr"]
+ if platform in ("linuxbsd", "windows", "android"):
+ return env["openxr"] and not env["disable_3d"]
else:
# not supported on these platforms
return False
@@ -20,6 +18,7 @@ def get_doc_classes():
"OpenXRActionMap",
"OpenXRInteractionProfile",
"OpenXRIPBinding",
+ "OpenXRHand",
]
diff --git a/modules/openxr/doc_classes/OpenXRAction.xml b/modules/openxr/doc_classes/OpenXRAction.xml
index 6ff8c1ad26..a3a45ebb4c 100644
--- a/modules/openxr/doc_classes/OpenXRAction.xml
+++ b/modules/openxr/doc_classes/OpenXRAction.xml
@@ -5,8 +5,8 @@
</brief_description>
<description>
This resource defines an OpenXR action. Actions can be used both for inputs (buttons/joystick/trigger/etc) and outputs (haptics).
- OpenXR performs automatic conversion between action type and input type whenever possible. An analogue trigger bound to a boolean action will thus return [code]false[/core] if the trigger is depressed and [code]true[/code] if pressed fully.
- Actions are not directly bound to specific devices, instead OpenXR recognises a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths.
+ OpenXR performs automatic conversion between action type and input type whenever possible. An analogue trigger bound to a boolean action will thus return [code]false[/code] if the trigger is depressed and [code]true[/code] if pressed fully.
+ Actions are not directly bound to specific devices, instead OpenXR recognizes a limited number of top level paths that identify devices by usage. We can restrict which devices an action can be bound to by these top level paths. For instance an action that should only be used for hand held controllers can have the top level paths "/user/hand/left" and "/user/hand/right" associated with them. See the [url=https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#semantic-path-reserved]reserved path section in the OpenXR specification[/url] for more info on the top level paths.
Note that the name of the resource is used to register the action with.
</description>
<tutorials>
@@ -16,7 +16,7 @@
The type of action.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
- The localised description of this action.
+ The localized description of this action.
</member>
<member name="toplevel_paths" type="PackedStringArray" setter="set_toplevel_paths" getter="get_toplevel_paths" default="PackedStringArray()">
A collections of toplevel paths to which this action can be bound.
diff --git a/modules/openxr/doc_classes/OpenXRActionMap.xml b/modules/openxr/doc_classes/OpenXRActionMap.xml
index a29d10be41..8a2f666e3f 100644
--- a/modules/openxr/doc_classes/OpenXRActionMap.xml
+++ b/modules/openxr/doc_classes/OpenXRActionMap.xml
@@ -13,14 +13,14 @@
<methods>
<method name="add_action_set">
<return type="void" />
- <argument index="0" name="action_set" type="OpenXRActionSet" />
+ <param index="0" name="action_set" type="OpenXRActionSet" />
<description>
Add an action set.
</description>
</method>
<method name="add_interaction_profile">
<return type="void" />
- <argument index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
+ <param index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Add an interaction profile.
</description>
@@ -33,21 +33,21 @@
</method>
<method name="find_action_set" qualifiers="const">
<return type="OpenXRActionSet" />
- <argument index="0" name="name" type="String" />
+ <param index="0" name="name" type="String" />
<description>
Retrieve an action set by name.
</description>
</method>
<method name="find_interaction_profile" qualifiers="const">
<return type="OpenXRInteractionProfile" />
- <argument index="0" name="name" type="String" />
+ <param index="0" name="name" type="String" />
<description>
Find an interaction profile by its name (path).
</description>
</method>
<method name="get_action_set" qualifiers="const">
<return type="OpenXRActionSet" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
Retrieve the action set at this index.
</description>
@@ -60,7 +60,7 @@
</method>
<method name="get_interaction_profile" qualifiers="const">
<return type="OpenXRInteractionProfile" />
- <argument index="0" name="idx" type="int" />
+ <param index="0" name="idx" type="int" />
<description>
Get the interaction profile at this index.
</description>
@@ -73,14 +73,14 @@
</method>
<method name="remove_action_set">
<return type="void" />
- <argument index="0" name="action_set" type="OpenXRActionSet" />
+ <param index="0" name="action_set" type="OpenXRActionSet" />
<description>
Remove an action set.
</description>
</method>
<method name="remove_interaction_profile">
<return type="void" />
- <argument index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
+ <param index="0" name="interaction_profile" type="OpenXRInteractionProfile" />
<description>
Remove an interaction profile.
</description>
diff --git a/modules/openxr/doc_classes/OpenXRActionSet.xml b/modules/openxr/doc_classes/OpenXRActionSet.xml
index 55cc0aaad4..39e518750a 100644
--- a/modules/openxr/doc_classes/OpenXRActionSet.xml
+++ b/modules/openxr/doc_classes/OpenXRActionSet.xml
@@ -12,7 +12,7 @@
<methods>
<method name="add_action">
<return type="void" />
- <argument index="0" name="action" type="OpenXRAction" />
+ <param index="0" name="action" type="OpenXRAction" />
<description>
Add an action to this action set.
</description>
@@ -25,7 +25,7 @@
</method>
<method name="remove_action">
<return type="void" />
- <argument index="0" name="action" type="OpenXRAction" />
+ <param index="0" name="action" type="OpenXRAction" />
<description>
Remove an action from this action set.
</description>
@@ -36,7 +36,7 @@
Collection of actions for this action set.
</member>
<member name="localized_name" type="String" setter="set_localized_name" getter="get_localized_name" default="&quot;&quot;">
- The localised name of this action set.
+ The localized name of this action set.
</member>
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="0">
The priority for this action set.
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
new file mode 100644
index 0000000000..5d5f8a6126
--- /dev/null
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="OpenXRHand" inherits="Node3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Node supporting finger tracking in OpenXR.
+ </brief_description>
+ <description>
+ This node enables OpenXR's hand tracking functionality. The node should be a child node of an [XROrigin3D] node, tracking will update its position to where the player's actual hand is positioned. This node also updates the skeleton of a properly skinned hand model. The hand mesh should be a child node of this node.
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
+ Specifies whether this node tracks the left or right hand of the player.
+ </member>
+ <member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
+ Set a [Skeleton3D] node for which the pose positions will be updated.
+ </member>
+ <member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
+ Set the motion range (if supported) limiting the hand motion.
+ </member>
+ </members>
+ <constants>
+ <constant name="HAND_LEFT" value="0" enum="Hands">
+ Tracking the player's left hand.
+ </constant>
+ <constant name="HAND_RIGHT" value="1" enum="Hands">
+ Tracking the player's right hand.
+ </constant>
+ <constant name="HAND_MAX" value="2" enum="Hands">
+ Maximum supported hands.
+ </constant>
+ <constant name="MOTION_RANGE_UNOBSTRUCTED" value="0" enum="MotionRange">
+ When player grips, hand skeleton will form a full fist.
+ </constant>
+ <constant name="MOTION_RANGE_CONFORM_TO_CONTROLLER" value="1" enum="MotionRange">
+ When player grips, hand skeleton conforms to the controller the player is holding.
+ </constant>
+ <constant name="MOTION_RANGE_MAX" value="2" enum="MotionRange">
+ Maximum supported motion ranges.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/openxr/doc_classes/OpenXRIPBinding.xml b/modules/openxr/doc_classes/OpenXRIPBinding.xml
index f96637f2f5..00806bda06 100644
--- a/modules/openxr/doc_classes/OpenXRIPBinding.xml
+++ b/modules/openxr/doc_classes/OpenXRIPBinding.xml
@@ -11,7 +11,7 @@
<methods>
<method name="add_path">
<return type="void" />
- <argument index="0" name="path" type="String" />
+ <param index="0" name="path" type="String" />
<description>
Add an input/output path to this binding.
</description>
@@ -24,14 +24,14 @@
</method>
<method name="has_path" qualifiers="const">
<return type="bool" />
- <argument index="0" name="path" type="String" />
+ <param index="0" name="path" type="String" />
<description>
Returns [code]true[/code] if this input/output path is part of this binding.
</description>
</method>
<method name="remove_path">
<return type="void" />
- <argument index="0" name="path" type="String" />
+ <param index="0" name="path" type="String" />
<description>
Removes this input/output path from this binding.
</description>
diff --git a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml
index 71c0db44ed..950bde031f 100644
--- a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml
+++ b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml
@@ -12,7 +12,7 @@
<methods>
<method name="get_binding" qualifiers="const">
<return type="OpenXRIPBinding" />
- <argument index="0" name="index" type="int" />
+ <param index="0" name="index" type="int" />
<description>
Retrieve the binding at this index.
</description>
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 25bf496de9..7251a4a9bd 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -5,11 +5,24 @@
</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 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.
+ Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialized 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="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link>
</tutorials>
+ <methods>
+ <method name="get_available_display_refresh_rates" qualifiers="const">
+ <return type="Array" />
+ <description>
+ Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0">
+ The display refresh rate for the current HMD. Only functional if this feature is supported by the OpenXR runtime and after the interface has been initialized.
+ </member>
+ </members>
<signals>
<signal name="pose_recentered">
<description>
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
index 41c6465f43..586b0b0697 100644
--- a/modules/openxr/editor/openxr_action_editor.cpp
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -1,36 +1,40 @@
-/*************************************************************************/
-/* openxr_action_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action_editor.h"
void OpenXRActionEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionEditor::_do_set_name);
+ ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionEditor::_do_set_localized_name);
+ ClassDB::bind_method(D_METHOD("_do_set_action_type", "type"), &OpenXRActionEditor::_do_set_action_type);
+
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_editor")));
}
@@ -48,24 +52,71 @@ void OpenXRActionEditor::_notification(int p_what) {
}
void OpenXRActionEditor::_on_action_name_changed(const String p_new_text) {
- // TODO validate if entry is allowed
-
- // If our localized name matches our action name, set this too
- if (action->get_name() == action->get_localized_name()) {
- action->set_localized_name(p_new_text);
- action_localized_name->set_text(p_new_text);
+ if (action->get_name() != p_new_text) {
+ undo_redo->create_action(TTR("Rename Action"));
+ undo_redo->add_do_method(this, "_do_set_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_name", action->get_name());
+ undo_redo->commit_action(false);
+
+ // If our localized name matches our action name, set this too
+ if (action->get_name() == action->get_localized_name()) {
+ undo_redo->create_action(TTR("Rename Actions Localized name"));
+ undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
+ undo_redo->commit_action(false);
+
+ action->set_localized_name(p_new_text);
+ action_localized_name->set_text(p_new_text);
+ }
+ action->set_name(p_new_text);
+ action->set_edited(true);
}
+}
+
+void OpenXRActionEditor::_do_set_name(const String p_new_text) {
action->set_name(p_new_text);
+ action->set_edited(true);
+ action_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_text) {
+ if (action->get_localized_name() != p_new_text) {
+ undo_redo->create_action(TTR("Rename Actions Localized name"));
+ undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_localized_name", action->get_localized_name());
+ undo_redo->commit_action(false);
+
+ action->set_localized_name(p_new_text);
+ action->set_edited(true);
+ }
+}
+
+void OpenXRActionEditor::_do_set_localized_name(const String p_new_text) {
action->set_localized_name(p_new_text);
+ action->set_edited(true);
+ action_localized_name->set_text(p_new_text);
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX);
- action->set_action_type(OpenXRAction::ActionType(p_idx));
+ OpenXRAction::ActionType action_type = OpenXRAction::ActionType(p_idx);
+
+ if (action->get_action_type() != action_type) {
+ undo_redo->create_action(TTR("Change Action Type"));
+ undo_redo->add_do_method(this, "_do_set_action_type", action_type);
+ undo_redo->add_undo_method(this, "_do_set_action_type", action->get_action_type());
+ undo_redo->commit_action(false);
+
+ action->set_action_type(action_type);
+ action->set_edited(true);
+ }
+}
+
+void OpenXRActionEditor::_do_set_action_type(OpenXRAction::ActionType p_action_type) {
+ action->set_action_type(p_action_type);
+ action->set_edited(true);
+ action_type_button->select(int(action->get_action_type()));
}
void OpenXRActionEditor::_on_remove_action() {
@@ -73,6 +124,7 @@ void OpenXRActionEditor::_on_remove_action() {
}
OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
+ undo_redo = EditorUndoRedoManager::get_singleton();
action = p_action;
set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -90,21 +142,21 @@ OpenXRActionEditor::OpenXRActionEditor(Ref<OpenXRAction> p_action) {
action_localized_name->connect("text_changed", callable_mp(this, &OpenXRActionEditor::_on_action_localized_name_changed));
add_child(action_localized_name);
- action_type = memnew(OptionButton);
- action_type->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
- action_type->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
- action_type->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
- action_type->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
- action_type->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
- action_type->select(int(action->get_action_type()));
- action_type->set_custom_minimum_size(Size2(100.0, 0.0));
- action_type->connect("item_selected", callable_mp(this, &OpenXRActionEditor::_on_item_selected));
- add_child(action_type);
+ action_type_button = memnew(OptionButton);
+ action_type_button->add_item("Bool", OpenXRAction::OPENXR_ACTION_BOOL);
+ action_type_button->add_item("Float", OpenXRAction::OPENXR_ACTION_FLOAT);
+ action_type_button->add_item("Vector2", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ action_type_button->add_item("Pose", OpenXRAction::OPENXR_ACTION_POSE);
+ action_type_button->add_item("Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ action_type_button->select(int(action->get_action_type()));
+ action_type_button->set_custom_minimum_size(Size2(100.0, 0.0));
+ action_type_button->connect("item_selected", callable_mp(this, &OpenXRActionEditor::_on_item_selected));
+ add_child(action_type_button);
// maybe add dropdown to edit our toplevel paths, or do we deduce them from our suggested bindings?
rem_action = memnew(Button);
- rem_action->set_tooltip(TTR("Remove action"));
+ rem_action->set_tooltip_text(TTR("Remove action"));
rem_action->connect("pressed", callable_mp(this, &OpenXRActionEditor::_on_remove_action));
rem_action->set_flat(true);
add_child(rem_action);
diff --git a/modules/openxr/editor/openxr_action_editor.h b/modules/openxr/editor/openxr_action_editor.h
index 6cf098cf08..765b3ef378 100644
--- a/modules/openxr/editor/openxr_action_editor.h
+++ b/modules/openxr/editor/openxr_action_editor.h
@@ -1,37 +1,38 @@
-/*************************************************************************/
-/* openxr_action_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_EDITOR_H
#define OPENXR_ACTION_EDITOR_H
#include "../action_map/openxr_action.h"
+#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/line_edit.h"
@@ -42,11 +43,12 @@ class OpenXRActionEditor : public HBoxContainer {
GDCLASS(OpenXRActionEditor, HBoxContainer);
private:
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRAction> action;
LineEdit *action_name = nullptr;
LineEdit *action_localized_name = nullptr;
- OptionButton *action_type = nullptr;
+ OptionButton *action_type_button = nullptr;
Button *rem_action = nullptr;
void _theme_changed();
@@ -59,6 +61,11 @@ protected:
static void _bind_methods();
void _notification(int p_what);
+ // used for undo/redo
+ void _do_set_name(const String p_new_text);
+ void _do_set_localized_name(const String p_new_text);
+ void _do_set_action_type(OpenXRAction::ActionType p_action_type);
+
public:
Ref<OpenXRAction> get_action() { return action; };
OpenXRActionEditor(Ref<OpenXRAction> p_action);
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 0a2d0a3110..ad5a515a01 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_map_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_map_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action_map_editor.h"
@@ -40,14 +40,16 @@
void OpenXRActionMapEditor::_bind_methods() {
ClassDB::bind_method("_add_action_set_editor", &OpenXRActionMapEditor::_add_action_set_editor);
- ClassDB::bind_method("_update_action_sets", &OpenXRActionMapEditor::_update_action_sets);
-
ClassDB::bind_method("_add_interaction_profile_editor", &OpenXRActionMapEditor::_add_interaction_profile_editor);
- ClassDB::bind_method("_update_interaction_profiles", &OpenXRActionMapEditor::_update_interaction_profiles);
ClassDB::bind_method(D_METHOD("_add_action_set", "name"), &OpenXRActionMapEditor::_add_action_set);
ClassDB::bind_method(D_METHOD("_set_focus_on_action_set", "action_set"), &OpenXRActionMapEditor::_set_focus_on_action_set);
ClassDB::bind_method(D_METHOD("_remove_action_set", "name"), &OpenXRActionMapEditor::_remove_action_set);
+
+ ClassDB::bind_method(D_METHOD("_do_add_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_add_action_set_editor);
+ ClassDB::bind_method(D_METHOD("_do_remove_action_set_editor", "action_set_editor"), &OpenXRActionMapEditor::_do_remove_action_set_editor);
+ ClassDB::bind_method(D_METHOD("_do_add_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_add_interaction_profile_editor);
+ ClassDB::bind_method(D_METHOD("_do_remove_interaction_profile_editor", "interaction_profile_editor"), &OpenXRActionMapEditor::_do_remove_interaction_profile_editor);
}
void OpenXRActionMapEditor::_notification(int p_what) {
@@ -55,16 +57,16 @@ void OpenXRActionMapEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
for (int i = 0; i < tabs->get_child_count(); i++) {
- Control *tab = static_cast<Control *>(tabs->get_child(i));
+ Control *tab = Object::cast_to<Control>(tabs->get_child(i));
if (tab) {
- tab->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ tab->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
}
}
} break;
case NOTIFICATION_READY: {
- _update_action_sets();
- _update_interaction_profiles();
+ _create_action_sets();
+ _create_interaction_profiles();
} break;
}
}
@@ -75,18 +77,13 @@ OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set_editor(Ref<OpenXRA
OpenXRActionSetEditor *action_set_editor = memnew(OpenXRActionSetEditor(action_map, p_action_set));
action_set_editor->connect("remove", callable_mp(this, &OpenXRActionMapEditor::_on_remove_action_set));
action_set_editor->connect("action_removed", callable_mp(this, &OpenXRActionMapEditor::_on_action_removed));
+
actionsets_vb->add_child(action_set_editor);
return action_set_editor;
}
-void OpenXRActionMapEditor::_update_action_sets() {
- // out with the old...
- while (actionsets_vb->get_child_count() > 0) {
- memdelete(actionsets_vb->get_child(0));
- }
-
- // in with the new...
+void OpenXRActionMapEditor::_create_action_sets() {
if (action_map.is_valid()) {
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
@@ -113,25 +110,13 @@ OpenXRInteractionProfileEditorBase *OpenXRActionMapEditor::_add_interaction_prof
// now add it in..
ERR_FAIL_NULL_V(new_profile_editor, nullptr);
tabs->add_child(new_profile_editor);
- new_profile_editor->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ new_profile_editor->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
- interaction_profiles.push_back(new_profile_editor);
-
return new_profile_editor;
}
-void OpenXRActionMapEditor::_update_interaction_profiles() {
- // out with the old...
- while (interaction_profiles.size() > 0) {
- Node *interaction_profile = interaction_profiles[0];
- interaction_profiles.remove_at(0);
-
- tabs->remove_child(interaction_profile);
- interaction_profile->queue_delete();
- }
-
- // in with the new...
+void OpenXRActionMapEditor::_create_interaction_profiles() {
if (action_map.is_valid()) {
Array new_interaction_profiles = action_map->get_interaction_profiles();
for (int i = 0; i < new_interaction_profiles.size(); i++) {
@@ -150,9 +135,17 @@ OpenXRActionSetEditor *OpenXRActionMapEditor::_add_action_set(String p_name) {
new_action_set->set_name(p_name);
new_action_set->set_localized_name(p_name);
action_map->add_action_set(new_action_set);
+ action_map->set_edited(true);
// update our editor right away
- return _add_action_set_editor(new_action_set);
+ OpenXRActionSetEditor *action_set_editor = _add_action_set_editor(new_action_set);
+
+ undo_redo->create_action(TTR("Add action set"));
+ undo_redo->add_do_method(this, "_do_add_action_set_editor", action_set_editor);
+ undo_redo->add_undo_method(this, "_do_remove_action_set_editor", action_set_editor);
+ undo_redo->commit_action(false);
+
+ return action_set_editor;
}
void OpenXRActionMapEditor::_remove_action_set(String p_name) {
@@ -160,13 +153,12 @@ void OpenXRActionMapEditor::_remove_action_set(String p_name) {
Ref<OpenXRActionSet> action_set = action_map->find_action_set(p_name);
ERR_FAIL_COND(action_set.is_null());
- if (action_set->get_action_count() > 0) {
- // we should remove these and add to our redo/undo step before calling _remove_action_set
- WARN_PRINT("Action set still has associated actions before being removed!");
+ for (int i = 0; i < actionsets_vb->get_child_count(); i++) {
+ OpenXRActionSetEditor *action_set_editor = Object::cast_to<OpenXRActionSetEditor>(actionsets_vb->get_child(i));
+ if (action_set_editor && action_set_editor->get_action_set() == action_set) {
+ _on_remove_action_set(action_set_editor);
+ }
}
-
- // now we remove it
- action_map->remove_action_set(action_set);
}
void OpenXRActionMapEditor::_on_add_action_set() {
@@ -203,14 +195,24 @@ void OpenXRActionMapEditor::_on_remove_action_set(Object *p_action_set_editor) {
Ref<OpenXRActionSet> action_set = action_set_editor->get_action_set();
ERR_FAIL_COND(action_set.is_null());
- action_map->remove_action_set(action_set);
- actionsets_vb->remove_child(action_set_editor);
- action_set_editor->queue_delete();
+ action_set_editor->remove_all_actions();
+
+ undo_redo->create_action(TTR("Remove action set"));
+ undo_redo->add_do_method(this, "_do_remove_action_set_editor", action_set_editor);
+ undo_redo->add_undo_method(this, "_do_add_action_set_editor", action_set_editor);
+ undo_redo->commit_action(true);
+
+ action_map->set_edited(true);
}
-void OpenXRActionMapEditor::_on_action_removed() {
- // make sure our interaction profiles are updated
- _update_interaction_profiles();
+void OpenXRActionMapEditor::_on_action_removed(Ref<OpenXRAction> p_action) {
+ for (int i = 0; i < tabs->get_tab_count(); i++) {
+ // First tab won't be an interaction profile editor, but being thorough..
+ OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
+ if (interaction_profile_editor) {
+ interaction_profile_editor->remove_all_bindings_for_action(p_action);
+ }
+ }
}
void OpenXRActionMapEditor::_on_add_interaction_profile() {
@@ -232,20 +234,35 @@ void OpenXRActionMapEditor::_on_interaction_profile_selected(const String p_path
new_profile.instantiate();
new_profile->set_interaction_profile_path(p_path);
action_map->add_interaction_profile(new_profile);
+ action_map->set_edited(true);
- _add_interaction_profile_editor(new_profile);
+ OpenXRInteractionProfileEditorBase *interaction_profile_editor = _add_interaction_profile_editor(new_profile);
+
+ undo_redo->create_action(TTR("Add interaction profile"));
+ undo_redo->add_do_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
+ undo_redo->add_undo_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
+ undo_redo->commit_action(false);
tabs->set_current_tab(tabs->get_tab_count() - 1);
}
void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_new_if_missing) {
- action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
- if (action_map.is_null()) {
- if (p_create_new_if_missing) {
+ Error err = OK;
+ action_map = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err);
+ if (err != OK) {
+ if ((err == ERR_FILE_NOT_FOUND || err == ERR_CANT_OPEN) && p_create_new_if_missing) {
action_map.instantiate();
action_map->create_default_action_sets();
+
+ // Save it immediately
+ err = ResourceSaver::save(action_map, p_path);
+ if (err != OK) {
+ // show warning but continue
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
+ }
+
} else {
- EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an OpenXR action map."));
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Error loading %s: %s."), edited_path, error_names[err]));
edited_path = "";
header_label->set_text("");
@@ -254,55 +271,123 @@ void OpenXRActionMapEditor::_load_action_map(const String p_path, bool p_create_
}
edited_path = p_path;
- header_label->set_text(TTR("OpenXR Action map:") + " " + p_path.get_file());
+ header_label->set_text(TTR("OpenXR Action map:") + " " + edited_path.get_file());
}
void OpenXRActionMapEditor::_on_save_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));
+ EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file %s: %s"), edited_path, error_names[err]));
return;
}
- _update_action_sets();
- _update_interaction_profiles();
+ // TODO should clear undo/redo history
+
+ // out with the old
+ _clear_action_map();
+
+ _create_action_sets();
+ _create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_reset_to_default_layout() {
+ // TODO should clear undo/redo history
+
+ // out with the old
+ _clear_action_map();
+
// create a new one
action_map.unref();
action_map.instantiate();
action_map->create_default_action_sets();
+ action_map->set_edited(true);
- _update_action_sets();
- _update_interaction_profiles();
+ _create_action_sets();
+ _create_interaction_profiles();
}
void OpenXRActionMapEditor::_on_tabs_tab_changed(int p_tab) {
}
void OpenXRActionMapEditor::_on_tab_button_pressed(int p_tab) {
- OpenXRInteractionProfileEditorBase *profile_editor = static_cast<OpenXRInteractionProfileEditorBase *>(tabs->get_tab_control(p_tab));
- ERR_FAIL_NULL(profile_editor);
+ OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(p_tab));
+ ERR_FAIL_NULL(interaction_profile_editor);
+
+ undo_redo->create_action(TTR("Remove interaction profile"));
+ undo_redo->add_do_method(this, "_do_remove_interaction_profile_editor", interaction_profile_editor);
+ undo_redo->add_undo_method(this, "_do_add_interaction_profile_editor", interaction_profile_editor);
+ undo_redo->commit_action(true);
+
+ action_map->set_edited(true);
+}
+
+void OpenXRActionMapEditor::_do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
+ Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
+ ERR_FAIL_COND(action_set.is_null());
+
+ action_map->add_action_set(action_set);
+ actionsets_vb->add_child(p_action_set_editor);
+}
+
+void OpenXRActionMapEditor::_do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor) {
+ Ref<OpenXRActionSet> action_set = p_action_set_editor->get_action_set();
+ ERR_FAIL_COND(action_set.is_null());
+
+ actionsets_vb->remove_child(p_action_set_editor);
+ action_map->remove_action_set(action_set);
+}
+
+void OpenXRActionMapEditor::_do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
+ Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
+ ERR_FAIL_COND(interaction_profile.is_null());
+
+ action_map->add_interaction_profile(interaction_profile);
+ tabs->add_child(p_interaction_profile_editor);
+ tabs->set_tab_button_icon(tabs->get_tab_count() - 1, get_theme_icon(SNAME("close"), SNAME("TabBar")));
+
+ tabs->set_current_tab(tabs->get_tab_count() - 1);
+}
- Ref<OpenXRInteractionProfile> interaction_profile = profile_editor->get_interaction_profile();
+void OpenXRActionMapEditor::_do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor) {
+ Ref<OpenXRInteractionProfile> interaction_profile = p_interaction_profile_editor->get_interaction_profile();
ERR_FAIL_COND(interaction_profile.is_null());
+ tabs->remove_child(p_interaction_profile_editor);
action_map->remove_interaction_profile(interaction_profile);
- tabs->remove_child(profile_editor);
- profile_editor->queue_delete();
}
void OpenXRActionMapEditor::open_action_map(String p_path) {
EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+ // out with the old...
+ _clear_action_map();
+
+ // now load in our new action map
_load_action_map(p_path);
- _update_action_sets();
- _update_interaction_profiles();
+ _create_action_sets();
+ _create_interaction_profiles();
+}
+
+void OpenXRActionMapEditor::_clear_action_map() {
+ while (actionsets_vb->get_child_count() > 0) {
+ Node *child = actionsets_vb->get_child(0);
+ actionsets_vb->remove_child(child);
+ child->queue_free();
+ }
+
+ for (int i = tabs->get_tab_count() - 1; i >= 0; --i) {
+ // First tab won't be an interaction profile editor, but being thorough..
+ OpenXRInteractionProfileEditorBase *interaction_profile_editor = Object::cast_to<OpenXRInteractionProfileEditorBase>(tabs->get_tab_control(i));
+ if (interaction_profile_editor) {
+ tabs->remove_child(interaction_profile_editor);
+ interaction_profile_editor->queue_free();
+ }
+ }
}
OpenXRActionMapEditor::OpenXRActionMapEditor() {
+ undo_redo = EditorUndoRedoManager::get_singleton();
set_custom_minimum_size(Size2(0.0, 300.0));
top_hb = memnew(HBoxContainer);
@@ -316,13 +401,13 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() {
add_action_set = memnew(Button);
add_action_set->set_text(TTR("Add Action Set"));
- add_action_set->set_tooltip(TTR("Add an action set."));
+ add_action_set->set_tooltip_text(TTR("Add an action set."));
add_action_set->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_add_action_set));
top_hb->add_child(add_action_set);
add_interaction_profile = memnew(Button);
add_interaction_profile->set_text(TTR("Add profile"));
- add_interaction_profile->set_tooltip(TTR("Add an interaction profile."));
+ add_interaction_profile->set_tooltip_text(TTR("Add an interaction profile."));
add_interaction_profile->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_add_interaction_profile));
top_hb->add_child(add_interaction_profile);
@@ -331,13 +416,13 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() {
save_as = memnew(Button);
save_as->set_text(TTR("Save"));
- save_as->set_tooltip(TTR("Save this OpenXR action map."));
+ save_as->set_tooltip_text(TTR("Save this OpenXR action map."));
save_as->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_save_action_map));
top_hb->add_child(save_as);
_default = memnew(Button);
_default->set_text(TTR("Reset to Default"));
- _default->set_tooltip(TTR("Reset to default OpenXR action map."));
+ _default->set_tooltip_text(TTR("Reset to default OpenXR action map."));
_default->connect("pressed", callable_mp(this, &OpenXRActionMapEditor::_on_reset_to_default_layout));
top_hb->add_child(_default);
@@ -364,7 +449,9 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() {
select_interaction_profile_dialog->connect("interaction_profile_selected", callable_mp(this, &OpenXRActionMapEditor::_on_interaction_profile_selected));
add_child(select_interaction_profile_dialog);
- _load_action_map(ProjectSettings::get_singleton()->get("xr/openxr/default_action_map"));
+ // Our Action map editor is only shown if openxr is enabled in project settings
+ // So load our action map and if it doesn't exist, create it right away.
+ _load_action_map(GLOBAL_GET("xr/openxr/default_action_map"), true);
}
OpenXRActionMapEditor::~OpenXRActionMapEditor() {
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index a19bc90f56..a04bae4a6e 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_map_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_map_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_MAP_EDITOR_H
#define OPENXR_ACTION_MAP_EDITOR_H
@@ -37,6 +37,7 @@
#include "../editor/openxr_select_interaction_profile_dialog.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
@@ -47,9 +48,9 @@ class OpenXRActionMapEditor : public VBoxContainer {
GDCLASS(OpenXRActionMapEditor, VBoxContainer);
private:
+ EditorUndoRedoManager *undo_redo;
String edited_path;
Ref<OpenXRActionMap> action_map;
- Vector<Node *> interaction_profiles;
HBoxContainer *top_hb = nullptr;
Label *header_label = nullptr;
@@ -64,9 +65,9 @@ private:
OpenXRSelectInteractionProfileDialog *select_interaction_profile_dialog = nullptr;
OpenXRActionSetEditor *_add_action_set_editor(Ref<OpenXRActionSet> p_action_set);
- void _update_action_sets();
+ void _create_action_sets();
OpenXRInteractionProfileEditorBase *_add_interaction_profile_editor(Ref<OpenXRInteractionProfile> p_interaction_profile);
- void _update_interaction_profiles();
+ void _create_interaction_profiles();
OpenXRActionSetEditor *_add_action_set(String p_name);
void _remove_action_set(String p_name);
@@ -74,7 +75,7 @@ private:
void _on_add_action_set();
void _set_focus_on_action_set(OpenXRActionSetEditor *p_action_set_editor);
void _on_remove_action_set(Object *p_action_set_editor);
- void _on_action_removed();
+ void _on_action_removed(Ref<OpenXRAction> p_action);
void _on_add_interaction_profile();
void _on_interaction_profile_selected(const String p_path);
@@ -90,6 +91,14 @@ protected:
static void _bind_methods();
void _notification(int p_what);
+ void _clear_action_map();
+
+ // used for undo/redo
+ void _do_add_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
+ void _do_remove_action_set_editor(OpenXRActionSetEditor *p_action_set_editor);
+ void _do_add_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
+ void _do_remove_interaction_profile_editor(OpenXRInteractionProfileEditorBase *p_interaction_profile_editor);
+
public:
void open_action_map(String p_path);
diff --git a/modules/openxr/editor/openxr_action_set_editor.cpp b/modules/openxr/editor/openxr_action_set_editor.cpp
index 7bf8557c5b..bcb0f5f8b1 100644
--- a/modules/openxr/editor/openxr_action_set_editor.cpp
+++ b/modules/openxr/editor/openxr_action_set_editor.cpp
@@ -1,39 +1,45 @@
-/*************************************************************************/
-/* openxr_action_set_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_set_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_action_set_editor.h"
#include "openxr_action_editor.h"
void OpenXRActionSetEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_do_set_name", "name"), &OpenXRActionSetEditor::_do_set_name);
+ ClassDB::bind_method(D_METHOD("_do_set_localized_name", "name"), &OpenXRActionSetEditor::_do_set_localized_name);
+ ClassDB::bind_method(D_METHOD("_do_set_priority", "value"), &OpenXRActionSetEditor::_do_set_priority);
+ ClassDB::bind_method(D_METHOD("_do_add_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_add_action_editor);
+ ClassDB::bind_method(D_METHOD("_do_remove_action_editor", "action_editor"), &OpenXRActionSetEditor::_do_remove_action_editor);
+
ADD_SIGNAL(MethodInfo("remove", PropertyInfo(Variant::OBJECT, "action_set_editor")));
- ADD_SIGNAL(MethodInfo("action_removed"));
+ ADD_SIGNAL(MethodInfo("action_removed", PropertyInfo(Variant::OBJECT, "action")));
}
void OpenXRActionSetEditor::_set_fold_icon() {
@@ -68,20 +74,6 @@ OpenXRActionEditor *OpenXRActionSetEditor::_add_action_editor(Ref<OpenXRAction>
return action_editor;
}
-void OpenXRActionSetEditor::_update_actions() {
- // out with the old...
- while (actions_vb->get_child_count() > 0) {
- memdelete(actions_vb->get_child(0));
- }
-
- // in with the new...
- Array actions = action_set->get_actions();
- for (int i = 0; i < actions.size(); i++) {
- Ref<OpenXRAction> action = actions[i];
- _add_action_editor(action);
- }
-}
-
void OpenXRActionSetEditor::_on_toggle_expand() {
is_expanded = !is_expanded;
actions_vb->set_visible(is_expanded);
@@ -89,24 +81,66 @@ void OpenXRActionSetEditor::_on_toggle_expand() {
}
void OpenXRActionSetEditor::_on_action_set_name_changed(const String p_new_text) {
- // TODO validate if entry is allowed
-
- // If our localized name matches our action set name, set this too
- if (action_set->get_name() == action_set->get_localized_name()) {
- action_set->set_localized_name(p_new_text);
- action_set_localized_name->set_text(p_new_text);
+ if (action_set->get_name() != p_new_text) {
+ undo_redo->create_action(TTR("Rename Action Set"));
+ undo_redo->add_do_method(this, "_do_set_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_name", action_set->get_name());
+ undo_redo->commit_action(false);
+
+ // If our localized name matches our action set name, set this too
+ if (action_set->get_name() == action_set->get_localized_name()) {
+ undo_redo->create_action(TTR("Rename Action Sets Localized name"));
+ undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
+ undo_redo->commit_action(false);
+
+ action_set->set_localized_name(p_new_text);
+ action_set_localized_name->set_text(p_new_text);
+ }
+ action_set->set_name(p_new_text);
+ action_set->set_edited(true);
}
+}
+
+void OpenXRActionSetEditor::_do_set_name(const String p_new_text) {
action_set->set_name(p_new_text);
+ action_set_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_localized_name_changed(const String p_new_text) {
+ if (action_set->get_localized_name() != p_new_text) {
+ undo_redo->create_action(TTR("Rename Action Sets Localized name"));
+ undo_redo->add_do_method(this, "_do_set_localized_name", p_new_text);
+ undo_redo->add_undo_method(this, "_do_set_localized_name", action_set->get_localized_name());
+ undo_redo->commit_action(false);
+
+ action_set->set_localized_name(p_new_text);
+ action_set->set_edited(true);
+ }
+}
+
+void OpenXRActionSetEditor::_do_set_localized_name(const String p_new_text) {
action_set->set_localized_name(p_new_text);
+ action_set_localized_name->set_text(p_new_text);
}
void OpenXRActionSetEditor::_on_action_set_priority_changed(const String p_new_text) {
int64_t value = p_new_text.to_int();
- action_set->set_priority(value);
+ if (action_set->get_priority() != value) {
+ undo_redo->create_action(TTR("Change Action Sets priority"));
+ undo_redo->add_do_method(this, "_do_set_priority", value);
+ undo_redo->add_undo_method(this, "_do_set_priority", action_set->get_priority());
+ undo_redo->commit_action(false);
+
+ action_set->set_priority(value);
+ action_set->set_edited(true);
+ }
+}
+
+void OpenXRActionSetEditor::_do_set_priority(int64_t p_value) {
+ action_set->set_priority(p_value);
+ action_set_priority->set_text(itos(p_value));
}
void OpenXRActionSetEditor::_on_add_action() {
@@ -116,8 +150,14 @@ void OpenXRActionSetEditor::_on_add_action() {
new_action->set_name("New");
new_action->set_localized_name("New");
action_set->add_action(new_action);
+ action_set->set_edited(true);
- _add_action_editor(new_action);
+ OpenXRActionEditor *action_editor = _add_action_editor(new_action);
+
+ undo_redo->create_action(TTR("Add action"));
+ undo_redo->add_do_method(this, "_do_add_action_editor", action_editor);
+ undo_redo->add_undo_method(this, "_do_remove_action_editor", action_editor);
+ undo_redo->commit_action(false);
// TODO handle focus
}
@@ -133,17 +173,36 @@ void OpenXRActionSetEditor::_on_remove_action(Object *p_action_editor) {
Ref<OpenXRAction> action = action_editor->get_action();
ERR_FAIL_COND(action.is_null());
- // TODO add undo/redo action
+ emit_signal("action_removed", action);
+
+ undo_redo->create_action(TTR("Delete action"));
+ undo_redo->add_do_method(this, "_do_remove_action_editor", action_editor);
+ undo_redo->add_undo_method(this, "_do_add_action_editor", action_editor);
+ undo_redo->commit_action(true);
- // TODO find where this action is used by our interaction profiles and remove it there
+ action_set->set_edited(true);
+}
- // And remove it....
- action_map->remove_action(action->get_name_with_set()); // remove it from the set and any interaction profile it relates to
- actions_vb->remove_child(action_editor);
- action_editor->queue_delete();
+void OpenXRActionSetEditor::_do_add_action_editor(OpenXRActionEditor *p_action_editor) {
+ Ref<OpenXRAction> action = p_action_editor->get_action();
+ ERR_FAIL_COND(action.is_null());
- // Let action map editor know so we can update our interaction profiles
- emit_signal("action_removed");
+ action_set->add_action(action);
+ actions_vb->add_child(p_action_editor);
+}
+
+void OpenXRActionSetEditor::_do_remove_action_editor(OpenXRActionEditor *p_action_editor) {
+ Ref<OpenXRAction> action = p_action_editor->get_action();
+ ERR_FAIL_COND(action.is_null());
+
+ actions_vb->remove_child(p_action_editor);
+ action_set->remove_action(action);
+}
+
+void OpenXRActionSetEditor::remove_all_actions() {
+ for (int i = actions_vb->get_child_count(); i > 0; --i) {
+ _on_remove_action(actions_vb->get_child(i));
+ }
}
void OpenXRActionSetEditor::set_focus_on_entry() {
@@ -152,6 +211,7 @@ void OpenXRActionSetEditor::set_focus_on_entry() {
}
OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set) {
+ undo_redo = EditorUndoRedoManager::get_singleton();
action_map = p_action_map;
action_set = p_action_set;
@@ -199,13 +259,13 @@ OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map,
action_set_hb->add_child(action_set_priority);
add_action = memnew(Button);
- add_action->set_tooltip("Add Action.");
+ add_action->set_tooltip_text("Add Action.");
add_action->connect("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_add_action));
add_action->set_flat(true);
action_set_hb->add_child(add_action);
rem_action_set = memnew(Button);
- rem_action_set->set_tooltip("Remove Action Set.");
+ rem_action_set->set_tooltip_text("Remove Action Set.");
rem_action_set->connect("pressed", callable_mp(this, &OpenXRActionSetEditor::_on_remove_action_set));
rem_action_set->set_flat(true);
action_set_hb->add_child(rem_action_set);
@@ -214,5 +274,10 @@ OpenXRActionSetEditor::OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map,
actions_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_vb->add_child(actions_vb);
- _update_actions();
+ // Add our existing actions
+ Array actions = action_set->get_actions();
+ for (int i = 0; i < actions.size(); i++) {
+ Ref<OpenXRAction> action = actions[i];
+ _add_action_editor(action);
+ }
}
diff --git a/modules/openxr/editor/openxr_action_set_editor.h b/modules/openxr/editor/openxr_action_set_editor.h
index d8c85d03dd..129f800abe 100644
--- a/modules/openxr/editor/openxr_action_set_editor.h
+++ b/modules/openxr/editor/openxr_action_set_editor.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_action_set_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_action_set_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ACTION_SET_EDITOR_H
#define OPENXR_ACTION_SET_EDITOR_H
@@ -44,6 +44,7 @@ class OpenXRActionSetEditor : public HBoxContainer {
GDCLASS(OpenXRActionSetEditor, HBoxContainer);
private:
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRActionMap> action_map;
Ref<OpenXRActionSet> action_set;
@@ -63,7 +64,6 @@ private:
void _set_fold_icon();
void _theme_changed();
OpenXRActionEditor *_add_action_editor(Ref<OpenXRAction> p_action);
- void _update_actions();
void _on_toggle_expand();
void _on_action_set_name_changed(const String p_new_text);
@@ -78,10 +78,19 @@ protected:
static void _bind_methods();
void _notification(int p_what);
+ // used for undo/redo
+ void _do_set_name(const String p_new_text);
+ void _do_set_localized_name(const String p_new_text);
+ void _do_set_priority(int64_t value);
+ void _do_add_action_editor(OpenXRActionEditor *p_action_editor);
+ void _do_remove_action_editor(OpenXRActionEditor *p_action_editor);
+
public:
Ref<OpenXRActionSet> get_action_set() { return action_set; };
void set_focus_on_entry();
+ void remove_all_actions();
+
OpenXRActionSetEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRActionSet> p_action_set);
};
diff --git a/modules/openxr/editor/openxr_editor_plugin.cpp b/modules/openxr/editor/openxr_editor_plugin.cpp
index b87b538511..75becf035a 100644
--- a/modules/openxr/editor/openxr_editor_plugin.cpp
+++ b/modules/openxr/editor/openxr_editor_plugin.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_editor_plugin.h"
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
index ce230ee95b..ef75385233 100644
--- a/modules/openxr/editor/openxr_editor_plugin.h
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_EDITOR_PLUGIN_H
#define 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 e2dc2d1b74..6a848dd430 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.cpp
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_interaction_profile_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interaction_profile_editor.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_interaction_profile_editor.h"
#include "scene/gui/box_container.h"
@@ -58,6 +58,13 @@ void OpenXRInteractionProfileEditorBase::_notification(int p_what) {
}
}
+void OpenXRInteractionProfileEditorBase::_do_update_interaction_profile() {
+ if (!is_dirty) {
+ is_dirty = true;
+ call_deferred("_update_interaction_profile");
+ }
+}
+
void OpenXRInteractionProfileEditorBase::_add_binding(const String p_action, const String p_path) {
ERR_FAIL_COND(action_map.is_null());
ERR_FAIL_COND(interaction_profile.is_null());
@@ -71,14 +78,16 @@ void OpenXRInteractionProfileEditorBase::_add_binding(const String p_action, con
binding.instantiate();
binding->set_action(action);
interaction_profile->add_binding(binding);
+ interaction_profile->set_edited(true);
}
binding->add_path(p_path);
+ binding->set_edited(true);
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
- call_deferred("_update_interaction_profile");
+ _do_update_interaction_profile();
}
void OpenXRInteractionProfileEditorBase::_remove_binding(const String p_action, const String p_path) {
@@ -91,25 +100,54 @@ void OpenXRInteractionProfileEditorBase::_remove_binding(const String p_action,
Ref<OpenXRIPBinding> binding = interaction_profile->get_binding_for_action(action);
if (binding.is_valid()) {
binding->remove_path(p_path);
+ binding->set_edited(true);
if (binding->get_path_count() == 0) {
interaction_profile->remove_binding(binding);
+ interaction_profile->set_edited(true);
}
// Update our toplevel paths
action->set_toplevel_paths(action_map->get_top_level_paths(action));
- call_deferred("_update_interaction_profile");
+ _do_update_interaction_profile();
+ }
+}
+
+void OpenXRInteractionProfileEditorBase::remove_all_bindings_for_action(Ref<OpenXRAction> p_action) {
+ Ref<OpenXRIPBinding> binding = interaction_profile->get_binding_for_action(p_action);
+ if (binding.is_valid()) {
+ String action_name = p_action->get_name_with_set();
+
+ // for our undo/redo we process all paths
+ undo_redo->create_action(TTR("Remove action from interaction profile"));
+ PackedStringArray paths = binding->get_paths();
+ for (const String &path : paths) {
+ undo_redo->add_do_method(this, "_remove_binding", action_name, path);
+ undo_redo->add_undo_method(this, "_add_binding", action_name, path);
+ }
+ undo_redo->commit_action(false);
+
+ // but we take a shortcut here :)
+ interaction_profile->remove_binding(binding);
+ interaction_profile->set_edited(true);
+
+ // Update our toplevel paths
+ p_action->set_toplevel_paths(action_map->get_top_level_paths(p_action));
+
+ _do_update_interaction_profile();
}
}
OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) {
+ undo_redo = EditorUndoRedoManager::get_singleton();
+
action_map = p_action_map;
interaction_profile = p_interaction_profile;
String profile_path = interaction_profile->get_interaction_profile_path();
String profile_name = profile_path;
- profile_def = OpenXRDefs::get_profile(profile_path);
+ profile_def = OpenXRInteractionProfileMetaData::get_singleton()->get_profile(profile_path);
if (profile_def != nullptr) {
profile_name = profile_def->display_name;
}
@@ -117,6 +155,9 @@ OpenXRInteractionProfileEditorBase::OpenXRInteractionProfileEditorBase(Ref<OpenX
set_name(profile_name);
set_h_size_flags(SIZE_EXPAND_FILL);
set_v_size_flags(SIZE_EXPAND_FILL);
+
+ // Make sure it is updated when it enters the tree...
+ is_dirty = true;
}
///////////////////////////////////////////////////////////////////////////
@@ -128,11 +169,22 @@ void OpenXRInteractionProfileEditor::select_action_for(const String p_io_path) {
}
void OpenXRInteractionProfileEditor::action_selected(const String p_action) {
- _add_binding(p_action, selecting_for_io_path);
+ undo_redo->create_action(TTR("Add binding"));
+ undo_redo->add_do_method(this, "_add_binding", p_action, selecting_for_io_path);
+ undo_redo->add_undo_method(this, "_remove_binding", p_action, selecting_for_io_path);
+ undo_redo->commit_action(true);
+
selecting_for_io_path = "";
}
-void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRDefs::IOPath *p_io_path) {
+void OpenXRInteractionProfileEditor::_on_remove_pressed(const String p_action, const String p_for_io_path) {
+ undo_redo->create_action(TTR("Remove binding"));
+ undo_redo->add_do_method(this, "_remove_binding", p_action, p_for_io_path);
+ undo_redo->add_undo_method(this, "_add_binding", p_action, p_for_io_path);
+ undo_redo->commit_action(true);
+}
+
+void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetaData::IOPath *p_io_path) {
HBoxContainer *path_hb = memnew(HBoxContainer);
path_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
p_container->add_child(path_hb);
@@ -196,7 +248,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")));
- action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditorBase *)this, &OpenXRInteractionProfileEditorBase::_remove_binding).bind(action->get_name_with_set(), String(p_io_path->openxr_path)));
+ action_rem->connect("pressed", callable_mp((OpenXRInteractionProfileEditor *)this, &OpenXRInteractionProfileEditor::_on_remove_pressed).bind(action->get_name_with_set(), String(p_io_path->openxr_path)));
action_hb->add_child(action_rem);
}
}
@@ -206,6 +258,11 @@ void OpenXRInteractionProfileEditor::_add_io_path(VBoxContainer *p_container, co
void OpenXRInteractionProfileEditor::_update_interaction_profile() {
ERR_FAIL_NULL(profile_def);
+ if (!is_dirty) {
+ // no need to update
+ return;
+ }
+
// out with the old...
while (main_hb->get_child_count() > 0) {
memdelete(main_hb->get_child(0));
@@ -214,9 +271,9 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() {
// in with the new...
// Determine toplevel paths
- Vector<const OpenXRDefs::TopLevelPath *> top_level_paths;
- for (int i = 0; i < profile_def->io_path_count; i++) {
- const OpenXRDefs::IOPath *io_path = &profile_def->io_paths[i];
+ Vector<String> top_level_paths;
+ for (int i = 0; i < profile_def->io_paths.size(); i++) {
+ const OpenXRInteractionProfileMetaData::IOPath *io_path = &profile_def->io_paths[i];
if (!top_level_paths.has(io_path->top_level_path)) {
top_level_paths.push_back(io_path->top_level_path);
@@ -233,21 +290,24 @@ void OpenXRInteractionProfileEditor::_update_interaction_profile() {
panel->add_child(container);
Label *label = memnew(Label);
- label->set_text(top_level_paths[i]->display_name);
+ label->set_text(OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_name(top_level_paths[i]));
container->add_child(label);
- for (int j = 0; j < profile_def->io_path_count; j++) {
- const OpenXRDefs::IOPath *io_path = &profile_def->io_paths[j];
+ for (int j = 0; j < profile_def->io_paths.size(); j++) {
+ const OpenXRInteractionProfileMetaData::IOPath *io_path = &profile_def->io_paths[j];
if (io_path->top_level_path == top_level_paths[i]) {
_add_io_path(container, io_path);
}
}
}
+
+ // and we've updated it...
+ is_dirty = false;
}
void OpenXRInteractionProfileEditor::_theme_changed() {
for (int i = 0; i < main_hb->get_child_count(); i++) {
- Control *panel = static_cast<Control *>(main_hb->get_child(i));
+ Control *panel = Object::cast_to<Control>(main_hb->get_child(i));
if (panel) {
panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer")));
}
@@ -256,14 +316,10 @@ void OpenXRInteractionProfileEditor::_theme_changed() {
OpenXRInteractionProfileEditor::OpenXRInteractionProfileEditor(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile) :
OpenXRInteractionProfileEditorBase(p_action_map, p_interaction_profile) {
- // TODO background of scrollbox should be darker with our VBoxContainers we're adding in _update_interaction_profile the normal color
-
main_hb = memnew(HBoxContainer);
add_child(main_hb);
select_action_dialog = memnew(OpenXRSelectActionDialog(p_action_map));
select_action_dialog->connect("action_selected", callable_mp(this, &OpenXRInteractionProfileEditor::action_selected));
add_child(select_action_dialog);
-
- _update_interaction_profile();
}
diff --git a/modules/openxr/editor/openxr_interaction_profile_editor.h b/modules/openxr/editor/openxr_interaction_profile_editor.h
index 20a37a80eb..fa25a000a9 100644
--- a/modules/openxr/editor/openxr_interaction_profile_editor.h
+++ b/modules/openxr/editor/openxr_interaction_profile_editor.h
@@ -1,39 +1,40 @@
-/*************************************************************************/
-/* openxr_interaction_profile_editor.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interaction_profile_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_INTERACTION_PROFILE_EDITOR_H
#define OPENXR_INTERACTION_PROFILE_EDITOR_H
#include "../action_map/openxr_action_map.h"
-#include "../action_map/openxr_defs.h"
#include "../action_map/openxr_interaction_profile.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/scroll_container.h"
#include "openxr_select_action_dialog.h"
@@ -42,22 +43,29 @@ class OpenXRInteractionProfileEditorBase : public ScrollContainer {
GDCLASS(OpenXRInteractionProfileEditorBase, ScrollContainer);
protected:
+ EditorUndoRedoManager *undo_redo;
Ref<OpenXRInteractionProfile> interaction_profile;
Ref<OpenXRActionMap> action_map;
+ bool is_dirty = false;
+
static void _bind_methods();
void _notification(int p_what);
- const OpenXRDefs::InteractionProfile *profile_def = nullptr;
+ const OpenXRInteractionProfileMetaData::InteractionProfile *profile_def = nullptr;
public:
Ref<OpenXRInteractionProfile> get_interaction_profile() { return interaction_profile; }
virtual void _update_interaction_profile() {}
virtual void _theme_changed() {}
+
+ void _do_update_interaction_profile();
void _add_binding(const String p_action, const String p_path);
void _remove_binding(const String p_action, const String p_path);
+ void remove_all_bindings_for_action(Ref<OpenXRAction> p_action);
+
OpenXRInteractionProfileEditorBase(Ref<OpenXRActionMap> p_action_map, Ref<OpenXRInteractionProfile> p_interaction_profile);
};
@@ -69,11 +77,12 @@ private:
HBoxContainer *main_hb = nullptr;
OpenXRSelectActionDialog *select_action_dialog = nullptr;
- void _add_io_path(VBoxContainer *p_container, const OpenXRDefs::IOPath *p_io_path);
+ void _add_io_path(VBoxContainer *p_container, const OpenXRInteractionProfileMetaData::IOPath *p_io_path);
public:
void select_action_for(const String p_io_path);
void action_selected(const String p_action);
+ void _on_remove_pressed(const String p_action, const String p_for_io_path);
virtual void _update_interaction_profile() override;
virtual void _theme_changed() override;
diff --git a/modules/openxr/editor/openxr_select_action_dialog.cpp b/modules/openxr/editor/openxr_select_action_dialog.cpp
index 80e58044d5..b7dd7d5def 100644
--- a/modules/openxr/editor/openxr_select_action_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_action_dialog.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_select_action_dialog.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_select_action_dialog.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_select_action_dialog.h"
#include "editor/editor_node.h"
@@ -39,7 +39,7 @@ void OpenXRSelectActionDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ scroll->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -47,7 +47,7 @@ void OpenXRSelectActionDialog::_notification(int p_what) {
void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
- Button *button = static_cast<Button *>(get_node(button_path));
+ Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(true);
}
@@ -57,7 +57,7 @@ void OpenXRSelectActionDialog::_on_select_action(const String p_action) {
if (selected_action != "") {
NodePath button_path = action_buttons[selected_action];
- Button *button = static_cast<Button *>(get_node(button_path));
+ Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
diff --git a/modules/openxr/editor/openxr_select_action_dialog.h b/modules/openxr/editor/openxr_select_action_dialog.h
index cbe1380e18..4d8eddf3e5 100644
--- a/modules/openxr/editor/openxr_select_action_dialog.h
+++ b/modules/openxr/editor/openxr_select_action_dialog.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_select_action_dialog.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_select_action_dialog.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SELECT_ACTION_DIALOG_H
#define 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 23b025db08..9362c08174 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_select_interaction_profile_dialog.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_select_interaction_profile_dialog.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_select_interaction_profile_dialog.h"
@@ -38,7 +38,7 @@ void OpenXRSelectInteractionProfileDialog::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
+ scroll->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
} break;
}
}
@@ -46,7 +46,7 @@ void OpenXRSelectInteractionProfileDialog::_notification(int p_what) {
void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const String p_interaction_profile) {
if (selected_interaction_profile != "") {
NodePath button_path = ip_buttons[selected_interaction_profile];
- Button *button = static_cast<Button *>(get_node(button_path));
+ Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(true);
}
@@ -56,7 +56,7 @@ void OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile(const
if (selected_interaction_profile != "") {
NodePath button_path = ip_buttons[selected_interaction_profile];
- Button *button = static_cast<Button *>(get_node(button_path));
+ Button *button = Object::cast_to<Button>(get_node(button_path));
if (button != nullptr) {
button->set_flat(false);
}
@@ -75,13 +75,13 @@ void OpenXRSelectInteractionProfileDialog::open(PackedStringArray p_do_not_inclu
ip_buttons.clear();
// in with the new
- PackedStringArray interaction_profiles = OpenXRDefs::get_interaction_profile_paths();
+ PackedStringArray interaction_profiles = OpenXRInteractionProfileMetaData::get_singleton()->get_interaction_profile_paths();
for (int i = 0; i < interaction_profiles.size(); i++) {
String path = interaction_profiles[i];
if (!p_do_not_include.has(path)) {
Button *ip_button = memnew(Button);
ip_button->set_flat(true);
- ip_button->set_text(OpenXRDefs::get_profile(path)->display_name);
+ ip_button->set_text(OpenXRInteractionProfileMetaData::get_singleton()->get_profile(path)->display_name);
ip_button->connect("pressed", callable_mp(this, &OpenXRSelectInteractionProfileDialog::_on_select_interaction_profile).bind(path));
main_vb->add_child(ip_button);
diff --git a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
index 54bfe3120a..4826a23a78 100644
--- a/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
+++ b/modules/openxr/editor/openxr_select_interaction_profile_dialog.h
@@ -1,37 +1,37 @@
-/*************************************************************************/
-/* openxr_select_interaction_profile_dialog.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_select_interaction_profile_dialog.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_SELECT_INTERACTION_PROFILE_DIALOG_H
#define OPENXR_SELECT_INTERACTION_PROFILE_DIALOG_H
-#include "../action_map/openxr_defs.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
diff --git a/modules/openxr/extensions/openxr_android_extension.cpp b/modules/openxr/extensions/openxr_android_extension.cpp
index 3bd4db169c..4465daf22a 100644
--- a/modules/openxr/extensions/openxr_android_extension.cpp
+++ b/modules/openxr/extensions/openxr_android_extension.cpp
@@ -1,35 +1,40 @@
-/*************************************************************************/
-/* openxr_android_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_android_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_android_extension.h"
+#include "java_godot_wrapper.h"
+#include "os_android.h"
+#include "thread_jandroid.h"
+#include <jni.h>
+#include <modules/openxr/openxr_api.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
@@ -39,22 +44,29 @@ OpenXRAndroidExtension *OpenXRAndroidExtension::get_singleton() {
return singleton;
}
-OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
- OpenXRExtensionWrapper(p_openxr_api) {
+OpenXRAndroidExtension::OpenXRAndroidExtension() {
singleton = this;
+}
+
+HashMap<String, bool *> OpenXRAndroidExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
- request_extensions[XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME] = nullptr; // must be available
+ request_extensions[XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME] = &create_instance_extension_available;
+
+ return request_extensions;
+}
- // Initialize the loader
- PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR;
- result = xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction *)(&xrInitializeLoaderKHR));
- ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to retrieve pointer to xrInitializeLoaderKHR");
+void OpenXRAndroidExtension::on_before_instance_created() {
+ if (XR_FAILED(EXT_TRY_INIT_XR_FUNC(xrInitializeLoaderKHR))) {
+ // XR_KHR_loader_init not supported on this platform
+ return;
+ }
+ loader_init_extension_available = true;
- // TODO fix this code, this is still code from GDNative!
- JNIEnv *env = android_api->godot_android_get_env();
+ JNIEnv *env = get_jni_env();
JavaVM *vm;
env->GetJavaVM(&vm);
- jobject activity_object = env->NewGlobalRef(android_api->godot_android_get_activity());
+ jobject activity_object = env->NewGlobalRef(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
XrLoaderInitInfoAndroidKHR loader_init_info_android = {
.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR,
@@ -62,10 +74,36 @@ OpenXRAndroidExtension::OpenXRAndroidExtension(OpenXRAPI *p_openxr_api) :
.applicationVM = vm,
.applicationContext = activity_object
};
- xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
+ XrResult result = xrInitializeLoaderKHR((const XrLoaderInitInfoBaseHeaderKHR *)&loader_init_info_android);
ERR_FAIL_COND_MSG(XR_FAILED(result), "Failed to call xrInitializeLoaderKHR");
}
+// We're keeping the Android create info struct here to avoid including openxr_platform.h in a header, which would break other extensions.
+// This is reasonably safe as the struct is only used during initialization and the extension is a singleton.
+static XrInstanceCreateInfoAndroidKHR instance_create_info;
+
+void *OpenXRAndroidExtension::set_instance_create_info_and_get_next_pointer(void *p_next_pointer) {
+ if (!create_instance_extension_available) {
+ if (!loader_init_extension_available) {
+ WARN_PRINT("No Android extensions available, couldn't pass JVM and Activity to OpenXR");
+ }
+ return nullptr;
+ }
+
+ JNIEnv *env = get_jni_env();
+ JavaVM *vm;
+ env->GetJavaVM(&vm);
+ jobject activity_object = env->NewGlobalRef(static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity());
+
+ instance_create_info = {
+ .type = XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR,
+ .next = p_next_pointer,
+ .applicationVM = vm,
+ .applicationActivity = activity_object
+ };
+ return &instance_create_info;
+}
+
OpenXRAndroidExtension::~OpenXRAndroidExtension() {
singleton = nullptr;
}
diff --git a/modules/openxr/extensions/openxr_android_extension.h b/modules/openxr/extensions/openxr_android_extension.h
index 88b0e310e7..0e7c44d6d5 100644
--- a/modules/openxr/extensions/openxr_android_extension.h
+++ b/modules/openxr/extensions/openxr_android_extension.h
@@ -1,47 +1,59 @@
-/*************************************************************************/
-/* openxr_android_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_android_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_ANDROID_EXTENSION_H
#define OPENXR_ANDROID_EXTENSION_H
+#include "../util.h"
#include "openxr_extension_wrapper.h"
class OpenXRAndroidExtension : public OpenXRExtensionWrapper {
public:
static OpenXRAndroidExtension *get_singleton();
- OpenXRAndroidExtension(OpenXRAPI *p_openxr_api);
+ OpenXRAndroidExtension();
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+ virtual void on_before_instance_created() override;
+ virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer) override;
+
virtual ~OpenXRAndroidExtension() override;
private:
static OpenXRAndroidExtension *singleton;
+
+ bool loader_init_extension_available = false;
+ bool create_instance_extension_available = false;
+
+ // Initialize the loader
+ EXT_PROTO_XRRESULT_FUNC1(xrInitializeLoaderKHR, (const XrLoaderInitInfoBaseHeaderKHR *), loaderInitInfo)
};
#endif // OPENXR_ANDROID_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
new file mode 100644
index 0000000000..7a16da144e
--- /dev/null
+++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.cpp
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/* openxr_composition_layer_depth_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_composition_layer_depth_extension.h"
+
+OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::singleton = nullptr;
+
+OpenXRCompositionLayerDepthExtension *OpenXRCompositionLayerDepthExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRCompositionLayerDepthExtension::OpenXRCompositionLayerDepthExtension() {
+ singleton = this;
+}
+
+OpenXRCompositionLayerDepthExtension::~OpenXRCompositionLayerDepthExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRCompositionLayerDepthExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRCompositionLayerDepthExtension::is_available() {
+ return available;
+}
+
+XrCompositionLayerBaseHeader *OpenXRCompositionLayerDepthExtension::get_composition_layer() {
+ // Seems this is all done in our base layer... Just in case this changes...
+
+ return nullptr;
+}
diff --git a/modules/openxr/extensions/openxr_composition_layer_depth_extension.h b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h
new file mode 100644
index 0000000000..50bbef4db4
--- /dev/null
+++ b/modules/openxr/extensions/openxr_composition_layer_depth_extension.h
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* openxr_composition_layer_depth_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_COMPOSITION_LAYER_DEPTH_EXTENSION_H
+#define OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H
+
+#include "openxr_composition_layer_provider.h"
+#include "openxr_extension_wrapper.h"
+
+class OpenXRCompositionLayerDepthExtension : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
+public:
+ static OpenXRCompositionLayerDepthExtension *get_singleton();
+
+ OpenXRCompositionLayerDepthExtension();
+ virtual ~OpenXRCompositionLayerDepthExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+ bool is_available();
+ virtual XrCompositionLayerBaseHeader *get_composition_layer() override;
+
+private:
+ static OpenXRCompositionLayerDepthExtension *singleton;
+
+ bool available = false;
+};
+
+#endif // OPENXR_COMPOSITION_LAYER_DEPTH_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_composition_layer_provider.h b/modules/openxr/extensions/openxr_composition_layer_provider.h
index 019dffa2a8..2f0869374e 100644
--- a/modules/openxr/extensions/openxr_composition_layer_provider.h
+++ b/modules/openxr/extensions/openxr_composition_layer_provider.h
@@ -1,45 +1,45 @@
-/*************************************************************************/
-/* openxr_composition_layer_provider.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_composition_layer_provider.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_COMPOSITION_LAYER_PROVIDER_H
#define OPENXR_COMPOSITION_LAYER_PROVIDER_H
+#include "openxr_extension_wrapper.h"
#include <openxr/openxr.h>
// Interface for OpenXR extensions that provide a composition layer.
class OpenXRCompositionLayerProvider {
public:
- // TODO changed to normal method definition for now
- // CI complains until we implement this, haven't ported it yet from plugin
- // virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0;
- XrCompositionLayerBaseHeader *get_composition_layer() { return nullptr; };
+ virtual XrCompositionLayerBaseHeader *get_composition_layer() = 0;
+
+ virtual ~OpenXRCompositionLayerProvider() {}
};
#endif // OPENXR_COMPOSITION_LAYER_PROVIDER_H
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index ecc6e0dd4e..84279635b5 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_extension_wrapper.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_extension_wrapper.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_EXTENSION_WRAPPER_H
#define OPENXR_EXTENSION_WRAPPER_H
@@ -42,19 +42,14 @@
class OpenXRAPI;
class OpenXRActionMap;
+// `OpenXRExtensionWrapper` allows us to implement OpenXR extensions.
class OpenXRExtensionWrapper {
-protected:
- OpenXRAPI *openxr_api = nullptr;
-
- // 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
- HashMap<String, bool *> request_extensions;
-
public:
- virtual HashMap<String, bool *> get_request_extensions() {
- return request_extensions;
- }
+ // `get_requested_extensions` should return a list of OpenXR extensions related to this extension.
+ // If the bool * is a nullptr this extension is mandatory
+ // If the bool * points to a boolean, the boolean will be updated
+ // to true if the extension is enabled.
+ virtual HashMap<String, bool *> get_requested_extensions() = 0;
// These functions allow an extension to add entries to a struct chain.
// `p_next_pointer` points to the last struct that was created for this chain
@@ -62,46 +57,60 @@ public:
// You should return the pointer to the last struct you define as your result.
// If you are not adding any structs, just return `p_next_pointer`.
// See existing extensions for examples of this implementation.
- virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
- virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
- virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; }
+ virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when we interogate OpenXRS system abilities.
+ virtual void *set_instance_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when we create our OpenXR instance.
+ virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when we create our OpenXR session.
+ virtual void *set_swapchain_create_info_and_get_next_pointer(void *p_next_pointer) { return p_next_pointer; } // Add additional data structures when creating OpenXR swap chains.
- virtual void on_instance_created(const XrInstance p_instance) {}
- virtual void on_instance_destroyed() {}
- virtual void on_session_created(const XrSession p_instance) {}
+ // `on_register_metadata` allows extensions to register additional controller metadata.
+ // This function is called even when OpenXRApi is not constructured as the metadata
+ // needs to be available to the editor.
+ // Also extensions should provide metadata regardless of whether they are supported
+ // on the host system as the controller data is used to setup action maps for users
+ // who may have access to the relevant hardware.
+ virtual void on_register_metadata() {}
+
+ virtual void on_before_instance_created() {} // `on_before_instance_created` is called before we create our OpenXR instance.
+ virtual void on_instance_created(const XrInstance p_instance) {} // `on_instance_created` is called right after we've successfully created our OpenXR instance.
+ virtual void on_instance_destroyed() {} // `on_instance_destroyed` is called right before we destroy our OpenXR instance.
+ virtual void on_session_created(const XrSession p_instance) {} // `on_session_created` is called right after we've successfully created our OpenXR session.
+ virtual void on_session_destroyed() {} // `on_session_destroyed` is called right before we destroy our OpenXR session.
+
+ // `on_process` is called as part of our OpenXR process handling,
+ // this happens right before physics process and normal processing is run.
+ // This is when controller data is queried and made available to game logic.
virtual void on_process() {}
- virtual void on_pre_render() {}
- virtual void on_session_destroyed() {}
+ virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewport.
- virtual void on_state_idle() {}
- virtual void on_state_ready() {}
- virtual void on_state_synchronized() {}
- virtual void on_state_visible() {}
- virtual void on_state_focused() {}
- virtual void on_state_stopping() {}
- virtual void on_state_loss_pending() {}
- virtual void on_state_exiting() {}
+ virtual void on_state_idle() {} // `on_state_idle` is called when the OpenXR session state is changed to idle.
+ virtual void on_state_ready() {} // `on_state_ready` is called when the OpenXR session state is changed to ready, this means OpenXR is ready to setup our session.
+ virtual void on_state_synchronized() {} // `on_state_synchronized` is called when the OpenXR session state is changed to synchronized, note that OpenXR also returns to this state when our application looses focus.
+ virtual void on_state_visible() {} // `on_state_visible` is called when the OpenXR session state is changed to visible, OpenXR is now ready to receive frames.
+ virtual void on_state_focused() {} // `on_state_focused` is called when the OpenXR session state is changed to focused, this state is the active state when our game runs.
+ virtual void on_state_stopping() {} // `on_state_stopping` is called when the OpenXR session state is changed to stopping.
+ virtual void on_state_loss_pending() {} // `on_state_loss_pending` is called when the OpenXR session state is changed to loss pending.
+ virtual void on_state_exiting() {} // `on_state_exiting` is called when the OpenXR session state is changed to exiting.
- // Returns true if the event was handled, false otherwise.
+ // `on_event_polled` is called when there is an OpenXR event to process.
+ // Should return true if the event was handled, false otherwise.
virtual bool on_event_polled(const XrEventDataBuffer &event) {
return false;
}
- OpenXRExtensionWrapper(OpenXRAPI *p_openxr_api) { openxr_api = p_openxr_api; };
+ OpenXRExtensionWrapper() = default;
virtual ~OpenXRExtensionWrapper() = default;
};
+// `OpenXRGraphicsExtensionWrapper` implements specific logic for each supported graphics API.
class OpenXRGraphicsExtensionWrapper : public OpenXRExtensionWrapper {
public:
- virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0;
- 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, 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){};
+ virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_swapchain_formats` should return a list of usable color formats.
+ virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) = 0; // `get_usable_depth_formats` should return a list of usable depth formats.
+ virtual String get_swapchain_format_name(int64_t p_swapchain_format) const = 0; // `get_swapchain_format_name` should return the constant name of a given format.
+ 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; // `get_swapchain_image_data` extracts image IDs for the swapchain images and stores there in an implementation dependent data structure.
+ virtual void cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) = 0; // `cleanup_swapchain_graphics_data` cleans up the data held in our implementation dependent data structure and should free up its memory.
+ virtual bool create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) = 0; // `create_projection_fov` creates a proper projection matrix based on asymmetric FOV data provided by OpenXR.
+ virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) = 0; // `get_texture` returns a Godot texture RID for the current active texture in our swapchain.
};
#endif // OPENXR_EXTENSION_WRAPPER_H
diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp
new file mode 100644
index 0000000000..0ef7070531
--- /dev/null
+++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp
@@ -0,0 +1,127 @@
+/**************************************************************************/
+/* openxr_fb_display_refresh_rate_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_fb_display_refresh_rate_extension.h"
+
+OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::singleton = nullptr;
+
+OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRDisplayRefreshRateExtension::OpenXRDisplayRefreshRateExtension() {
+ singleton = this;
+}
+
+OpenXRDisplayRefreshRateExtension::~OpenXRDisplayRefreshRateExtension() {
+ display_refresh_rate_ext = false;
+}
+
+HashMap<String, bool *> OpenXRDisplayRefreshRateExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME] = &display_refresh_rate_ext;
+
+ return request_extensions;
+}
+
+void OpenXRDisplayRefreshRateExtension::on_instance_created(const XrInstance p_instance) {
+ if (display_refresh_rate_ext) {
+ EXT_INIT_XR_FUNC(xrEnumerateDisplayRefreshRatesFB);
+ EXT_INIT_XR_FUNC(xrGetDisplayRefreshRateFB);
+ EXT_INIT_XR_FUNC(xrRequestDisplayRefreshRateFB);
+ }
+}
+
+void OpenXRDisplayRefreshRateExtension::on_instance_destroyed() {
+ display_refresh_rate_ext = false;
+}
+
+float OpenXRDisplayRefreshRateExtension::get_refresh_rate() const {
+ float refresh_rate = 0.0;
+
+ if (display_refresh_rate_ext) {
+ float rate;
+ XrResult result = xrGetDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), &rate);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to obtain refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ } else {
+ refresh_rate = rate;
+ }
+ }
+
+ return refresh_rate;
+}
+
+void OpenXRDisplayRefreshRateExtension::set_refresh_rate(float p_refresh_rate) {
+ if (display_refresh_rate_ext) {
+ XrResult result = xrRequestDisplayRefreshRateFB(OpenXRAPI::get_singleton()->get_session(), p_refresh_rate);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to set refresh rate [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ }
+ }
+}
+
+Array OpenXRDisplayRefreshRateExtension::get_available_refresh_rates() const {
+ Array arr;
+ XrResult result;
+
+ if (display_refresh_rate_ext) {
+ uint32_t display_refresh_rate_count = 0;
+ result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), 0, &display_refresh_rate_count, nullptr);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ }
+
+ if (display_refresh_rate_count > 0) {
+ float *display_refresh_rates = (float *)memalloc(sizeof(float) * display_refresh_rate_count);
+ if (display_refresh_rates == nullptr) {
+ print_line("OpenXR: Failed to obtain refresh rates memory buffer [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ return arr;
+ }
+
+ result = xrEnumerateDisplayRefreshRatesFB(OpenXRAPI::get_singleton()->get_session(), display_refresh_rate_count, &display_refresh_rate_count, display_refresh_rates);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to obtain refresh rates count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ memfree(display_refresh_rates);
+ return arr;
+ }
+
+ for (uint32_t i = 0; i < display_refresh_rate_count; i++) {
+ float refresh_rate = display_refresh_rates[i];
+ arr.push_back(Variant(refresh_rate));
+ }
+
+ memfree(display_refresh_rates);
+ }
+ }
+
+ return arr;
+}
diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h
new file mode 100644
index 0000000000..0e814cbb13
--- /dev/null
+++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* openxr_fb_display_refresh_rate_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_FB_DISPLAY_REFRESH_RATE_EXTENSION_H
+#define OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H
+
+// This extension gives us access to the possible display refresh rates
+// supported by the HMD.
+// While this is an FB extension it has been adopted by most runtimes and
+// will likely become core in the near future.
+
+#include "../openxr_api.h"
+#include "../util.h"
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRDisplayRefreshRateExtension *get_singleton();
+
+ OpenXRDisplayRefreshRateExtension();
+ virtual ~OpenXRDisplayRefreshRateExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+
+ float get_refresh_rate() const;
+ void set_refresh_rate(float p_refresh_rate);
+
+ Array get_available_refresh_rates() const;
+
+private:
+ static OpenXRDisplayRefreshRateExtension *singleton;
+
+ bool display_refresh_rate_ext = false;
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateDisplayRefreshRatesFB, (XrSession), session, (uint32_t), displayRefreshRateCapacityInput, (uint32_t *), displayRefreshRateCountOutput, (float *), displayRefreshRates);
+ EXT_PROTO_XRRESULT_FUNC2(xrGetDisplayRefreshRateFB, (XrSession), session, (float *), display_refresh_rate);
+ EXT_PROTO_XRRESULT_FUNC2(xrRequestDisplayRefreshRateFB, (XrSession), session, (float), display_refresh_rate);
+};
+
+#endif // OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp
new file mode 100644
index 0000000000..569895a620
--- /dev/null
+++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp
@@ -0,0 +1,240 @@
+/**************************************************************************/
+/* openxr_fb_passthrough_extension_wrapper.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_fb_passthrough_extension_wrapper.h"
+
+#include "core/os/os.h"
+#include "scene/main/viewport.h"
+#include "scene/main/window.h"
+
+using namespace godot;
+
+OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::singleton = nullptr;
+
+OpenXRFbPassthroughExtensionWrapper *OpenXRFbPassthroughExtensionWrapper::get_singleton() {
+ return singleton;
+}
+
+OpenXRFbPassthroughExtensionWrapper::OpenXRFbPassthroughExtensionWrapper() {
+ singleton = this;
+}
+
+OpenXRFbPassthroughExtensionWrapper::~OpenXRFbPassthroughExtensionWrapper() {
+ cleanup();
+}
+
+HashMap<String, bool *> OpenXRFbPassthroughExtensionWrapper::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_FB_PASSTHROUGH_EXTENSION_NAME] = &fb_passthrough_ext;
+ request_extensions[XR_FB_TRIANGLE_MESH_EXTENSION_NAME] = &fb_triangle_mesh_ext;
+
+ return request_extensions;
+}
+
+void OpenXRFbPassthroughExtensionWrapper::cleanup() {
+ fb_passthrough_ext = false;
+ fb_triangle_mesh_ext = false;
+}
+
+Viewport *OpenXRFbPassthroughExtensionWrapper::get_main_viewport() {
+ MainLoop *main_loop = OS::get_singleton()->get_main_loop();
+ if (!main_loop) {
+ print_error("Unable to retrieve main loop");
+ return nullptr;
+ }
+
+ auto *scene_tree = Object::cast_to<SceneTree>(main_loop);
+ if (!scene_tree) {
+ print_error("Unable to retrieve scene tree");
+ return nullptr;
+ }
+
+ Viewport *viewport = scene_tree->get_root()->get_viewport();
+ return viewport;
+}
+
+void OpenXRFbPassthroughExtensionWrapper::on_instance_created(const XrInstance instance) {
+ if (fb_passthrough_ext) {
+ bool result = initialize_fb_passthrough_extension(instance);
+ if (!result) {
+ print_error("Failed to initialize fb_passthrough extension");
+ fb_passthrough_ext = false;
+ }
+ }
+
+ if (fb_triangle_mesh_ext) {
+ bool result = initialize_fb_triangle_mesh_extension(instance);
+ if (!result) {
+ print_error("Failed to initialize fb_triangle_mesh extension");
+ fb_triangle_mesh_ext = false;
+ }
+ }
+
+ if (fb_passthrough_ext) {
+ OpenXRAPI::get_singleton()->register_composition_layer_provider(this);
+ }
+}
+
+bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() {
+ return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE;
+}
+
+bool OpenXRFbPassthroughExtensionWrapper::is_composition_passthrough_layer_ready() {
+ return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && composition_passthrough_layer.layerHandle != XR_NULL_HANDLE;
+}
+
+bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() {
+ if (passthrough_handle == XR_NULL_HANDLE) {
+ return false;
+ }
+
+ if (is_passthrough_enabled()) {
+ return true;
+ }
+
+ // Start the passthrough feature
+ XrResult result = xrPassthroughStartFB(passthrough_handle);
+ if (!is_valid_passthrough_result(result, "Failed to start passthrough")) {
+ stop_passthrough();
+ return false;
+ }
+
+ // Create the passthrough layer
+ result = xrCreatePassthroughLayerFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_layer_config, &passthrough_layer);
+ if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) {
+ stop_passthrough();
+ return false;
+ }
+
+ // Check if the the viewport has transparent background
+ Viewport *viewport = get_main_viewport();
+ if (viewport && !viewport->has_transparent_background()) {
+ print_error("Main viewport doesn't have transparent background! Passthrough may not properly render.");
+ }
+
+ composition_passthrough_layer.layerHandle = passthrough_layer;
+
+ return true;
+}
+
+void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) {
+ if (fb_passthrough_ext) {
+ // Create the passthrough feature and start it.
+ XrResult result = xrCreatePassthroughFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_create_info, &passthrough_handle);
+ if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to create passthrough")) {
+ passthrough_handle = XR_NULL_HANDLE;
+ return;
+ }
+ }
+}
+
+XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() {
+ if (is_composition_passthrough_layer_ready()) {
+ return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer;
+ } else {
+ return nullptr;
+ }
+}
+
+void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() {
+ if (!fb_passthrough_ext) {
+ return;
+ }
+
+ composition_passthrough_layer.layerHandle = XR_NULL_HANDLE;
+
+ XrResult result;
+ if (passthrough_layer != XR_NULL_HANDLE) {
+ // Destroy the layer
+ result = xrDestroyPassthroughLayerFB(passthrough_layer);
+ OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough layer");
+ passthrough_layer = XR_NULL_HANDLE;
+ }
+
+ if (passthrough_handle != XR_NULL_HANDLE) {
+ result = xrPassthroughPauseFB(passthrough_handle);
+ OpenXRAPI::get_singleton()->xr_result(result, "Unable to stop passthrough feature");
+ }
+}
+
+void OpenXRFbPassthroughExtensionWrapper::on_session_destroyed() {
+ if (fb_passthrough_ext) {
+ stop_passthrough();
+
+ XrResult result;
+ if (passthrough_handle != XR_NULL_HANDLE) {
+ result = xrDestroyPassthroughFB(passthrough_handle);
+ OpenXRAPI::get_singleton()->xr_result(result, "Unable to destroy passthrough feature");
+ passthrough_handle = XR_NULL_HANDLE;
+ }
+ }
+}
+
+void OpenXRFbPassthroughExtensionWrapper::on_instance_destroyed() {
+ if (fb_passthrough_ext) {
+ OpenXRAPI::get_singleton()->unregister_composition_layer_provider(this);
+ }
+ cleanup();
+}
+
+bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_passthrough_extension(const XrInstance p_instance) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+
+ EXT_INIT_XR_FUNC_V(xrCreatePassthroughFB);
+ EXT_INIT_XR_FUNC_V(xrDestroyPassthroughFB);
+ EXT_INIT_XR_FUNC_V(xrPassthroughStartFB);
+ EXT_INIT_XR_FUNC_V(xrPassthroughPauseFB);
+ EXT_INIT_XR_FUNC_V(xrCreatePassthroughLayerFB);
+ EXT_INIT_XR_FUNC_V(xrDestroyPassthroughLayerFB);
+ EXT_INIT_XR_FUNC_V(xrPassthroughLayerPauseFB);
+ EXT_INIT_XR_FUNC_V(xrPassthroughLayerResumeFB);
+ EXT_INIT_XR_FUNC_V(xrPassthroughLayerSetStyleFB);
+ EXT_INIT_XR_FUNC_V(xrCreateGeometryInstanceFB);
+ EXT_INIT_XR_FUNC_V(xrDestroyGeometryInstanceFB);
+ EXT_INIT_XR_FUNC_V(xrGeometryInstanceSetTransformFB);
+
+ return true;
+}
+
+bool OpenXRFbPassthroughExtensionWrapper::initialize_fb_triangle_mesh_extension(const XrInstance p_instance) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+
+ EXT_INIT_XR_FUNC_V(xrCreateTriangleMeshFB);
+ EXT_INIT_XR_FUNC_V(xrDestroyTriangleMeshFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshGetVertexBufferFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshGetIndexBufferFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginUpdateFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshEndUpdateFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshBeginVertexBufferUpdateFB);
+ EXT_INIT_XR_FUNC_V(xrTriangleMeshEndVertexBufferUpdateFB);
+
+ return true;
+}
diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h
new file mode 100644
index 0000000000..c01394529e
--- /dev/null
+++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h
@@ -0,0 +1,247 @@
+/**************************************************************************/
+/* openxr_fb_passthrough_extension_wrapper.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
+#define OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
+
+#include "../openxr_api.h"
+#include "../util.h"
+
+#include "openxr_composition_layer_provider.h"
+#include "openxr_extension_wrapper.h"
+
+#include <map>
+
+class Viewport;
+
+// Wrapper for the set of Facebook XR passthrough extensions.
+class OpenXRFbPassthroughExtensionWrapper : public OpenXRExtensionWrapper, public OpenXRCompositionLayerProvider {
+public:
+ OpenXRFbPassthroughExtensionWrapper();
+ ~OpenXRFbPassthroughExtensionWrapper();
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ void on_instance_created(const XrInstance instance) override;
+
+ void on_session_created(const XrSession session) override;
+
+ void on_session_destroyed() override;
+
+ void on_instance_destroyed() override;
+
+ XrCompositionLayerBaseHeader *get_composition_layer() override;
+
+ bool is_passthrough_supported() {
+ return fb_passthrough_ext;
+ }
+
+ bool is_passthrough_enabled();
+
+ bool start_passthrough();
+
+ void stop_passthrough();
+
+ static OpenXRFbPassthroughExtensionWrapper *get_singleton();
+
+private:
+ // Create a passthrough feature
+ EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughFB,
+ (XrSession), session,
+ (const XrPassthroughCreateInfoFB *), create_info,
+ (XrPassthroughFB *), feature_out)
+
+ // Destroy a previously created passthrough feature
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughFB, (XrPassthroughFB), feature)
+
+ //*** Passthrough feature state management functions *********
+ // Start the passthrough feature
+ EXT_PROTO_XRRESULT_FUNC1(xrPassthroughStartFB, (XrPassthroughFB), passthrough)
+ // Pause the passthrough feature
+ EXT_PROTO_XRRESULT_FUNC1(xrPassthroughPauseFB, (XrPassthroughFB), passthrough)
+
+ EXT_PROTO_XRRESULT_FUNC3(xrCreatePassthroughLayerFB, (XrSession), session,
+ (const XrPassthroughLayerCreateInfoFB *), config,
+ (XrPassthroughLayerFB *), layer_out)
+
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyPassthroughLayerFB, (XrPassthroughLayerFB), layer)
+
+ EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerPauseFB, (XrPassthroughLayerFB), layer)
+ EXT_PROTO_XRRESULT_FUNC1(xrPassthroughLayerResumeFB, (XrPassthroughLayerFB), layer)
+
+ // Set the style of an existing passthrough layer. If the enabled feature set
+ // doesn’t change, this is a lightweight operation that can be called in every
+ // frame to animate the style. Changes that may incur a bigger cost:
+ // - Enabling/disabling the color mapping, or changing the type of mapping
+ // (monochromatic to RGBA or back).
+ // - Changing `textureOpacityFactor` from 0 to non-zero or vice versa
+ // - Changing `edgeColor[3]` from 0 to non-zero or vice versa
+ // NOTE: For XR_FB_passthrough, all color values are treated as linear.
+ EXT_PROTO_XRRESULT_FUNC2(xrPassthroughLayerSetStyleFB,
+ (XrPassthroughLayerFB), layer,
+ (const XrPassthroughStyleFB *), style)
+
+ // Create a geometry instance to be used as a projection surface for passthrough.
+ // A geometry instance assigns a triangle mesh as part of the specified layer's
+ // projection surface.
+ // The operation is only valid if the passthrough layer's purpose has been set to
+ // `XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB`. Otherwise, the call this function will
+ // result in an error. In the specified layer, Passthrough will be visible where the view
+ // is covered by the user-specified geometries.
+ //
+ // A triangle mesh object can be instantiated multiple times - in the same or different layers'
+ // projection surface. Each instantiation has its own transformation, which
+ // can be updated using `xrGeometryInstanceSetTransformFB`.
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateGeometryInstanceFB,
+ (XrSession), session,
+ (const XrGeometryInstanceCreateInfoFB *), create_info,
+ (XrGeometryInstanceFB *), out_geometry_instance)
+
+ // Destroys a previously created geometry instance from passthrough rendering.
+ // This removes the geometry instance from passthrough rendering.
+ // The operation has no effect on other instances or the underlying mesh.
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyGeometryInstanceFB, (XrGeometryInstanceFB), instance)
+
+ // Update the transformation of a passthrough geometry instance.
+ EXT_PROTO_XRRESULT_FUNC2(xrGeometryInstanceSetTransformFB,
+ (XrGeometryInstanceFB), instance,
+ (const XrGeometryInstanceTransformFB *), transformation)
+
+ // Create a triangle mesh geometry object.
+ // Depending on the behavior flags, the mesh could be created immutable (data is assigned
+ // at creation and cannot be changed) or mutable (the mesh is created empty and can be updated
+ // by calling begin/end update functions).
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateTriangleMeshFB,
+ (XrSession), session,
+ (const XrTriangleMeshCreateInfoFB *), create_info,
+ (XrTriangleMeshFB *), out_triangle_mesh)
+
+ // Destroy an `XrTriangleMeshFB` object along with its data. The mesh buffers must not be
+ // accessed anymore after their parent mesh object has been destroyed.
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyTriangleMeshFB, (XrTriangleMeshFB), mesh)
+
+ // Retrieve a pointer to the vertex buffer. The vertex buffer is structured as an array of 3 floats
+ // per vertex representing x, y, and z: `[x0, y0, z0, x1, y1, z1, ...]`. The size of the buffer is
+ // `maxVertexCount * 3` floats. The application must call `xrTriangleMeshBeginUpdateFB` or
+ // `xrTriangleMeshBeginVertexBufferUpdateFB` before making modifications to the vertex
+ // buffer. The buffer location is guaranteed to remain constant over the lifecycle of the mesh
+ // object.
+ EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetVertexBufferFB,
+ (XrTriangleMeshFB), mesh,
+ (XrVector3f **), out_vertex_buffer)
+
+ // Retrieve the index buffer that defines the topology of the triangle mesh. Each triplet of
+ // consecutive elements point to three vertices in the vertex buffer and thus form a triangle. The
+ // size of each element is `indexElementSize` bytes, and thus the size of the buffer is
+ // `maxTriangleCount * 3 * indexElementSize` bytes. The application must call
+ // `xrTriangleMeshBeginUpdateFB` before making modifications to the index buffer. The buffer
+ // location is guaranteed to remain constant over the lifecycle of the mesh object.
+ EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshGetIndexBufferFB,
+ (XrTriangleMeshFB), mesh,
+ (uint32_t **), out_index_buffer)
+
+ // Begin updating the mesh buffer data. The application must call this function before it makes any
+ // modifications to the buffers retrieved by `xrTriangleMeshGetVertexBufferFB` and
+ // `xrTriangleMeshGetIndexBufferFB`. If only the vertex buffer needs to be updated,
+ // `xrTriangleMeshBeginVertexBufferUpdateFB` can be used instead. To commit the
+ // modifications, the application must call `xrTriangleMeshEndUpdateFB`.
+ EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshBeginUpdateFB, (XrTriangleMeshFB), mesh)
+
+ // Signal the API that the application has finished updating the mesh buffers after a call to
+ // `xrTriangleMeshBeginUpdateFB`. `vertexCount` and `triangleCount` specify the actual
+ // number of primitives that make up the mesh after the update. They must be larger than zero but
+ // smaller or equal to the maximum counts defined at create time. Buffer data beyond these counts
+ // is ignored.
+ EXT_PROTO_XRRESULT_FUNC3(xrTriangleMeshEndUpdateFB,
+ (XrTriangleMeshFB), mesh,
+ (uint32_t), vertexCount,
+ (uint32_t), triangle_count)
+
+ // Update the vertex positions of a triangle mesh. Can only be called once the mesh topology has
+ // been set using `xrTriangleMeshBeginUpdateFB`/`xrTriangleMeshEndUpdateFB`. The
+ // vertex count is defined by the last invocation to `xrTriangleMeshEndUpdateFB`. Once the
+ // modification is done, `xrTriangleMeshEndVertexBufferUpdateFB` must be called.
+ EXT_PROTO_XRRESULT_FUNC2(xrTriangleMeshBeginVertexBufferUpdateFB,
+ (XrTriangleMeshFB), mesh,
+ (uint32_t *), out_vertex_count)
+
+ // Signal the API that the contents of the vertex buffer data has been updated
+ // after a call to `xrTriangleMeshBeginVertexBufferUpdateFB`.
+ EXT_PROTO_XRRESULT_FUNC1(xrTriangleMeshEndVertexBufferUpdateFB, (XrTriangleMeshFB), mesh)
+
+ bool initialize_fb_passthrough_extension(const XrInstance instance);
+
+ bool initialize_fb_triangle_mesh_extension(const XrInstance instance);
+
+ void cleanup();
+
+ // TODO: Temporary workaround (https://github.com/GodotVR/godot_openxr/issues/138)
+ // Address a bug in the passthrough api where XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB is
+ // returned even when the operation is valid on Meta Quest devices.
+ // The issue should be addressed on that platform in OS release v37.
+ inline bool is_valid_passthrough_result(XrResult result, const char *format) {
+ return OpenXRAPI::get_singleton()->xr_result(result, format) || result == XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB;
+ }
+
+ Viewport *get_main_viewport();
+
+ bool is_composition_passthrough_layer_ready();
+
+ static OpenXRFbPassthroughExtensionWrapper *singleton;
+
+ bool fb_passthrough_ext = false; // required for any passthrough functionality
+ bool fb_triangle_mesh_ext = false; // only use for projected passthrough
+
+ XrPassthroughCreateInfoFB passthrough_create_info = {
+ XR_TYPE_PASSTHROUGH_CREATE_INFO_FB,
+ nullptr,
+ 0,
+ };
+ XrPassthroughFB passthrough_handle = XR_NULL_HANDLE;
+
+ XrPassthroughLayerCreateInfoFB passthrough_layer_config = {
+ XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB,
+ nullptr,
+ passthrough_handle,
+ XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB,
+ XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB,
+ };
+ XrPassthroughLayerFB passthrough_layer = XR_NULL_HANDLE;
+
+ XrCompositionLayerPassthroughFB composition_passthrough_layer = {
+ XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB,
+ nullptr,
+ XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT,
+ XR_NULL_HANDLE,
+ XR_NULL_HANDLE,
+ };
+};
+
+#endif // OPENXR_FB_PASSTHROUGH_EXTENSION_WRAPPER_H
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
new file mode 100644
index 0000000000..4dac7bed4f
--- /dev/null
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -0,0 +1,272 @@
+/**************************************************************************/
+/* openxr_hand_tracking_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_hand_tracking_extension.h"
+#include "../openxr_api.h"
+#include "core/string/print_string.h"
+#include "servers/xr_server.h"
+
+#include <openxr/openxr.h>
+
+OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::singleton = nullptr;
+
+OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRHandTrackingExtension::OpenXRHandTrackingExtension() {
+ singleton = this;
+
+ // Make sure this is cleared until we actually request it
+ handTrackingSystemProperties.supportsHandTracking = false;
+}
+
+OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext;
+ request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext;
+ request_extensions[XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME] = &hand_tracking_aim_state_ext;
+
+ return request_extensions;
+}
+
+void OpenXRHandTrackingExtension::on_instance_created(const XrInstance p_instance) {
+ if (hand_tracking_ext) {
+ EXT_INIT_XR_FUNC(xrCreateHandTrackerEXT);
+ EXT_INIT_XR_FUNC(xrDestroyHandTrackerEXT);
+ EXT_INIT_XR_FUNC(xrLocateHandJointsEXT);
+
+ hand_tracking_ext = xrCreateHandTrackerEXT_ptr && xrDestroyHandTrackerEXT_ptr && xrLocateHandJointsEXT_ptr;
+ }
+}
+
+void OpenXRHandTrackingExtension::on_session_destroyed() {
+ cleanup_hand_tracking();
+}
+
+void OpenXRHandTrackingExtension::on_instance_destroyed() {
+ xrCreateHandTrackerEXT_ptr = nullptr;
+ xrDestroyHandTrackerEXT_ptr = nullptr;
+ xrLocateHandJointsEXT_ptr = nullptr;
+}
+
+void *OpenXRHandTrackingExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
+ if (!hand_tracking_ext) {
+ // not supported...
+ return p_next_pointer;
+ }
+
+ handTrackingSystemProperties = {
+ XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, // type
+ p_next_pointer, // next
+ false, // supportsHandTracking
+ };
+
+ return &handTrackingSystemProperties;
+}
+
+void OpenXRHandTrackingExtension::on_state_ready() {
+ if (!handTrackingSystemProperties.supportsHandTracking) {
+ // not supported...
+ return;
+ }
+
+ // Setup our hands and reset data
+ for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) {
+ // we'll do this later
+ hand_trackers[i].is_initialized = false;
+ hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
+
+ hand_trackers[i].aimState.aimPose = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
+ hand_trackers[i].aimState.pinchStrengthIndex = 0.0;
+ hand_trackers[i].aimState.pinchStrengthMiddle = 0.0;
+ hand_trackers[i].aimState.pinchStrengthRing = 0.0;
+ hand_trackers[i].aimState.pinchStrengthLittle = 0.0;
+
+ hand_trackers[i].locations.isActive = false;
+
+ for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; j++) {
+ hand_trackers[i].joint_locations[j] = { 0, { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }, 0.0 };
+ hand_trackers[i].joint_velocities[j] = { 0, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
+ }
+ }
+}
+
+void OpenXRHandTrackingExtension::on_process() {
+ if (!handTrackingSystemProperties.supportsHandTracking) {
+ // not supported...
+ return;
+ }
+
+ // process our hands
+ const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render
+
+ XrResult result;
+
+ for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) {
+ if (hand_trackers[i].hand_tracker == XR_NULL_HANDLE) {
+ XrHandTrackerCreateInfoEXT createInfo = {
+ XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, // type
+ nullptr, // next
+ i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, // hand
+ XR_HAND_JOINT_SET_DEFAULT_EXT, // handJointSet
+ };
+
+ result = xrCreateHandTrackerEXT(OpenXRAPI::get_singleton()->get_session(), &createInfo, &hand_trackers[i].hand_tracker);
+ if (XR_FAILED(result)) {
+ // not successful? then we do nothing.
+ print_line("OpenXR: Failed to obtain hand tracking information [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ hand_trackers[i].is_initialized = false;
+ } else {
+ void *next_pointer = nullptr;
+ if (hand_tracking_aim_state_ext) {
+ hand_trackers[i].aimState.type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
+ hand_trackers[i].aimState.next = next_pointer;
+ hand_trackers[i].aimState.status = 0;
+ hand_trackers[i].aimState.aimPose = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
+ hand_trackers[i].aimState.pinchStrengthIndex = 0.0;
+ hand_trackers[i].aimState.pinchStrengthMiddle = 0.0;
+ hand_trackers[i].aimState.pinchStrengthRing = 0.0;
+ hand_trackers[i].aimState.pinchStrengthLittle = 0.0;
+
+ next_pointer = &hand_trackers[i].aimState;
+ }
+
+ hand_trackers[i].velocities.type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
+ hand_trackers[i].velocities.next = next_pointer;
+ hand_trackers[i].velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
+ hand_trackers[i].velocities.jointVelocities = hand_trackers[i].joint_velocities;
+ next_pointer = &hand_trackers[i].velocities;
+
+ hand_trackers[i].locations.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
+ hand_trackers[i].locations.next = next_pointer;
+ hand_trackers[i].locations.isActive = false;
+ hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
+ hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations;
+
+ hand_trackers[i].is_initialized = true;
+ }
+ }
+
+ if (hand_trackers[i].is_initialized) {
+ void *next_pointer = nullptr;
+
+ XrHandJointsMotionRangeInfoEXT motionRangeInfo;
+
+ if (hand_motion_range_ext) {
+ motionRangeInfo.type = XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT;
+ motionRangeInfo.next = next_pointer;
+ motionRangeInfo.handJointsMotionRange = hand_trackers[i].motion_range;
+
+ next_pointer = &motionRangeInfo;
+ }
+
+ XrHandJointsLocateInfoEXT locateInfo = {
+ XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, // type
+ next_pointer, // next
+ OpenXRAPI::get_singleton()->get_play_space(), // baseSpace
+ time, // time
+ };
+
+ result = xrLocateHandJointsEXT(hand_trackers[i].hand_tracker, &locateInfo, &hand_trackers[i].locations);
+ if (XR_FAILED(result)) {
+ // not successful? then we do nothing.
+ print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ continue;
+ }
+
+ // For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large
+ const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose;
+ if (
+ !hand_trackers[i].locations.isActive || isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
+ hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
+ }
+
+ /* TODO change this to managing the controller from openxr_interface
+ if (hand_tracking_aim_state_ext && hand_trackers[i].locations.isActive && check_bit(XR_HAND_TRACKING_AIM_VALID_BIT_FB, hand_trackers[i].aimState.status)) {
+ // Controllers are updated based on the aim state's pose and pinches' strength
+ if (hand_trackers[i].aim_state_godot_controller == -1) {
+ hand_trackers[i].aim_state_godot_controller =
+ arvr_api->godot_arvr_add_controller(
+ const_cast<char *>(hand_controller_names[i]),
+ i + HAND_CONTROLLER_ID_OFFSET,
+ true,
+ true);
+ }
+ }
+ */
+ }
+ }
+}
+
+void OpenXRHandTrackingExtension::on_state_stopping() {
+ // cleanup
+ cleanup_hand_tracking();
+}
+
+void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) {
+ if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) {
+ xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker);
+
+ hand_trackers[i].is_initialized = false;
+ hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
+ }
+ }
+}
+
+bool OpenXRHandTrackingExtension::get_active() {
+ return handTrackingSystemProperties.supportsHandTracking;
+}
+
+const OpenXRHandTrackingExtension::HandTracker *OpenXRHandTrackingExtension::get_hand_tracker(uint32_t p_hand) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, nullptr);
+
+ return &hand_trackers[p_hand];
+}
+
+XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(uint32_t p_hand) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT);
+
+ return hand_trackers[p_hand].motion_range;
+}
+
+void OpenXRHandTrackingExtension::set_motion_range(uint32_t p_hand, XrHandJointsMotionRangeEXT p_motion_range) {
+ ERR_FAIL_UNSIGNED_INDEX(p_hand, MAX_OPENXR_TRACKED_HANDS);
+ hand_trackers[p_hand].motion_range = p_motion_range;
+}
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h
new file mode 100644
index 0000000000..5ab44ed969
--- /dev/null
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* openxr_hand_tracking_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_HAND_TRACKING_EXTENSION_H
+#define OPENXR_HAND_TRACKING_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+#include "../util.h"
+
+#define MAX_OPENXR_TRACKED_HANDS 2
+
+class OpenXRHandTrackingExtension : public OpenXRExtensionWrapper {
+public:
+ struct HandTracker {
+ bool is_initialized = false;
+ XrHandJointsMotionRangeEXT motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
+
+ XrHandTrackerEXT hand_tracker = XR_NULL_HANDLE;
+ XrHandJointLocationEXT joint_locations[XR_HAND_JOINT_COUNT_EXT];
+ XrHandJointVelocityEXT joint_velocities[XR_HAND_JOINT_COUNT_EXT];
+
+ XrHandTrackingAimStateFB aimState;
+ XrHandJointVelocitiesEXT velocities;
+ XrHandJointLocationsEXT locations;
+ };
+
+ static OpenXRHandTrackingExtension *get_singleton();
+
+ OpenXRHandTrackingExtension();
+ virtual ~OpenXRHandTrackingExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void on_instance_destroyed() override;
+ virtual void on_session_destroyed() override;
+
+ virtual void *set_system_properties_and_get_next_pointer(void *p_next_pointer) override;
+ virtual void on_state_ready() override;
+ virtual void on_process() override;
+ virtual void on_state_stopping() override;
+
+ bool get_active();
+ const HandTracker *get_hand_tracker(uint32_t p_hand) const;
+
+ XrHandJointsMotionRangeEXT get_motion_range(uint32_t p_hand) const;
+ void set_motion_range(uint32_t p_hand, XrHandJointsMotionRangeEXT p_motion_range);
+
+private:
+ static OpenXRHandTrackingExtension *singleton;
+
+ // state
+ XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties;
+ HandTracker hand_trackers[MAX_OPENXR_TRACKED_HANDS]; // Fixed for left and right hand
+
+ // related extensions
+ bool hand_tracking_ext = false;
+ bool hand_motion_range_ext = false;
+ bool hand_tracking_aim_state_ext = false;
+
+ // functions
+ void cleanup_hand_tracking();
+
+ // OpenXR API call wrappers
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateHandTrackerEXT, (XrSession), p_session, (const XrHandTrackerCreateInfoEXT *), p_createInfo, (XrHandTrackerEXT *), p_handTracker)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyHandTrackerEXT, (XrHandTrackerEXT), p_handTracker)
+ EXT_PROTO_XRRESULT_FUNC3(xrLocateHandJointsEXT, (XrHandTrackerEXT), p_handTracker, (const XrHandJointsLocateInfoEXT *), p_locateInfo, (XrHandJointLocationsEXT *), p_locations)
+};
+
+#endif // OPENXR_HAND_TRACKING_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_htc_controller_extension.cpp b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
new file mode 100644
index 0000000000..4d141b0695
--- /dev/null
+++ b/modules/openxr/extensions/openxr_htc_controller_extension.cpp
@@ -0,0 +1,129 @@
+/**************************************************************************/
+/* openxr_htc_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+HashMap<String, bool *> OpenXRHTCControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_COSMOS];
+ request_extensions[XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available[HTC_VIVE_FOCUS3];
+
+ return request_extensions;
+}
+
+bool OpenXRHTCControllerExtension::is_available(HTCControllers p_type) {
+ return available[p_type];
+}
+
+void OpenXRHTCControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // HTC Vive Cosmos controller
+ metadata->register_interaction_profile("Vive Cosmos controller", "/interaction_profiles/htc/vive_cosmos_controller", XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Shoulder click", "/user/hand/left", "/user/hand/left/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Shoulder click", "/user/hand/right", "/user/hand/right/input/shoulder/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Squeeze click", "/user/hand/right", "/user/hand/right/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_cosmos_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // HTC Vive Focus 3 controller
+ metadata->register_interaction_profile("Vive Focus 3 controller", "/interaction_profiles/htc/vive_focus3_controller", XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch ", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze touch", "/user/hand/left", "/user/hand/left/input/squeeze/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze click", "/user/hand/right", "/user/hand/right/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Squeeze touch", "/user/hand/right", "/user/hand/right/input/squeeze/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Thumbrest touch", "/user/hand/right", "/user/hand/right/input/thumbrest/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_focus3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/mono/glue/arguments_vector.h b/modules/openxr/extensions/openxr_htc_controller_extension.h
index 4405809887..2aaeac2577 100644
--- a/modules/mono/glue/arguments_vector.h
+++ b/modules/openxr/extensions/openxr_htc_controller_extension.h
@@ -1,67 +1,55 @@
-/*************************************************************************/
-/* arguments_vector.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 ARGUMENTS_VECTOR_H
-#define ARGUMENTS_VECTOR_H
-
-#include "core/os/memory.h"
-
-template <typename T, int POOL_SIZE = 5>
-struct ArgumentsVector {
-private:
- T pool[POOL_SIZE];
- T *_ptr = nullptr;
- int size;
+/**************************************************************************/
+/* openxr_htc_controller_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_CONTROLLER_EXTENSION_H
+#define OPENXR_HTC_CONTROLLER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRHTCControllerExtension : public OpenXRExtensionWrapper {
+public:
+ enum HTCControllers {
+ // Note, HTC Vive Wand controllers are part of the core spec and not part of our extension.
+ HTC_VIVE_COSMOS,
+ HTC_VIVE_FOCUS3,
+ HTC_MAX_CONTROLLERS
+ };
- ArgumentsVector() = delete;
- ArgumentsVector(const ArgumentsVector &) = delete;
+ virtual HashMap<String, bool *> get_requested_extensions() override;
-public:
- T *ptr() { return _ptr; }
- T &get(int p_idx) { return _ptr[p_idx]; }
- void set(int p_idx, const T &p_value) { _ptr[p_idx] = p_value; }
+ bool is_available(HTCControllers p_type);
- explicit ArgumentsVector(int p_size) :
- size(p_size) {
- if (p_size <= POOL_SIZE) {
- _ptr = pool;
- } else {
- _ptr = memnew_arr(T, p_size);
- }
- }
+ virtual void on_register_metadata() override;
- ~ArgumentsVector() {
- if (size > POOL_SIZE) {
- memdelete_arr(_ptr);
- }
- }
+private:
+ bool available[HTC_MAX_CONTROLLERS] = { false, false };
};
-#endif // ARGUMENTS_VECTOR_H
+#endif // OPENXR_HTC_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
index 302acf4e30..1d2bd11a00 100644
--- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
@@ -1,57 +1,183 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_htc_vive_tracker_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "../action_map/openxr_interaction_profile_meta_data.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;
+HashMap<String, bool *> OpenXRHTCViveTrackerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
request_extensions[XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME] = &available;
-}
-OpenXRHTCViveTrackerExtension::~OpenXRHTCViveTrackerExtension() {
- singleton = nullptr;
+ return request_extensions;
}
bool OpenXRHTCViveTrackerExtension::is_available() {
return available;
}
+void OpenXRHTCViveTrackerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // 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.
+ metadata->register_interaction_profile("HTC Vive tracker", "/interaction_profiles/htc/vive_tracker_htcx", XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Menu click", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trigger click", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Squeeze click", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad click", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Trackpad touch", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ // register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Grip pose", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ // metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/handheld_object", "/user/vive_tracker_htcx/role/handheld_object/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/left_foot/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/right_foot/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/left_shoulder/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/right_shoulder/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/left_elbow/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/right_elbow/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/left_knee/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/right_knee/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/waist/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/chest/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/camera/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/htc/vive_tracker_htcx", "Haptic output", "/user/vive_tracker_htcx/role/keyboard", "/user/vive_tracker_htcx/role/keyboard/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
+
bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &event) {
switch (event.type) {
case XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX: {
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
index 7f37351f27..b51398fd4e 100644
--- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_htc_vive_tracker_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -35,17 +35,14 @@
class OpenXRHTCViveTrackerExtension : public OpenXRExtensionWrapper {
public:
- static OpenXRHTCViveTrackerExtension *get_singleton();
-
- OpenXRHTCViveTrackerExtension(OpenXRAPI *p_openxr_api);
- virtual ~OpenXRHTCViveTrackerExtension() override;
+ virtual HashMap<String, bool *> get_requested_extensions() override;
bool is_available();
+
+ virtual void on_register_metadata() override;
virtual bool on_event_polled(const XrEventDataBuffer &event) override;
private:
- static OpenXRHTCViveTrackerExtension *singleton;
-
bool available = false;
};
diff --git a/modules/openxr/extensions/openxr_huawei_controller_extension.cpp b/modules/openxr/extensions/openxr_huawei_controller_extension.cpp
new file mode 100644
index 0000000000..aff92ee651
--- /dev/null
+++ b/modules/openxr/extensions/openxr_huawei_controller_extension.cpp
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* openxr_huawei_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_huawei_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+HashMap<String, bool *> OpenXRHuaweiControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRHuaweiControllerExtension::is_available() {
+ return available;
+}
+
+void OpenXRHuaweiControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Huawei controller
+ metadata->register_interaction_profile("Huawei controller", "/interaction_profiles/huawei/controller", XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Home click", "/user/hand/left", "/user/hand/left/input/home/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Home click", "/user/hand/right", "/user/hand/right/input/home/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Back click", "/user/hand/left", "/user/hand/left/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Back click", "/user/hand/right", "/user/hand/right/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Volume up click", "/user/hand/left", "/user/hand/left/input/volume_up/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Volume up click", "/user/hand/right", "/user/hand/right/input/volume_up/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Volume down click", "/user/hand/left", "/user/hand/left/input/volume_down/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Volume down click", "/user/hand/right", "/user/hand/right/input/volume_down/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad", "/user/hand/left", "/user/hand/left/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad click", "/user/hand/left", "/user/hand/left/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad touch", "/user/hand/left", "/user/hand/left/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad", "/user/hand/right", "/user/hand/right/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad click", "/user/hand/right", "/user/hand/right/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Trackpad touch", "/user/hand/right", "/user/hand/right/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/huawei/controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_huawei_controller_extension.h b/modules/openxr/extensions/openxr_huawei_controller_extension.h
new file mode 100644
index 0000000000..d4847fb0fd
--- /dev/null
+++ b/modules/openxr/extensions/openxr_huawei_controller_extension.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* openxr_huawei_controller_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_HUAWEI_CONTROLLER_EXTENSION_H
+#define OPENXR_HUAWEI_CONTROLLER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRHuaweiControllerExtension : public OpenXRExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available = false;
+};
+
+#endif // OPENXR_HUAWEI_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp
new file mode 100644
index 0000000000..0d201161f1
--- /dev/null
+++ b/modules/openxr/extensions/openxr_opengl_extension.cpp
@@ -0,0 +1,416 @@
+/**************************************************************************/
+/* openxr_opengl_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GLES3_ENABLED
+
+#include "../extensions/openxr_opengl_extension.h"
+#include "../openxr_util.h"
+#include "drivers/gles3/effects/copy_effects.h"
+#include "drivers/gles3/storage/texture_storage.h"
+#include "servers/rendering/rendering_server_globals.h"
+#include "servers/rendering_server.h"
+
+HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+#ifdef ANDROID_ENABLED
+ request_extensions[XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME] = nullptr;
+#else
+ request_extensions[XR_KHR_OPENGL_ENABLE_EXTENSION_NAME] = nullptr;
+#endif
+
+ return request_extensions;
+}
+
+void OpenXROpenGLExtension::on_instance_created(const XrInstance p_instance) {
+ // Obtain pointers to functions we're accessing here.
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+#ifdef ANDROID_ENABLED
+ EXT_INIT_XR_FUNC(xrGetOpenGLESGraphicsRequirementsKHR);
+#else
+ EXT_INIT_XR_FUNC(xrGetOpenGLGraphicsRequirementsKHR);
+#endif
+ EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
+}
+
+bool OpenXROpenGLExtension::check_graphics_api_support(XrVersion p_desired_version) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
+
+ XrSystemId system_id = OpenXRAPI::get_singleton()->get_system_id();
+ XrInstance instance = OpenXRAPI::get_singleton()->get_instance();
+
+#ifdef ANDROID_ENABLED
+ XrGraphicsRequirementsOpenGLESKHR opengl_requirements;
+ opengl_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR;
+ opengl_requirements.next = nullptr;
+
+ XrResult result = xrGetOpenGLESGraphicsRequirementsKHR(instance, system_id, &opengl_requirements);
+ if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get OpenGL graphics requirements!")) {
+ return false;
+ }
+#else
+ XrGraphicsRequirementsOpenGLKHR opengl_requirements;
+ opengl_requirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR;
+ opengl_requirements.next = nullptr;
+
+ XrResult result = xrGetOpenGLGraphicsRequirementsKHR(instance, system_id, &opengl_requirements);
+ if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to get OpenGL graphics requirements!")) {
+ return false;
+ }
+#endif
+
+ if (p_desired_version < opengl_requirements.minApiVersionSupported) {
+ print_line("OpenXR: Requested OpenGL version does not meet the minimum version this runtime supports.");
+ print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
+ print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.minApiVersionSupported));
+ print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.maxApiVersionSupported));
+ return false;
+ }
+
+ if (p_desired_version > opengl_requirements.maxApiVersionSupported) {
+ print_line("OpenXR: Requested OpenGL version exceeds the maximum version this runtime has been tested on and is known to support.");
+ print_line("- desired_version ", OpenXRUtil::make_xr_version_string(p_desired_version));
+ print_line("- minApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.minApiVersionSupported));
+ print_line("- maxApiVersionSupported ", OpenXRUtil::make_xr_version_string(opengl_requirements.maxApiVersionSupported));
+ }
+
+ return true;
+}
+
+#ifdef WIN32
+XrGraphicsBindingOpenGLWin32KHR OpenXROpenGLExtension::graphics_binding_gl;
+#elif ANDROID_ENABLED
+XrGraphicsBindingOpenGLESAndroidKHR OpenXROpenGLExtension::graphics_binding_gl;
+#else
+XrGraphicsBindingOpenGLXlibKHR OpenXROpenGLExtension::graphics_binding_gl;
+#endif
+
+void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
+ XrVersion desired_version = XR_MAKE_VERSION(3, 3, 0);
+
+ if (!check_graphics_api_support(desired_version)) {
+ print_line("OpenXR: Trying to initialize with OpenGL anyway...");
+ //return p_next_pointer;
+ }
+
+ DisplayServer *display_server = DisplayServer::get_singleton();
+
+#ifdef WIN32
+ graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR,
+ graphics_binding_gl.next = p_next_pointer;
+
+ graphics_binding_gl.hDC = (HDC)display_server->window_get_native_handle(DisplayServer::WINDOW_VIEW);
+ graphics_binding_gl.hGLRC = (HGLRC)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
+#elif ANDROID_ENABLED
+ graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
+ graphics_binding_gl.next = p_next_pointer;
+
+ graphics_binding_gl.display = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
+ graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
+ graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
+#else
+ graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
+ graphics_binding_gl.next = p_next_pointer;
+
+ void *display_handle = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
+ void *glxcontext_handle = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
+ void *glxdrawable_handle = (void *)display_server->window_get_native_handle(DisplayServer::WINDOW_HANDLE);
+
+ graphics_binding_gl.xDisplay = (Display *)display_handle;
+ graphics_binding_gl.glxContext = (GLXContext)glxcontext_handle;
+ graphics_binding_gl.glxDrawable = (GLXDrawable)glxdrawable_handle;
+
+ // spec says to use proper values but runtimes don't care
+ graphics_binding_gl.visualid = 0;
+ graphics_binding_gl.glxFBConfig = 0;
+#endif
+
+ return &graphics_binding_gl;
+}
+
+void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
+ p_usable_swap_chains.push_back(GL_RGBA8);
+ p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
+}
+
+void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
+ p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT32F);
+ p_usable_depth_formats.push_back(GL_DEPTH24_STENCIL8);
+ p_usable_depth_formats.push_back(GL_DEPTH32F_STENCIL8);
+ p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24);
+}
+
+bool OpenXROpenGLExtension::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) {
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+ ERR_FAIL_NULL_V(texture_storage, false);
+
+ uint32_t swapchain_length;
+ XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to get swapchaim image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ return false;
+ }
+
+#ifdef ANDROID_ENABLED
+ XrSwapchainImageOpenGLESKHR *images = (XrSwapchainImageOpenGLESKHR *)memalloc(sizeof(XrSwapchainImageOpenGLESKHR) * swapchain_length);
+#else
+ XrSwapchainImageOpenGLKHR *images = (XrSwapchainImageOpenGLKHR *)memalloc(sizeof(XrSwapchainImageOpenGLKHR) * swapchain_length);
+#endif
+ ERR_FAIL_NULL_V_MSG(images, false, "OpenXR Couldn't allocate memory for swap chain image");
+
+ for (uint64_t i = 0; i < swapchain_length; i++) {
+#ifdef ANDROID_ENABLED
+ images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
+#else
+ images[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
+#endif
+ images[i].next = nullptr;
+ images[i].image = 0;
+ }
+
+ result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to get swapchaim images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
+ memfree(images);
+ return false;
+ }
+
+ SwapchainGraphicsData *data = memnew(SwapchainGraphicsData);
+ if (data == nullptr) {
+ print_line("OpenXR: Failed to allocate memory for swapchain data");
+ memfree(images);
+ return false;
+ }
+ *r_swapchain_graphics_data = data;
+ data->is_multiview = (p_array_size > 1);
+
+ Image::Format format = Image::FORMAT_RGBA8;
+
+ Vector<RID> texture_rids;
+
+ for (uint64_t i = 0; i < swapchain_length; i++) {
+ RID texture_rid = texture_storage->texture_create_external(
+ p_array_size == 1 ? GLES3::Texture::TYPE_2D : GLES3::Texture::TYPE_LAYERED,
+ format,
+ images[i].image,
+ p_width,
+ p_height,
+ 1,
+ p_array_size);
+
+ texture_rids.push_back(texture_rid);
+ }
+
+ data->texture_rids = texture_rids;
+
+ memfree(images);
+
+ return true;
+}
+
+bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) {
+ XrMatrix4x4f matrix;
+ XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far);
+
+ for (int j = 0; j < 4; j++) {
+ for (int i = 0; i < 4; i++) {
+ r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
+ }
+ }
+
+ return true;
+}
+
+RID OpenXROpenGLExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
+ SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
+ ERR_FAIL_NULL_V(data, RID());
+
+ ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
+ return data->texture_rids[p_image_index];
+}
+
+void OpenXROpenGLExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
+ if (*p_swapchain_graphics_data == nullptr) {
+ return;
+ }
+
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+ ERR_FAIL_NULL(texture_storage);
+
+ SwapchainGraphicsData *data = (SwapchainGraphicsData *)*p_swapchain_graphics_data;
+
+ for (int i = 0; i < data->texture_rids.size(); i++) {
+ texture_storage->texture_free(data->texture_rids[i]);
+ }
+ data->texture_rids.clear();
+
+ memdelete(data);
+ *p_swapchain_graphics_data = nullptr;
+}
+
+#define ENUM_TO_STRING_CASE(e) \
+ case e: { \
+ return String(#e); \
+ } break;
+
+String OpenXROpenGLExtension::get_swapchain_format_name(int64_t p_swapchain_format) const {
+ // These are somewhat different per platform, will need to weed some stuff out...
+ switch (p_swapchain_format) {
+#ifdef ANDROID_ENABLED
+ // using definitions from GLES3/gl3.h
+
+ ENUM_TO_STRING_CASE(GL_RGBA4)
+ ENUM_TO_STRING_CASE(GL_RGB5_A1)
+ ENUM_TO_STRING_CASE(GL_RGB565)
+ ENUM_TO_STRING_CASE(GL_RGB8)
+ ENUM_TO_STRING_CASE(GL_RGBA8)
+ ENUM_TO_STRING_CASE(GL_RGB10_A2)
+ ENUM_TO_STRING_CASE(GL_RGBA32F)
+ ENUM_TO_STRING_CASE(GL_RGB32F)
+ ENUM_TO_STRING_CASE(GL_RGBA16F)
+ ENUM_TO_STRING_CASE(GL_RGB16F)
+ ENUM_TO_STRING_CASE(GL_R11F_G11F_B10F)
+ ENUM_TO_STRING_CASE(GL_UNSIGNED_INT_10F_11F_11F_REV)
+ ENUM_TO_STRING_CASE(GL_RGB9_E5)
+ ENUM_TO_STRING_CASE(GL_UNSIGNED_INT_5_9_9_9_REV)
+ ENUM_TO_STRING_CASE(GL_RGBA32UI)
+ ENUM_TO_STRING_CASE(GL_RGB32UI)
+ ENUM_TO_STRING_CASE(GL_RGBA16UI)
+ ENUM_TO_STRING_CASE(GL_RGB16UI)
+ ENUM_TO_STRING_CASE(GL_RGBA8UI)
+ ENUM_TO_STRING_CASE(GL_RGB8UI)
+ ENUM_TO_STRING_CASE(GL_RGBA32I)
+ ENUM_TO_STRING_CASE(GL_RGB32I)
+ ENUM_TO_STRING_CASE(GL_RGBA16I)
+ ENUM_TO_STRING_CASE(GL_RGB16I)
+ ENUM_TO_STRING_CASE(GL_RGBA8I)
+ ENUM_TO_STRING_CASE(GL_RGB8I)
+ ENUM_TO_STRING_CASE(GL_RG)
+ ENUM_TO_STRING_CASE(GL_RG_INTEGER)
+ ENUM_TO_STRING_CASE(GL_R8)
+ ENUM_TO_STRING_CASE(GL_RG8)
+ ENUM_TO_STRING_CASE(GL_R16F)
+ ENUM_TO_STRING_CASE(GL_R32F)
+ ENUM_TO_STRING_CASE(GL_RG16F)
+ ENUM_TO_STRING_CASE(GL_RG32F)
+ ENUM_TO_STRING_CASE(GL_R8I)
+ ENUM_TO_STRING_CASE(GL_R8UI)
+ ENUM_TO_STRING_CASE(GL_R16I)
+ ENUM_TO_STRING_CASE(GL_R16UI)
+ ENUM_TO_STRING_CASE(GL_R32I)
+ ENUM_TO_STRING_CASE(GL_R32UI)
+ ENUM_TO_STRING_CASE(GL_RG8I)
+ ENUM_TO_STRING_CASE(GL_RG8UI)
+ ENUM_TO_STRING_CASE(GL_RG16I)
+ ENUM_TO_STRING_CASE(GL_RG16UI)
+ ENUM_TO_STRING_CASE(GL_RG32I)
+ ENUM_TO_STRING_CASE(GL_RG32UI)
+ ENUM_TO_STRING_CASE(GL_R8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RG8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGB8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGBA8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGB10_A2UI)
+ ENUM_TO_STRING_CASE(GL_SRGB)
+ ENUM_TO_STRING_CASE(GL_SRGB8)
+ ENUM_TO_STRING_CASE(GL_SRGB8_ALPHA8)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_R11_EAC)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_SIGNED_R11_EAC)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_RG11_EAC)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_SIGNED_RG11_EAC)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_RGB8_ETC2)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_ETC2)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
+ ENUM_TO_STRING_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT16)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT24)
+ ENUM_TO_STRING_CASE(GL_DEPTH24_STENCIL8)
+
+#else
+ // using definitions from GLAD
+ ENUM_TO_STRING_CASE(GL_R8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RG8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGB8_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGBA8_SNORM)
+ ENUM_TO_STRING_CASE(GL_R16_SNORM)
+ ENUM_TO_STRING_CASE(GL_RG16_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGB16_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGBA16_SNORM)
+ ENUM_TO_STRING_CASE(GL_RGB4)
+ ENUM_TO_STRING_CASE(GL_RGB5)
+ ENUM_TO_STRING_CASE(GL_RGB8)
+ ENUM_TO_STRING_CASE(GL_RGB10)
+ ENUM_TO_STRING_CASE(GL_RGB12)
+ ENUM_TO_STRING_CASE(GL_RGB16)
+ ENUM_TO_STRING_CASE(GL_RGBA2)
+ ENUM_TO_STRING_CASE(GL_RGBA4)
+ ENUM_TO_STRING_CASE(GL_RGB5_A1)
+ ENUM_TO_STRING_CASE(GL_RGBA8)
+ ENUM_TO_STRING_CASE(GL_RGB10_A2)
+ ENUM_TO_STRING_CASE(GL_RGBA12)
+ ENUM_TO_STRING_CASE(GL_RGBA16)
+ ENUM_TO_STRING_CASE(GL_RGBA32F)
+ ENUM_TO_STRING_CASE(GL_RGB32F)
+ ENUM_TO_STRING_CASE(GL_RGBA16F)
+ ENUM_TO_STRING_CASE(GL_RGB16F)
+ ENUM_TO_STRING_CASE(GL_RGBA32UI)
+ ENUM_TO_STRING_CASE(GL_RGB32UI)
+ ENUM_TO_STRING_CASE(GL_RGBA16UI)
+ ENUM_TO_STRING_CASE(GL_RGB16UI)
+ ENUM_TO_STRING_CASE(GL_RGBA8UI)
+ ENUM_TO_STRING_CASE(GL_RGB8UI)
+ ENUM_TO_STRING_CASE(GL_RGBA32I)
+ ENUM_TO_STRING_CASE(GL_RGB32I)
+ ENUM_TO_STRING_CASE(GL_RGBA16I)
+ ENUM_TO_STRING_CASE(GL_RGB16I)
+ ENUM_TO_STRING_CASE(GL_RGBA8I)
+ ENUM_TO_STRING_CASE(GL_RGB8I)
+ ENUM_TO_STRING_CASE(GL_RGB10_A2UI)
+ ENUM_TO_STRING_CASE(GL_SRGB)
+ ENUM_TO_STRING_CASE(GL_SRGB8)
+ ENUM_TO_STRING_CASE(GL_SRGB_ALPHA)
+ ENUM_TO_STRING_CASE(GL_SRGB8_ALPHA8)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT16)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT24)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT32)
+ ENUM_TO_STRING_CASE(GL_DEPTH24_STENCIL8)
+ ENUM_TO_STRING_CASE(GL_R11F_G11F_B10F)
+ ENUM_TO_STRING_CASE(GL_DEPTH_COMPONENT32F)
+ ENUM_TO_STRING_CASE(GL_DEPTH32F_STENCIL8)
+#endif
+ default: {
+ return String("Swapchain format 0x") + String::num_int64(p_swapchain_format, 16);
+ } break;
+ }
+}
+
+#endif // GLES3_ENABLED
diff --git a/modules/openxr/extensions/openxr_opengl_extension.h b/modules/openxr/extensions/openxr_opengl_extension.h
new file mode 100644
index 0000000000..03a640cbfb
--- /dev/null
+++ b/modules/openxr/extensions/openxr_opengl_extension.h
@@ -0,0 +1,118 @@
+/**************************************************************************/
+/* openxr_opengl_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_OPENGL_EXTENSION_H
+#define OPENXR_OPENGL_EXTENSION_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/vector.h"
+#include "openxr_extension_wrapper.h"
+
+#include "../openxr_api.h"
+#include "../util.h"
+
+#ifdef ANDROID_ENABLED
+#define XR_USE_GRAPHICS_API_OPENGL_ES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#else
+#define XR_USE_GRAPHICS_API_OPENGL
+#endif
+
+#ifdef WINDOWS_ENABLED
+// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
+// however due to the way the openxr headers are put together, we have no choice.
+#include <windows.h>
+#endif
+
+#ifdef X11_ENABLED
+#include OPENGL_INCLUDE_H
+#define GL_GLEXT_PROTOTYPES 1
+#define GL3_PROTOTYPES 1
+#include "thirdparty/glad/glad/gl.h"
+#include "thirdparty/glad/glad/glx.h"
+#include <X11/Xlib.h>
+#endif
+
+#ifdef ANDROID_ENABLED
+// The jobject type from jni.h is used by openxr_platform.h on Android.
+#include <jni.h>
+#endif
+
+// include platform dependent structs
+#include <openxr/openxr_platform.h>
+
+class OpenXROpenGLExtension : public OpenXRGraphicsExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ virtual void on_instance_created(const XrInstance p_instance) override;
+ virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
+
+ virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
+ virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
+ 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, Projection &r_camera_matrix) override;
+ virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
+
+private:
+ static OpenXROpenGLExtension *singleton;
+
+#ifdef WIN32
+ static XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl;
+#elif ANDROID_ENABLED
+ static XrGraphicsBindingOpenGLESAndroidKHR graphics_binding_gl;
+#else
+ static XrGraphicsBindingOpenGLXlibKHR graphics_binding_gl;
+#endif
+
+ struct SwapchainGraphicsData {
+ bool is_multiview;
+ Vector<RID> texture_rids;
+ };
+
+ bool check_graphics_api_support(XrVersion p_desired_version);
+
+#ifdef ANDROID_ENABLED
+ EXT_PROTO_XRRESULT_FUNC3(xrGetOpenGLESGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsOpenGLESKHR *), p_graphics_requirements)
+#else
+ EXT_PROTO_XRRESULT_FUNC3(xrGetOpenGLGraphicsRequirementsKHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsOpenGLKHR *), p_graphics_requirements)
+#endif
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
+};
+
+#endif // GLES3_ENABLED
+
+#endif // OPENXR_OPENGL_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_palm_pose_extension.cpp b/modules/openxr/extensions/openxr_palm_pose_extension.cpp
new file mode 100644
index 0000000000..75117e8db3
--- /dev/null
+++ b/modules/openxr/extensions/openxr_palm_pose_extension.cpp
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* openxr_palm_pose_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_palm_pose_extension.h"
+#include "core/string/print_string.h"
+
+OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::singleton = nullptr;
+
+OpenXRPalmPoseExtension *OpenXRPalmPoseExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRPalmPoseExtension::OpenXRPalmPoseExtension() {
+ singleton = this;
+}
+
+OpenXRPalmPoseExtension::~OpenXRPalmPoseExtension() {
+ singleton = nullptr;
+}
+
+HashMap<String, bool *> OpenXRPalmPoseExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_EXT_PALM_POSE_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRPalmPoseExtension::is_available() {
+ return available;
+}
diff --git a/modules/openxr/extensions/openxr_palm_pose_extension.h b/modules/openxr/extensions/openxr_palm_pose_extension.h
new file mode 100644
index 0000000000..975b8b5d0c
--- /dev/null
+++ b/modules/openxr/extensions/openxr_palm_pose_extension.h
@@ -0,0 +1,53 @@
+/**************************************************************************/
+/* openxr_palm_pose_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_PALM_POSE_EXTENSION_H
+#define OPENXR_PALM_POSE_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRPalmPoseExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRPalmPoseExtension *get_singleton();
+
+ OpenXRPalmPoseExtension();
+ virtual ~OpenXRPalmPoseExtension() override;
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+private:
+ static OpenXRPalmPoseExtension *singleton;
+
+ bool available = false;
+};
+
+#endif // OPENXR_PALM_POSE_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.cpp b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
new file mode 100644
index 0000000000..f2fcf22ce2
--- /dev/null
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.cpp
@@ -0,0 +1,98 @@
+/**************************************************************************/
+/* openxr_pico_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_pico_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+// Pico controllers are not part of the OpenXR spec at the time of writing this
+// code. We'll hardcode the extension name that is used internally, verified by
+// tests on the Pico 4. Note that later versions of the Pico 4 and OpenXR
+// runtime on Pico might use a different, standardized extension name.
+#ifndef XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME
+#define XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_PICO_controller_interaction"
+#endif
+
+HashMap<String, bool *> OpenXRPicoControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRPicoControllerExtension::is_available() {
+ return available;
+}
+
+void OpenXRPicoControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Pico controller (Pico 4 and Pico Neo 3 controllers)
+ metadata->register_interaction_profile("Pico controller", "/interaction_profiles/pico/neo3_controller", XR_PICO_CONTROLLER_INTERACTION_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Screenshot click", "/user/hand/right", "/user/hand/right/input/back/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "X touch", "/user/hand/left", "/user/hand/left/input/x/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Y touch", "/user/hand/left", "/user/hand/left/input/y/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "A touch", "/user/hand/right", "/user/hand/right/input/a/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "B touch", "/user/hand/right", "/user/hand/right/input/b/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/left", "/user/hand/left/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/left", "/user/hand/left/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Thumbstick touch", "/user/hand/right", "/user/hand/right/input/thumbstick/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/pico/neo3_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_pico_controller_extension.h b/modules/openxr/extensions/openxr_pico_controller_extension.h
new file mode 100644
index 0000000000..a2a1e2f3d3
--- /dev/null
+++ b/modules/openxr/extensions/openxr_pico_controller_extension.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* openxr_pico_controller_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_PICO_CONTROLLER_EXTENSION_H
+#define OPENXR_PICO_CONTROLLER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRPicoControllerExtension : public OpenXRExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available = false;
+};
+
+#endif // OPENXR_PICO_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 2608c4ac17..d5739db23f 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -1,108 +1,72 @@
-/*************************************************************************/
-/* openxr_vulkan_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_vulkan_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/string/print_string.h"
#include "../extensions/openxr_vulkan_extension.h"
-#include "../openxr_api.h"
#include "../openxr_util.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"
-// need to include Vulkan so we know of type definitions
-#define XR_USE_GRAPHICS_API_VULKAN
-
-#ifdef WINDOWS_ENABLED
-// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
-// however due to the way the openxr headers are put together, we have no choice.
-#include <windows.h>
-#endif
-
-// include platform dependent structs
-#include <openxr/openxr_platform.h>
-
-PFN_xrGetVulkanGraphicsRequirements2KHR xrGetVulkanGraphicsRequirements2KHR_ptr = nullptr;
-PFN_xrCreateVulkanInstanceKHR xrCreateVulkanInstanceKHR_ptr = nullptr;
-PFN_xrGetVulkanGraphicsDevice2KHR xrGetVulkanGraphicsDevice2KHR_ptr = nullptr;
-PFN_xrCreateVulkanDeviceKHR xrCreateVulkanDeviceKHR_ptr = nullptr;
-
-OpenXRVulkanExtension::OpenXRVulkanExtension(OpenXRAPI *p_openxr_api) :
- OpenXRGraphicsExtensionWrapper(p_openxr_api) {
+OpenXRVulkanExtension::OpenXRVulkanExtension() {
VulkanContext::set_vulkan_hooks(this);
-
- request_extensions[XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME] = nullptr; // must be available
-
- ERR_FAIL_NULL(openxr_api);
}
OpenXRVulkanExtension::~OpenXRVulkanExtension() {
VulkanContext::set_vulkan_hooks(nullptr);
}
-void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
- XrResult result;
-
- ERR_FAIL_NULL(openxr_api);
-
- // Obtain pointers to functions we're accessing here, they are (not yet) part of core.
- result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsRequirements2KHR_ptr);
- if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to xrGetVulkanGraphicsRequirements2KHR entry point [", openxr_api->get_error_string(result), "]");
- }
+HashMap<String, bool *> OpenXRVulkanExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
- result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanInstanceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanInstanceKHR_ptr);
- if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to xrCreateVulkanInstanceKHR entry point [", openxr_api->get_error_string(result), "]");
- }
-
- result = xrGetInstanceProcAddr(p_instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction *)&xrGetVulkanGraphicsDevice2KHR_ptr);
- if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to xrGetVulkanGraphicsDevice2KHR entry point [", openxr_api->get_error_string(result), "]");
- }
+ request_extensions[XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME] = nullptr; // must be available
- result = xrGetInstanceProcAddr(p_instance, "xrCreateVulkanDeviceKHR", (PFN_xrVoidFunction *)&xrCreateVulkanDeviceKHR_ptr);
- if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to xrCreateVulkanDeviceKHR entry point [", openxr_api->get_error_string(result), "]");
- }
+ return request_extensions;
}
-XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsRequirements2KHR(XrInstance p_instance, XrSystemId p_system_id, XrGraphicsRequirementsVulkanKHR *p_graphics_requirements) {
- ERR_FAIL_NULL_V(xrGetVulkanGraphicsRequirements2KHR_ptr, XR_ERROR_HANDLE_INVALID);
+void OpenXRVulkanExtension::on_instance_created(const XrInstance p_instance) {
+ ERR_FAIL_NULL(OpenXRAPI::get_singleton());
+
+ // Obtain pointers to functions we're accessing here, they are (not yet) part of core.
- return (*xrGetVulkanGraphicsRequirements2KHR_ptr)(p_instance, p_system_id, p_graphics_requirements);
+ EXT_INIT_XR_FUNC(xrGetVulkanGraphicsRequirements2KHR);
+ EXT_INIT_XR_FUNC(xrCreateVulkanInstanceKHR);
+ EXT_INIT_XR_FUNC(xrGetVulkanGraphicsDevice2KHR);
+ EXT_INIT_XR_FUNC(xrCreateVulkanDeviceKHR);
+ EXT_INIT_XR_FUNC(xrEnumerateSwapchainImages);
}
bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_version) {
- ERR_FAIL_NULL_V(openxr_api, false);
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrGraphicsRequirementsVulkan2KHR vulkan_requirements = {
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR, // type
@@ -111,9 +75,9 @@ bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_versi
0 // maxApiVersionSupported
};
- XrResult result = xrGetVulkanGraphicsRequirements2KHR(openxr_api->get_instance(), openxr_api->get_system_id(), &vulkan_requirements);
+ XrResult result = xrGetVulkanGraphicsRequirements2KHR(OpenXRAPI::get_singleton()->get_instance(), OpenXRAPI::get_singleton()->get_system_id(), &vulkan_requirements);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to get vulkan graphics requirements [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to get vulkan graphics requirements [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
@@ -141,12 +105,6 @@ bool OpenXRVulkanExtension::check_graphics_api_support(XrVersion p_desired_versi
return true;
}
-XrResult OpenXRVulkanExtension::xrCreateVulkanInstanceKHR(XrInstance p_instance, const XrVulkanInstanceCreateInfoKHR *p_create_info, VkInstance *r_vulkan_instance, VkResult *r_vulkan_result) {
- ERR_FAIL_NULL_V(xrCreateVulkanInstanceKHR_ptr, XR_ERROR_HANDLE_INVALID);
-
- return (*xrCreateVulkanInstanceKHR_ptr)(p_instance, p_create_info, r_vulkan_instance, r_vulkan_result);
-}
-
bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) {
// get the vulkan version we are creating
uint32_t vulkan_version = p_vulkan_create_info->pApplicationInfo->apiVersion;
@@ -163,7 +121,7 @@ bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p
XrVulkanInstanceCreateInfoKHR xr_vulkan_instance_info = {
XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, // type
nullptr, // next
- openxr_api->get_system_id(), // systemId
+ OpenXRAPI::get_singleton()->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
p_vulkan_create_info, // vulkanCreateInfo
@@ -171,9 +129,9 @@ bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p
};
VkResult vk_result = VK_SUCCESS;
- XrResult result = xrCreateVulkanInstanceKHR(openxr_api->get_instance(), &xr_vulkan_instance_info, &vulkan_instance, &vk_result);
+ XrResult result = xrCreateVulkanInstanceKHR(OpenXRAPI::get_singleton()->get_instance(), &xr_vulkan_instance_info, &vulkan_instance, &vk_result);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to create vulkan instance [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to create vulkan instance [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
@@ -195,25 +153,19 @@ bool OpenXRVulkanExtension::create_vulkan_instance(const VkInstanceCreateInfo *p
return true;
}
-XrResult OpenXRVulkanExtension::xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device) {
- ERR_FAIL_NULL_V(xrGetVulkanGraphicsDevice2KHR_ptr, XR_ERROR_HANDLE_INVALID);
-
- return (*xrGetVulkanGraphicsDevice2KHR_ptr)(p_instance, p_get_info, r_vulkan_physical_device);
-}
-
bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
- ERR_FAIL_NULL_V(openxr_api, false);
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
XrVulkanGraphicsDeviceGetInfoKHR get_info = {
XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, // type
nullptr, // next
- openxr_api->get_system_id(), // systemId
+ OpenXRAPI::get_singleton()->get_system_id(), // systemId
vulkan_instance, // vulkanInstance
};
- XrResult result = xrGetVulkanGraphicsDevice2KHR(openxr_api->get_instance(), &get_info, &vulkan_physical_device);
+ XrResult result = xrGetVulkanGraphicsDevice2KHR(OpenXRAPI::get_singleton()->get_instance(), &get_info, &vulkan_physical_device);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to obtain vulkan physical device [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to obtain vulkan physical device [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
@@ -222,14 +174,8 @@ bool OpenXRVulkanExtension::get_physical_device(VkPhysicalDevice *r_device) {
return true;
}
-XrResult OpenXRVulkanExtension::xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result) {
- ERR_FAIL_NULL_V(xrCreateVulkanDeviceKHR_ptr, XR_ERROR_HANDLE_INVALID);
-
- return (*xrCreateVulkanDeviceKHR_ptr)(p_instance, p_create_info, r_device, r_result);
-}
-
bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) {
- ERR_FAIL_NULL_V(openxr_api, false);
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), false);
// the first entry in our queue list should be the one we need to remember...
vulkan_queue_family_index = p_device_create_info->pQueueCreateInfos[0].queueFamilyIndex;
@@ -238,7 +184,7 @@ bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_dev
XrVulkanDeviceCreateInfoKHR create_info = {
XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, // type
nullptr, // next
- openxr_api->get_system_id(), // systemId
+ OpenXRAPI::get_singleton()->get_system_id(), // systemId
0, // createFlags
vkGetInstanceProcAddr, // pfnGetInstanceProcAddr
vulkan_physical_device, // vulkanPhysicalDevice
@@ -247,9 +193,9 @@ bool OpenXRVulkanExtension::create_vulkan_device(const VkDeviceCreateInfo *p_dev
};
VkResult vk_result = VK_SUCCESS;
- XrResult result = xrCreateVulkanDeviceKHR(openxr_api->get_instance(), &create_info, &vulkan_device, &vk_result);
+ XrResult result = xrCreateVulkanDeviceKHR(OpenXRAPI::get_singleton()->get_instance(), &create_info, &vulkan_device, &vk_result);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to create vulkan device [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to create vulkan device [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
@@ -285,6 +231,12 @@ void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usab
p_usable_swap_chains.push_back(VK_FORMAT_B8G8R8A8_UINT);
}
+void OpenXRVulkanExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) {
+ p_usable_swap_chains.push_back(VK_FORMAT_R32_SFLOAT);
+ p_usable_swap_chains.push_back(VK_FORMAT_D24_UNORM_S8_UINT);
+ p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT);
+}
+
bool OpenXRVulkanExtension::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) {
XrSwapchainImageVulkanKHR *images = nullptr;
@@ -296,7 +248,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
uint32_t swapchain_length;
XrResult result = xrEnumerateSwapchainImages(p_swapchain, 0, &swapchain_length, nullptr);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to get swapchaim image count [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to get swapchaim image count [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
return false;
}
@@ -311,7 +263,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
result = xrEnumerateSwapchainImages(p_swapchain, swapchain_length, &swapchain_length, (XrSwapchainImageBaseHeader *)images);
if (XR_FAILED(result)) {
- print_line("OpenXR: Failed to get swapchaim images [", openxr_api->get_error_string(result), "]");
+ print_line("OpenXR: Failed to get swapchaim images [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
memfree(images);
return false;
}
@@ -328,7 +280,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
RenderingDevice::DataFormat format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
RenderingDevice::TextureSamples samples = RenderingDevice::TEXTURE_SAMPLES_1;
- uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ uint64_t usage_flags = RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT;
switch (p_swapchain_format) {
case VK_FORMAT_R8G8B8A8_SRGB:
@@ -340,16 +292,32 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
// will thus do an sRGB -> Linear conversion as expected.
// format = RenderingDevice::DATA_FORMAT_R8G8B8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UNORM;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_B8G8R8A8_SRGB:
// format = RenderingDevice::DATA_FORMAT_B8G8R8A8_SRGB;
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UNORM;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_R8G8B8A8_UINT:
format = RenderingDevice::DATA_FORMAT_R8G8B8A8_UINT;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case VK_FORMAT_B8G8R8A8_UINT:
format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+ break;
+ case VK_FORMAT_R32_SFLOAT:
+ format = RenderingDevice::DATA_FORMAT_R32_SFLOAT;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ break;
+ case VK_FORMAT_D24_UNORM_S8_UINT:
+ format = RenderingDevice::DATA_FORMAT_D24_UNORM_S8_UINT;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ break;
+ case VK_FORMAT_D32_SFLOAT_S8_UINT:
+ format = RenderingDevice::DATA_FORMAT_D32_SFLOAT_S8_UINT;
+ usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
default:
// continue with our default value
@@ -385,8 +353,7 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
break;
}
- Vector<RID> image_rids;
- Vector<RID> framebuffers;
+ Vector<RID> texture_rids;
// create Godot texture objects for each entry in our swapchain
for (uint64_t i = 0; i < swapchain_length; i++) {
@@ -401,19 +368,10 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in
1,
p_array_size);
- image_rids.push_back(image_rid);
-
- {
- Vector<RID> fb;
- fb.push_back(image_rid);
-
- RID fb_rid = rendering_device->framebuffer_create(fb, RenderingDevice::INVALID_ID, p_array_size);
- framebuffers.push_back(fb_rid);
- }
+ texture_rids.push_back(image_rid);
}
- data->image_rids = image_rids;
- data->framebuffers = framebuffers;
+ data->texture_rids = texture_rids;
memfree(images);
@@ -427,33 +385,19 @@ bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
- r_camera_matrix.matrix[j][i] = matrix.m[j * 4 + i];
+ r_camera_matrix.columns[j][i] = matrix.m[j * 4 + i];
}
}
return true;
}
-bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target, void *p_swapchain_graphics_data, int p_image_index) {
+RID OpenXRVulkanExtension::get_texture(void *p_swapchain_graphics_data, int p_image_index) {
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
- ERR_FAIL_NULL_V(data, false);
- ERR_FAIL_COND_V(p_from_render_target.is_null(), 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);
-
- RID depth_image; // TODO implement
+ ERR_FAIL_NULL_V(data, RID());
- ERR_FAIL_INDEX_V(p_image_index, data->framebuffers.size(), false);
- RID fb = data->framebuffers[p_image_index];
- ERR_FAIL_COND_V(fb.is_null(), false);
-
- // Our vulkan extension can only be used in conjunction with our vulkan renderer.
- 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;
+ ERR_FAIL_INDEX_V(p_image_index, data->texture_rids.size(), RID());
+ return data->texture_rids[p_image_index];
}
void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_graphics_data) {
@@ -468,17 +412,11 @@ void OpenXRVulkanExtension::cleanup_swapchain_graphics_data(void **p_swapchain_g
RenderingDevice *rendering_device = rendering_server->get_rendering_device();
ERR_FAIL_NULL(rendering_device);
- for (int i = 0; i < data->image_rids.size(); i++) {
- // This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain
- rendering_device->free(data->image_rids[i]);
- }
- data->image_rids.clear();
-
- for (int i = 0; i < data->framebuffers.size(); i++) {
+ for (int i = 0; i < data->texture_rids.size(); i++) {
// This should clean up our RIDs and associated texture objects but shouldn't destroy the images, they are owned by our XrSwapchain
- rendering_device->free(data->framebuffers[i]);
+ rendering_device->free(data->texture_rids[i]);
}
- data->framebuffers.clear();
+ data->texture_rids.clear();
memdelete(data);
*p_swapchain_graphics_data = nullptr;
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.h b/modules/openxr/extensions/openxr_vulkan_extension.h
index 5dddc4b9c9..013a8d5a51 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.h
+++ b/modules/openxr/extensions/openxr_vulkan_extension.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_vulkan_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_vulkan_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_VULKAN_EXTENSION_H
#define OPENXR_VULKAN_EXTENSION_H
@@ -36,22 +36,33 @@
#include "drivers/vulkan/vulkan_context.h"
-// Forward declare these so we don't need OpenXR headers where-ever this is included
-// Including OpenXR at this point gives loads and loads of compile issues especially
-// on Windows because windows.h is EVIL and really shouldn't be included outside of platform
-// but we really don't have a choice in the matter
+#include "../openxr_api.h"
+#include "../util.h"
-struct XrGraphicsRequirementsVulkanKHR;
-struct XrVulkanInstanceCreateInfoKHR;
-struct XrVulkanGraphicsDeviceGetInfoKHR;
-struct XrVulkanDeviceCreateInfoKHR;
-struct XrGraphicsBindingVulkanKHR;
+// need to include Vulkan so we know of type definitions
+#define XR_USE_GRAPHICS_API_VULKAN
+
+#ifdef WINDOWS_ENABLED
+// Including windows.h here is absolutely evil, we shouldn't be doing this outside of platform
+// however due to the way the openxr headers are put together, we have no choice.
+#include <windows.h>
+#endif
+
+#ifdef ANDROID_ENABLED
+// The jobject type from jni.h is used by openxr_platform.h on Android.
+#include <jni.h>
+#endif
+
+// include platform dependent structs
+#include <openxr/openxr_platform.h>
class OpenXRVulkanExtension : public OpenXRGraphicsExtensionWrapper, VulkanHooks {
public:
- OpenXRVulkanExtension(OpenXRAPI *p_openxr_api);
+ OpenXRVulkanExtension();
virtual ~OpenXRVulkanExtension() override;
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
virtual void on_instance_created(const XrInstance p_instance) override;
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
@@ -60,11 +71,12 @@ public:
virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) override;
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
+ virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
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, 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;
+ virtual RID get_texture(void *p_swapchain_graphics_data, int p_image_index) override;
private:
static OpenXRVulkanExtension *singleton;
@@ -72,8 +84,7 @@ private:
struct SwapchainGraphicsData {
bool is_multiview;
- Vector<RID> image_rids;
- Vector<RID> framebuffers;
+ Vector<RID> texture_rids;
};
bool check_graphics_api_support(XrVersion p_desired_version);
@@ -84,10 +95,11 @@ private:
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);
- XrResult xrGetVulkanGraphicsDevice2KHR(XrInstance p_instance, const XrVulkanGraphicsDeviceGetInfoKHR *p_get_info, VkPhysicalDevice *r_vulkan_physical_device);
- XrResult xrCreateVulkanDeviceKHR(XrInstance p_instance, const XrVulkanDeviceCreateInfoKHR *p_create_info, VkDevice *r_device, VkResult *r_result);
+ EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsRequirements2KHR, (XrInstance), p_instance, (XrSystemId), p_system_id, (XrGraphicsRequirementsVulkanKHR *), p_graphics_requirements)
+ EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanInstanceKHR, (XrInstance), p_instance, (const XrVulkanInstanceCreateInfoKHR *), p_create_info, (VkInstance *), r_vulkan_instance, (VkResult *), r_vulkan_result)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetVulkanGraphicsDevice2KHR, (XrInstance), p_instance, (const XrVulkanGraphicsDeviceGetInfoKHR *), p_get_info, (VkPhysicalDevice *), r_vulkan_physical_device)
+ EXT_PROTO_XRRESULT_FUNC4(xrCreateVulkanDeviceKHR, (XrInstance), p_instance, (const XrVulkanDeviceCreateInfoKHR *), p_create_info, (VkDevice *), r_device, (VkResult *), r_result)
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainImages, (XrSwapchain), p_swapchain, (uint32_t), p_image_capacity_input, (uint32_t *), p_image_count_output, (XrSwapchainImageBaseHeader *), p_images)
};
#endif // OPENXR_VULKAN_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_wmr_controller_extension.cpp b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp
new file mode 100644
index 0000000000..7017496d75
--- /dev/null
+++ b/modules/openxr/extensions/openxr_wmr_controller_extension.cpp
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* openxr_wmr_controller_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_wmr_controller_extension.h"
+#include "../action_map/openxr_interaction_profile_meta_data.h"
+
+HashMap<String, bool *> OpenXRWMRControllerExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ // Note HP G2 is available on WMR and SteamVR, but Odessey is only available on WMR
+ request_extensions[XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME] = &available[WMR_HPMR];
+ request_extensions[XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME] = &available[WMR_SAMSUNG_ODESSY];
+
+ return request_extensions;
+}
+
+bool OpenXRWMRControllerExtension::is_available(WMRControllers p_type) {
+ return available[p_type];
+}
+
+void OpenXRWMRControllerExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetaData *metadata = OpenXRInteractionProfileMetaData::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // HP MR controller (newer G2 controllers)
+ metadata->register_interaction_profile("HPMR controller", "/interaction_profiles/hp/mixed_reality_controller", XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Menu click", "/user/hand/right", "/user/hand/right/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "X click", "/user/hand/left", "/user/hand/left/input/x/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Y click", "/user/hand/left", "/user/hand/left/input/y/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "A click", "/user/hand/right", "/user/hand/right/input/a/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "B click", "/user/hand/right", "/user/hand/right/input/b/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/hp/mixed_reality_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+
+ // Samsung Odyssey controller
+ metadata->register_interaction_profile("Samsung Odyssey controller", "/interaction_profiles/samsung/odyssey_controller", XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Palm pose", "/user/hand/left", "/user/hand/left/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Palm pose", "/user/hand/right", "/user/hand/right/input/palm_ext/pose", XR_EXT_PALM_POSE_EXTENSION_NAME, OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Menu click", "/user/hand/left", "/user/hand/left/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Menu click", "/user/hand/right", "/user/hand/right/input/menu/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trigger", "/user/hand/left", "/user/hand/left/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trigger click", "/user/hand/left", "/user/hand/left/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trigger", "/user/hand/right", "/user/hand/right/input/trigger/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trigger click", "/user/hand/right", "/user/hand/right/input/trigger/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Squeeze click", "/user/hand/left", "/user/hand/left/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Squeeze click", "/user/hand/right", "/user/hand/right/input/squeeze/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Thumbstick", "/user/hand/right", "/user/hand/right/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Thumbstick click", "/user/hand/right", "/user/hand/right/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad", "/user/hand/left", "/user/hand/left/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad click", "/user/hand/left", "/user/hand/left/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad touch", "/user/hand/left", "/user/hand/left/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad", "/user/hand/right", "/user/hand/right/input/trackpad", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad click", "/user/hand/right", "/user/hand/right/input/trackpad/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Trackpad touch", "/user/hand/right", "/user/hand/right/input/trackpad/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
+
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/samsung/odyssey_controller", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_wmr_controller_extension.h b/modules/openxr/extensions/openxr_wmr_controller_extension.h
new file mode 100644
index 0000000000..3bbdff586e
--- /dev/null
+++ b/modules/openxr/extensions/openxr_wmr_controller_extension.h
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* openxr_wmr_controller_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_WMR_CONTROLLER_EXTENSION_H
+#define OPENXR_WMR_CONTROLLER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRWMRControllerExtension : public OpenXRExtensionWrapper {
+public:
+ enum WMRControllers {
+ WMR_HPMR,
+ WMR_SAMSUNG_ODESSY,
+ WMR_MAX_CONTROLLERS
+ };
+
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available(WMRControllers p_type);
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available[WMR_MAX_CONTROLLERS] = { false, false };
+};
+
+#endif // OPENXR_WMR_CONTROLLER_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 92d074cb75..6b8f140923 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_api.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_api.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_api.h"
#include "openxr_util.h"
@@ -41,18 +41,50 @@
#endif
#ifdef ANDROID_ENABLED
-#include "extensions/openxr_android_extension.h"
+#define OPENXR_LOADER_NAME "libopenxr_loader.so"
#endif
+// We need to have all the graphics API defines before the Vulkan or OpenGL
+// extensions are included, otherwise we'll only get one graphics API.
+#ifdef VULKAN_ENABLED
+#define XR_USE_GRAPHICS_API_VULKAN
+#endif
+#ifdef GLES3_ENABLED
+#ifdef ANDROID_ENABLED
+#define XR_USE_GRAPHICS_API_OPENGL_ES
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#else
+#define XR_USE_GRAPHICS_API_OPENGL
+#endif // ANDROID_ENABLED
+#ifdef X11_ENABLED
+#include OPENGL_INCLUDE_H
+#define GL_GLEXT_PROTOTYPES 1
+#define GL3_PROTOTYPES 1
+#include "thirdparty/glad/glad/gl.h"
+#include "thirdparty/glad/glad/glx.h"
+#include <X11/Xlib.h>
+#endif // X11_ENABLED
+#endif // GLES_ENABLED
+
#ifdef VULKAN_ENABLED
#include "extensions/openxr_vulkan_extension.h"
#endif
-#include "extensions/openxr_htc_vive_tracker_extension.h"
+#ifdef GLES3_ENABLED
+#include "extensions/openxr_opengl_extension.h"
+#endif
+
+#include "extensions/openxr_composition_layer_depth_extension.h"
+#include "extensions/openxr_fb_display_refresh_rate_extension.h"
+#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
#include "modules/openxr/openxr_interface.h"
OpenXRAPI *OpenXRAPI::singleton = nullptr;
+Vector<OpenXRExtensionWrapper *> OpenXRAPI::registered_extension_wrappers;
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"
@@ -69,10 +101,6 @@ bool OpenXRAPI::openxr_is_enabled(bool p_check_run_in_editor) {
}
}
-OpenXRAPI *OpenXRAPI::get_singleton() {
- return singleton;
-}
-
String OpenXRAPI::get_default_action_map_resource_name() {
String name = GLOBAL_GET("xr/openxr/default_action_map");
@@ -126,11 +154,9 @@ bool OpenXRAPI::load_layer_properties() {
result = xrEnumerateApiLayerProperties(num_layer_properties, &num_layer_properties, layer_properties);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate api layer properties");
-#ifdef DEBUG
for (uint32_t i = 0; i < num_layer_properties; i++) {
- print_line("OpenXR: Found OpenXR layer ", layer_properties[i].layerName);
+ print_verbose(String("OpenXR: Found OpenXR layer ") + layer_properties[i].layerName);
}
-#endif
return true;
}
@@ -158,11 +184,9 @@ bool OpenXRAPI::load_supported_extensions() {
result = xrEnumerateInstanceExtensionProperties(nullptr, num_supported_extensions, &num_supported_extensions, supported_extensions);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate extension properties");
-#ifdef DEBUG
for (uint32_t i = 0; i < num_supported_extensions; i++) {
- print_line("OpenXR: Found OpenXR extension ", supported_extensions[i].extensionName);
+ print_verbose(String("OpenXR: Found OpenXR extension ") + supported_extensions[i].extensionName);
}
-#endif
return true;
}
@@ -170,20 +194,89 @@ bool OpenXRAPI::load_supported_extensions() {
bool OpenXRAPI::is_extension_supported(const String &p_extension) const {
for (uint32_t i = 0; i < num_supported_extensions; i++) {
if (supported_extensions[i].extensionName == p_extension) {
-#ifdef DEBUG
- print_line("OpenXR: requested extension", p_extension, "is supported");
-#endif
return true;
}
}
-#ifdef DEBUG
- print_line("OpenXR: requested extension", p_extension, "is not supported");
-#endif
+ return false;
+}
+
+bool OpenXRAPI::is_extension_enabled(const String &p_extension) const {
+ CharString extension = p_extension.ascii();
+
+ for (int i = 0; i < enabled_extensions.size(); i++) {
+ if (strcmp(enabled_extensions[i].ptr(), extension.ptr()) == 0) {
+ return true;
+ }
+ }
return false;
}
+bool OpenXRAPI::is_top_level_path_supported(const String &p_toplevel_path) {
+ String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_top_level_extension(p_toplevel_path);
+
+ // If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
+ ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported toplevel path " + p_toplevel_path);
+
+ if (required_extension == "") {
+ // no extension needed, core top level are always "supported", they just won't be used if not really supported
+ return true;
+ }
+
+ if (!is_extension_enabled(required_extension)) {
+ // It is very likely we have top level paths for which the extension is not available so don't flood the logs with unnecessary spam.
+ print_verbose("OpenXR: Top level path " + p_toplevel_path + " requires extension " + required_extension);
+ return false;
+ }
+
+ return true;
+}
+
+bool OpenXRAPI::is_interaction_profile_supported(const String &p_ip_path) {
+ String required_extension = OpenXRInteractionProfileMetaData::get_singleton()->get_interaction_profile_extension(p_ip_path);
+
+ // If unsupported is returned we likely have a misspelled interaction profile path in our action map. Always output that as an error.
+ ERR_FAIL_COND_V_MSG(required_extension == XR_PATH_UNSUPPORTED_NAME, false, "OpenXR: Unsupported interaction profile " + p_ip_path);
+
+ if (required_extension == "") {
+ // no extension needed, core interaction profiles are always "supported", they just won't be used if not really supported
+ return true;
+ }
+
+ if (!is_extension_enabled(required_extension)) {
+ // It is very likely we have interaction profiles for which the extension is not available so don't flood the logs with unnecessary spam.
+ print_verbose("OpenXR: Interaction profile " + p_ip_path + " requires extension " + required_extension);
+ return false;
+ }
+
+ return true;
+}
+
+bool OpenXRAPI::interaction_profile_supports_io_path(const String &p_ip_path, const String &p_io_path) {
+ if (!is_interaction_profile_supported(p_ip_path)) {
+ return false;
+ }
+
+ const OpenXRInteractionProfileMetaData::IOPath *io_path = OpenXRInteractionProfileMetaData::get_singleton()->get_io_path(p_ip_path, p_io_path);
+
+ // If the io_path is not part of our meta data we've likely got a misspelled name or a bad action map, report
+ ERR_FAIL_NULL_V_MSG(io_path, false, "OpenXR: Unsupported io path " + String(p_ip_path) + String(p_io_path));
+
+ if (io_path->openxr_extension_name == "") {
+ // no extension needed, core io paths are always "supported", they just won't be used if not really supported
+ return true;
+ }
+
+ if (!is_extension_enabled(io_path->openxr_extension_name)) {
+ // It is very likely we have io paths for which the extension is not available so don't flood the logs with unnecessary spam.
+ print_verbose("OpenXR: IO path " + String(p_ip_path) + String(p_io_path) + " requires extension " + io_path->openxr_extension_name);
+ return false;
+ }
+
+ return true;
+}
+
void OpenXRAPI::copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len) {
CharString char_string = p_string.utf8();
int len = char_string.length();
@@ -203,7 +296,7 @@ bool OpenXRAPI::create_instance() {
// Append the extensions requested by the registered extension wrappers.
HashMap<String, bool *> requested_extensions;
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
- const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_request_extensions();
+ const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_requested_extensions();
// requested_extensions.insert(wrapper_request_extensions.begin(), wrapper_request_extensions.end());
for (auto &requested_extension : wrapper_request_extensions) {
@@ -211,14 +304,6 @@ bool OpenXRAPI::create_instance() {
}
}
- // Add optional extensions for controllers that may be supported.
- // Overkill to create extension classes for this.
- requested_extensions[XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME] = &ext_hp_mixed_reality_available;
- requested_extensions[XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME] = &ext_samsung_odyssey_available;
- requested_extensions[XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME] = &ext_vive_cosmos_available;
- requested_extensions[XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME] = &ext_vive_focus3_available;
- requested_extensions[XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME] = &ext_huawei_controller_available;
-
// Check which extensions are supported
enabled_extensions.clear();
@@ -226,7 +311,7 @@ bool OpenXRAPI::create_instance() {
if (!is_extension_supported(requested_extension.key)) {
if (requested_extension.value == nullptr) {
// nullptr means this is a manditory extension so we fail
- ERR_FAIL_V_MSG(false, "OpenXR: OpenXR Runtime does not support OpenGL extension!");
+ ERR_FAIL_V_MSG(false, String("OpenXR: OpenXR Runtime does not support ") + requested_extension.key + String(" extension!"));
} else {
// set this extension as not supported
*requested_extension.value = false;
@@ -245,6 +330,7 @@ bool OpenXRAPI::create_instance() {
Vector<const char *> extension_ptrs;
for (int i = 0; i < enabled_extensions.size(); i++) {
+ print_verbose(String("OpenXR: Enabling extension ") + String(enabled_extensions[i]));
extension_ptrs.push_back(enabled_extensions[i].get_data());
}
@@ -260,9 +346,17 @@ bool OpenXRAPI::create_instance() {
XR_CURRENT_API_VERSION // apiVersion
};
+ void *next_pointer = nullptr;
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ void *np = wrapper->set_instance_create_info_and_get_next_pointer(next_pointer);
+ if (np != nullptr) {
+ next_pointer = np;
+ }
+ }
+
XrInstanceCreateInfo instance_create_info = {
XR_TYPE_INSTANCE_CREATE_INFO, // type
- nullptr, // next
+ next_pointer, // next
0, // createFlags
application_info, // applicationInfo
0, // enabledApiLayerCount, need to find out if we need support for this?
@@ -284,6 +378,9 @@ bool OpenXRAPI::create_instance() {
0, // runtimeVersion, from here will be set by our get call
"" // runtimeName
};
+
+ OPENXR_API_INIT_XR_FUNC_V(xrGetInstanceProperties);
+
result = xrGetInstanceProperties(instance, &instanceProps);
if (XR_FAILED(result)) {
// not fatal probably
@@ -379,11 +476,9 @@ bool OpenXRAPI::load_supported_view_configuration_types() {
result = xrEnumerateViewConfigurations(instance, system_id, num_view_configuration_types, &num_view_configuration_types, supported_view_configuration_types);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerateview configurations");
-#ifdef DEBUG
for (uint32_t i = 0; i < num_view_configuration_types; i++) {
- print_line("OpenXR: Found supported view configuration ", OpenXRUtil::get_view_configuration_name(supported_view_configuration_types[i]));
+ print_verbose(String("OpenXR: Found supported view configuration ") + OpenXRUtil::get_view_configuration_name(supported_view_configuration_types[i]));
}
-#endif
return true;
}
@@ -432,17 +527,15 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType
result = xrEnumerateViewConfigurationViews(instance, system_id, p_configuration_type, view_count, &view_count, view_configuration_views);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate view configurations");
-#ifdef DEBUG
for (uint32_t i = 0; i < view_count; i++) {
- print_line("OpenXR: Found supported view configuration view");
- print_line(" - width: ", view_configuration_views[i].maxImageRectWidth);
- print_line(" - height: ", view_configuration_views[i].maxImageRectHeight);
- print_line(" - sample count: ", view_configuration_views[i].maxSwapchainSampleCount);
- print_line(" - recommended render width: ", view_configuration_views[i].recommendedImageRectWidth);
- print_line(" - recommended render height: ", view_configuration_views[i].recommendedImageRectHeight);
- print_line(" - recommended render sample count: ", view_configuration_views[i].recommendedSwapchainSampleCount);
+ print_verbose("OpenXR: Found supported view configuration view");
+ print_verbose(String(" - width: ") + itos(view_configuration_views[i].maxImageRectWidth));
+ print_verbose(String(" - height: ") + itos(view_configuration_views[i].maxImageRectHeight));
+ print_verbose(String(" - sample count: ") + itos(view_configuration_views[i].maxSwapchainSampleCount));
+ print_verbose(String(" - recommended render width: ") + itos(view_configuration_views[i].recommendedImageRectWidth));
+ print_verbose(String(" - recommended render height: ") + itos(view_configuration_views[i].recommendedImageRectHeight));
+ print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount));
}
-#endif
return true;
}
@@ -467,6 +560,12 @@ void OpenXRAPI::destroy_instance() {
instance = XR_NULL_HANDLE;
}
enabled_extensions.clear();
+
+ if (graphics_extension != nullptr) {
+ unregister_extension_wrapper(graphics_extension);
+ memdelete(graphics_extension);
+ graphics_extension = nullptr;
+ }
}
bool OpenXRAPI::create_session() {
@@ -524,11 +623,9 @@ bool OpenXRAPI::load_supported_reference_spaces() {
result = xrEnumerateReferenceSpaces(session, num_reference_spaces, &num_reference_spaces, supported_reference_spaces);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate reference spaces");
- // #ifdef DEBUG
for (uint32_t i = 0; i < num_reference_spaces; i++) {
- print_line("OpenXR: Found supported reference space ", OpenXRUtil::get_reference_space_name(supported_reference_spaces[i]));
+ print_verbose(String("OpenXR: Found supported reference space ") + OpenXRUtil::get_reference_space_name(supported_reference_spaces[i]));
}
- // #endif
return true;
}
@@ -621,11 +718,9 @@ bool OpenXRAPI::load_supported_swapchain_formats() {
result = xrEnumerateSwapchainFormats(session, num_swapchain_formats, &num_swapchain_formats, supported_swapchain_formats);
ERR_FAIL_COND_V_MSG(XR_FAILED(result), false, "OpenXR: Failed to enumerate swapchain formats");
- // #ifdef DEBUG
for (uint32_t i = 0; i < num_swapchain_formats; i++) {
- print_line("OpenXR: Found supported swapchain format ", get_swapchain_format_name(supported_swapchain_formats[i]));
+ print_verbose(String("OpenXR: Found supported swapchain format ") + get_swapchain_format_name(supported_swapchain_formats[i]));
}
- // #endif
return true;
}
@@ -642,7 +737,7 @@ bool OpenXRAPI::is_swapchain_format_supported(int64_t p_swapchain_format) {
return false;
}
-bool OpenXRAPI::create_main_swapchain() {
+bool OpenXRAPI::create_swapchains() {
ERR_FAIL_NULL_V(graphics_extension, false);
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
@@ -660,34 +755,36 @@ bool OpenXRAPI::create_main_swapchain() {
already rendering the next frame.
Finally an area we need to expand upon is that Foveated rendering is only enabled for the swap chain we create,
- as we render 3D content into internal buffers that are copied into the swapchain, we don't get any of the performance gains
- until such time as we implement VRS.
+ as we render 3D content into internal buffers that are copied into the swapchain, we do now have (basic) VRS support
*/
- // Build a vector with swapchain formats we want to use, from best fit to worst
- Vector<int64_t> usable_swapchain_formats;
- int64_t swapchain_format_to_use = 0;
+ Size2 recommended_size = get_recommended_target_size();
- graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
+ // We start with our color swapchain...
+ {
+ // Build a vector with swapchain formats we want to use, from best fit to worst
+ Vector<int64_t> usable_swapchain_formats;
+ int64_t swapchain_format_to_use = 0;
- // now find out which one is supported
- for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
- if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
- swapchain_format_to_use = usable_swapchain_formats[i];
- }
- }
+ graphics_extension->get_usable_swapchain_formats(usable_swapchain_formats);
- if (swapchain_format_to_use == 0) {
- swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
- print_line("Couldn't find usable swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
- } else {
- print_line("Using swap chain format:", get_swapchain_format_name(swapchain_format_to_use));
- }
+ // now find out which one is supported
+ for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
+ if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
+ swapchain_format_to_use = usable_swapchain_formats[i];
+ }
+ }
- Size2 recommended_size = get_recommended_target_size();
+ if (swapchain_format_to_use == 0) {
+ swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
+ print_line("Couldn't find usable color swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
+ } else {
+ print_verbose(String("Using color swap chain format:") + get_swapchain_format_name(swapchain_format_to_use));
+ }
- if (!create_swapchain(swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchain, &swapchain_graphics_data)) {
- return false;
+ if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT | XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain, &swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data)) {
+ return false;
+ }
}
views = (XrView *)memalloc(sizeof(XrView) * view_count);
@@ -696,18 +793,74 @@ bool OpenXRAPI::create_main_swapchain() {
projection_views = (XrCompositionLayerProjectionView *)memalloc(sizeof(XrCompositionLayerProjectionView) * view_count);
ERR_FAIL_NULL_V_MSG(projection_views, false, "OpenXR Couldn't allocate memory for projection views");
+ // We create our depth swapchain if:
+ // - we've enabled submitting depth buffer
+ // - we support our depth layer extension
+ // - we have our spacewarp extension (not yet implemented)
+ if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available()) {
+ // Build a vector with swapchain formats we want to use, from best fit to worst
+ Vector<int64_t> usable_swapchain_formats;
+ int64_t swapchain_format_to_use = 0;
+
+ graphics_extension->get_usable_depth_formats(usable_swapchain_formats);
+
+ // now find out which one is supported
+ for (int i = 0; i < usable_swapchain_formats.size() && swapchain_format_to_use == 0; i++) {
+ if (is_swapchain_format_supported(usable_swapchain_formats[i])) {
+ swapchain_format_to_use = usable_swapchain_formats[i];
+ }
+ }
+
+ if (swapchain_format_to_use == 0) {
+ swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best...
+ print_line("Couldn't find usable depth swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead.");
+ } else {
+ print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(swapchain_format_to_use));
+ }
+
+ if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) {
+ return false;
+ }
+
+ depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count);
+ ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views");
+ }
+
+ // We create our velocity swapchain if:
+ // - we have our spacewarp extension (not yet implemented)
+ {
+ // TBD
+ }
+
for (uint32_t i = 0; i < view_count; i++) {
views[i].type = XR_TYPE_VIEW;
views[i].next = nullptr;
projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
projection_views[i].next = nullptr;
- projection_views[i].subImage.swapchain = swapchain;
+ projection_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain;
projection_views[i].subImage.imageArrayIndex = i;
projection_views[i].subImage.imageRect.offset.x = 0;
projection_views[i].subImage.imageRect.offset.y = 0;
projection_views[i].subImage.imageRect.extent.width = recommended_size.width;
projection_views[i].subImage.imageRect.extent.height = recommended_size.height;
+
+ if (submit_depth_buffer && OpenXRCompositionLayerDepthExtension::get_singleton()->is_available() && depth_views) {
+ projection_views[i].next = &depth_views[i];
+
+ depth_views[i].type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
+ depth_views[i].next = nullptr;
+ depth_views[i].subImage.swapchain = swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain;
+ depth_views[i].subImage.imageArrayIndex = i;
+ depth_views[i].subImage.imageRect.offset.x = 0;
+ depth_views[i].subImage.imageRect.offset.y = 0;
+ depth_views[i].subImage.imageRect.extent.width = recommended_size.width;
+ depth_views[i].subImage.imageRect.extent.height = recommended_size.height;
+ depth_views[i].minDepth = 0.0;
+ depth_views[i].maxDepth = 1.0;
+ depth_views[i].nearZ = 0.01; // Near and far Z will be set to the correct values in fill_projection_matrix
+ depth_views[i].farZ = 100.0;
+ }
};
return true;
@@ -719,7 +872,7 @@ void OpenXRAPI::destroy_session() {
}
if (graphics_extension) {
- graphics_extension->cleanup_swapchain_graphics_data(&swapchain_graphics_data);
+ graphics_extension->cleanup_swapchain_graphics_data(&swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data);
}
if (views != nullptr) {
@@ -732,9 +885,16 @@ void OpenXRAPI::destroy_session() {
projection_views = nullptr;
}
- if (swapchain != XR_NULL_HANDLE) {
- xrDestroySwapchain(swapchain);
- swapchain = XR_NULL_HANDLE;
+ if (depth_views != nullptr) {
+ memfree(depth_views);
+ depth_views = nullptr;
+ }
+
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ if (swapchains[i].swapchain != XR_NULL_HANDLE) {
+ xrDestroySwapchain(swapchains[i].swapchain);
+ swapchains[i].swapchain = XR_NULL_HANDLE;
+ }
}
if (supported_swapchain_formats != nullptr) {
@@ -768,7 +928,7 @@ void OpenXRAPI::destroy_session() {
}
}
-bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
+bool OpenXRAPI::create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
ERR_FAIL_NULL_V(graphics_extension, false);
@@ -786,7 +946,7 @@ bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, u
XR_TYPE_SWAPCHAIN_CREATE_INFO, // type
next_pointer, // next
0, // createFlags
- XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, // usageFlags
+ p_usage_flags, // usageFlags
p_swapchain_format, // format
p_sample_count, // sampleCount
p_width, // width
@@ -814,9 +974,7 @@ bool OpenXRAPI::create_swapchain(int64_t p_swapchain_format, uint32_t p_width, u
}
bool OpenXRAPI::on_state_idle() {
-#ifdef DEBUG
- print_line("On state idle");
-#endif
+ print_verbose("On state idle");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_idle();
@@ -826,9 +984,7 @@ bool OpenXRAPI::on_state_idle() {
}
bool OpenXRAPI::on_state_ready() {
-#ifdef DEBUG
- print_line("On state ready");
-#endif
+ print_verbose("On state ready");
// begin session
XrSessionBeginInfo session_begin_info = {
@@ -850,7 +1006,7 @@ bool OpenXRAPI::on_state_ready() {
// That will be very very ugly
// The other possibility is to create a separate OpenXRViewport type specifically for this goal as part of our OpenXR module
- if (!create_main_swapchain()) {
+ if (!create_swapchains()) {
return false;
}
@@ -871,9 +1027,7 @@ bool OpenXRAPI::on_state_ready() {
}
bool OpenXRAPI::on_state_synchronized() {
-#ifdef DEBUG
- print_line("On state synchronized");
-#endif
+ print_verbose("On state synchronized");
// Just in case, see if we already have active trackers...
List<RID> trackers;
@@ -890,9 +1044,7 @@ bool OpenXRAPI::on_state_synchronized() {
}
bool OpenXRAPI::on_state_visible() {
-#ifdef DEBUG
- print_line("On state visible");
-#endif
+ print_verbose("On state visible");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_visible();
@@ -906,9 +1058,7 @@ bool OpenXRAPI::on_state_visible() {
}
bool OpenXRAPI::on_state_focused() {
-#ifdef DEBUG
- print_line("On state focused");
-#endif
+ print_verbose("On state focused");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_focused();
@@ -922,9 +1072,7 @@ bool OpenXRAPI::on_state_focused() {
}
bool OpenXRAPI::on_state_stopping() {
-#ifdef DEBUG
- print_line("On state stopping");
-#endif
+ print_verbose("On state stopping");
if (xr_interface) {
xr_interface->on_state_stopping();
@@ -950,9 +1098,7 @@ bool OpenXRAPI::on_state_stopping() {
}
bool OpenXRAPI::on_state_loss_pending() {
-#ifdef DEBUG
- print_line("On state loss pending");
-#endif
+ print_verbose("On state loss pending");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_loss_pending();
@@ -964,9 +1110,7 @@ bool OpenXRAPI::on_state_loss_pending() {
}
bool OpenXRAPI::on_state_exiting() {
-#ifdef DEBUG
- print_line("On state existing");
-#endif
+ print_verbose("On state existing");
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_exiting();
@@ -977,6 +1121,30 @@ bool OpenXRAPI::on_state_exiting() {
return true;
}
+void OpenXRAPI::set_form_factor(XrFormFactor p_form_factor) {
+ ERR_FAIL_COND(is_initialized());
+
+ form_factor = p_form_factor;
+}
+
+void OpenXRAPI::set_view_configuration(XrViewConfigurationType p_view_configuration) {
+ ERR_FAIL_COND(is_initialized());
+
+ view_configuration = p_view_configuration;
+}
+
+void OpenXRAPI::set_reference_space(XrReferenceSpaceType p_reference_space) {
+ ERR_FAIL_COND(is_initialized());
+
+ reference_space = p_reference_space;
+}
+
+void OpenXRAPI::set_submit_depth_buffer(bool p_submit_depth_buffer) {
+ ERR_FAIL_COND(is_initialized());
+
+ submit_depth_buffer = p_submit_depth_buffer;
+}
+
bool OpenXRAPI::is_initialized() {
return (instance != XR_NULL_HANDLE);
}
@@ -992,12 +1160,101 @@ bool OpenXRAPI::is_running() {
return running;
}
+bool OpenXRAPI::openxr_loader_init() {
+#ifdef ANDROID_ENABLED
+ ERR_FAIL_COND_V_MSG(openxr_loader_library_handle != nullptr, false, "OpenXR Loader library is already loaded.");
+
+ {
+ Error error_code = OS::get_singleton()->open_dynamic_library(OPENXR_LOADER_NAME, openxr_loader_library_handle);
+ ERR_FAIL_COND_V_MSG(error_code != OK, false, "OpenXR loader not found.");
+ }
+
+ {
+ Error error_code = OS::get_singleton()->get_dynamic_library_symbol_handle(openxr_loader_library_handle, "xrGetInstanceProcAddr", (void *&)xrGetInstanceProcAddr);
+ ERR_FAIL_COND_V_MSG(error_code != OK, false, "Symbol xrGetInstanceProcAddr not found in OpenXR Loader library.");
+ }
+#endif
+
+ // Resolve the symbols that don't require an instance
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateInstance);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateApiLayerProperties);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateInstanceExtensionProperties);
+
+ return true;
+}
+
+bool OpenXRAPI::resolve_instance_openxr_symbols() {
+ ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+
+ OPENXR_API_INIT_XR_FUNC_V(xrAcquireSwapchainImage);
+ OPENXR_API_INIT_XR_FUNC_V(xrApplyHapticFeedback);
+ OPENXR_API_INIT_XR_FUNC_V(xrAttachSessionActionSets);
+ OPENXR_API_INIT_XR_FUNC_V(xrBeginFrame);
+ OPENXR_API_INIT_XR_FUNC_V(xrBeginSession);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateAction);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSet);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateActionSpace);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateReferenceSpace);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateSession);
+ OPENXR_API_INIT_XR_FUNC_V(xrCreateSwapchain);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroyAction);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroyActionSet);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroyInstance);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroySession);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroySpace);
+ OPENXR_API_INIT_XR_FUNC_V(xrDestroySwapchain);
+ OPENXR_API_INIT_XR_FUNC_V(xrEndFrame);
+ OPENXR_API_INIT_XR_FUNC_V(xrEndSession);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateReferenceSpaces);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateSwapchainFormats);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurations);
+ OPENXR_API_INIT_XR_FUNC_V(xrEnumerateViewConfigurationViews);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateBoolean);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateFloat);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetActionStateVector2f);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetCurrentInteractionProfile);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetSystem);
+ OPENXR_API_INIT_XR_FUNC_V(xrGetSystemProperties);
+ OPENXR_API_INIT_XR_FUNC_V(xrLocateViews);
+ OPENXR_API_INIT_XR_FUNC_V(xrLocateSpace);
+ OPENXR_API_INIT_XR_FUNC_V(xrPathToString);
+ OPENXR_API_INIT_XR_FUNC_V(xrPollEvent);
+ OPENXR_API_INIT_XR_FUNC_V(xrReleaseSwapchainImage);
+ OPENXR_API_INIT_XR_FUNC_V(xrResultToString);
+ OPENXR_API_INIT_XR_FUNC_V(xrStringToPath);
+ OPENXR_API_INIT_XR_FUNC_V(xrSuggestInteractionProfileBindings);
+ OPENXR_API_INIT_XR_FUNC_V(xrSyncActions);
+ OPENXR_API_INIT_XR_FUNC_V(xrWaitFrame);
+ OPENXR_API_INIT_XR_FUNC_V(xrWaitSwapchainImage);
+
+ return true;
+}
+
+XrResult OpenXRAPI::try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
+ return xrGetInstanceProcAddr(instance, p_name, p_addr);
+}
+
+XrResult OpenXRAPI::get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr) {
+ XrResult result = try_get_instance_proc_addr(p_name, p_addr);
+
+ if (result != XR_SUCCESS) {
+ String error_message = String("Symbol ") + p_name + " not found in OpenXR instance.";
+ ERR_FAIL_COND_V_MSG(true, result, error_message.utf8().get_data());
+ }
+
+ return result;
+}
+
bool OpenXRAPI::initialize(const String &p_rendering_driver) {
ERR_FAIL_COND_V_MSG(instance != XR_NULL_HANDLE, false, "OpenXR instance was already created");
+ if (!openxr_loader_init()) {
+ return false;
+ }
+
if (p_rendering_driver == "vulkan") {
#ifdef VULKAN_ENABLED
- graphics_extension = memnew(OpenXRVulkanExtension(this));
+ graphics_extension = memnew(OpenXRVulkanExtension);
register_extension_wrapper(graphics_extension);
#else
// shouldn't be possible...
@@ -1005,9 +1262,8 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
#endif
} else if (p_rendering_driver == "opengl3") {
#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.");
+ graphics_extension = memnew(OpenXROpenGLExtension);
+ register_extension_wrapper(graphics_extension);
#else
// shouldn't be possible...
ERR_FAIL_V(false);
@@ -1017,6 +1273,10 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
}
// initialize
+ for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
+ wrapper->on_before_instance_created();
+ }
+
if (!load_layer_properties()) {
destroy_instance();
return false;
@@ -1032,6 +1292,11 @@ bool OpenXRAPI::initialize(const String &p_rendering_driver) {
return false;
}
+ if (!resolve_instance_openxr_symbols()) {
+ destroy_instance();
+ return false;
+ }
+
if (!get_system_info()) {
destroy_instance();
return false;
@@ -1088,6 +1353,23 @@ void OpenXRAPI::register_extension_wrapper(OpenXRExtensionWrapper *p_extension_w
registered_extension_wrappers.push_back(p_extension_wrapper);
}
+void OpenXRAPI::unregister_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper) {
+ registered_extension_wrappers.erase(p_extension_wrapper);
+}
+
+void OpenXRAPI::register_extension_metadata() {
+ for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
+ extension_wrapper->on_register_metadata();
+ }
+}
+
+void OpenXRAPI::cleanup_extension_wrappers() {
+ for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
+ memdelete(extension_wrapper);
+ }
+ registered_extension_wrappers.clear();
+}
+
Size2 OpenXRAPI::get_recommended_target_size() {
ERR_FAIL_NULL_V(view_configuration_views, Size2());
@@ -1144,12 +1426,10 @@ XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform,
head_pose_confidence = confidence;
if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_NONE) {
print_line("OpenXR head space location not valid (check tracking?)");
-#ifdef DEBUG
} else if (head_pose_confidence == XRPose::XR_TRACKING_CONFIDENCE_LOW) {
- print_line("OpenVR Head pose now tracking with low confidence");
+ print_verbose("OpenVR Head pose now tracking with low confidence");
} else {
- print_line("OpenVR Head pose now tracking with high confidence");
-#endif
+ print_verbose("OpenVR Head pose now tracking with high confidence");
}
}
@@ -1189,6 +1469,15 @@ bool OpenXRAPI::get_view_projection(uint32_t p_view, double p_z_near, double p_z
return false;
}
+ // if we're using depth views, make sure we update our near and far there...
+ if (depth_views != nullptr) {
+ for (uint32_t i = 0; i < view_count; i++) {
+ depth_views[i].nearZ = p_z_near;
+ depth_views[i].farZ = p_z_far;
+ }
+ }
+
+ // now update our projection
return graphics_extension->create_projection_fov(views[p_view].fov, p_z_near, p_z_far, p_camera_matrix);
}
@@ -1228,7 +1517,7 @@ bool OpenXRAPI::poll_events() {
// TODO We get this event if we're about to loose our OpenXR instance.
// We should queue exiting Godot at this point.
- print_verbose("OpenXR EVENT: instance loss pending at " + itos(event->lossTime));
+ print_verbose(String("OpenXR EVENT: instance loss pending at ") + itos(event->lossTime));
return false;
} break;
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
@@ -1236,9 +1525,9 @@ bool OpenXRAPI::poll_events() {
session_state = event->state;
if (session_state >= XR_SESSION_STATE_MAX_ENUM) {
- print_verbose("OpenXR EVENT: session state changed to UNKNOWN - " + itos(session_state));
+ print_verbose(String("OpenXR EVENT: session state changed to UNKNOWN - ") + itos(session_state));
} else {
- print_verbose("OpenXR EVENT: session state changed to " + OpenXRUtil::get_session_state_name(session_state));
+ print_verbose(String("OpenXR EVENT: session state changed to ") + OpenXRUtil::get_session_state_name(session_state));
switch (session_state) {
case XR_SESSION_STATE_IDLE:
@@ -1273,7 +1562,7 @@ bool OpenXRAPI::poll_events() {
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent;
- print_verbose("OpenXR EVENT: reference space type " + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!");
+ print_verbose(String("OpenXR EVENT: reference space type ") + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!");
if (event->poseValid && xr_interface) {
xr_interface->on_pose_recentered();
}
@@ -1292,7 +1581,7 @@ bool OpenXRAPI::poll_events() {
} break;
default:
if (!handled) {
- print_verbose("OpenXR Unhandled event type " + OpenXRUtil::get_structure_type_name(runtimeEvent.type));
+ print_verbose(String("OpenXR Unhandled event type ") + OpenXRUtil::get_structure_type_name(runtimeEvent.type));
}
break;
}
@@ -1327,15 +1616,15 @@ bool OpenXRAPI::process() {
return true;
}
-bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index) {
- ERR_FAIL_COND_V(image_acquired, true); // this was not released when it should be, error out and re-use...
+bool OpenXRAPI::acquire_image(OpenXRSwapChainInfo &p_swapchain) {
+ ERR_FAIL_COND_V(p_swapchain.image_acquired, true); // this was not released when it should be, error out and re-use...
XrResult result;
XrSwapchainImageAcquireInfo swapchain_image_acquire_info = {
XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, // type
nullptr // next
};
- result = xrAcquireSwapchainImage(p_swapchain, &swapchain_image_acquire_info, &r_image_index);
+ result = xrAcquireSwapchainImage(p_swapchain.swapchain, &swapchain_image_acquire_info, &p_swapchain.image_index);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to acquire swapchain image [", get_error_string(result), "]");
return false;
@@ -1347,7 +1636,7 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index)
17000000 // timeout in nanoseconds
};
- result = xrWaitSwapchainImage(p_swapchain, &swapchain_image_wait_info);
+ result = xrWaitSwapchainImage(p_swapchain.swapchain, &swapchain_image_wait_info);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to wait for swapchain image [", get_error_string(result), "]");
return false;
@@ -1356,12 +1645,12 @@ bool OpenXRAPI::acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index)
return true;
}
-bool OpenXRAPI::release_image(XrSwapchain p_swapchain) {
+bool OpenXRAPI::release_image(OpenXRSwapChainInfo &p_swapchain) {
XrSwapchainImageReleaseInfo swapchain_image_release_info = {
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, // type
nullptr // next
};
- XrResult result = xrReleaseSwapchainImage(swapchain, &swapchain_image_release_info);
+ XrResult result = xrReleaseSwapchainImage(p_swapchain.swapchain, &swapchain_image_release_info);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to release swapchain image! [", get_error_string(result), "]");
return false;
@@ -1382,6 +1671,10 @@ void OpenXRAPI::pre_render() {
// 2) It will use the previous timing to pause our thread so that rendering starts as close to displaying as possible
// This must thus be called as close to when we start rendering as possible
XrFrameWaitInfo frame_wait_info = { XR_TYPE_FRAME_WAIT_INFO, nullptr };
+ frame_state.predictedDisplayTime = 0;
+ frame_state.predictedDisplayPeriod = 0;
+ frame_state.shouldRender = false;
+
XrResult result = xrWaitFrame(session, &frame_wait_info, &frame_state);
if (XR_FAILED(result)) {
print_line("OpenXR: xrWaitFrame() was not successful [", get_error_string(result), "]");
@@ -1396,7 +1689,7 @@ void OpenXRAPI::pre_render() {
if (frame_state.predictedDisplayPeriod > 500000000) {
// display period more then 0.5 seconds? must be wrong data
- print_verbose("OpenXR resetting invalid display period " + rtos(frame_state.predictedDisplayPeriod));
+ print_verbose(String("OpenXR resetting invalid display period ") + rtos(frame_state.predictedDisplayPeriod));
frame_state.predictedDisplayPeriod = 0;
}
@@ -1443,13 +1736,11 @@ void OpenXRAPI::pre_render() {
}
if (view_pose_valid != pose_valid) {
view_pose_valid = pose_valid;
-#ifdef DEBUG
if (!view_pose_valid) {
- print_line("OpenXR View pose became invalid");
+ print_verbose("OpenXR View pose became invalid");
} else {
- print_line("OpenXR View pose became valid");
+ print_verbose("OpenXR View pose became valid");
}
-#endif
}
// let's start our frame..
@@ -1471,28 +1762,41 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
// TODO: at some point in time we may support multiple viewports in which case we need to handle that...
+ // Acquire our images
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ if (!swapchains[i].image_acquired && swapchains[i].swapchain != XR_NULL_HANDLE) {
+ if (!acquire_image(swapchains[i])) {
+ return false;
+ }
+ swapchains[i].image_acquired = true;
+ }
+ }
+
return true;
}
+RID OpenXRAPI::get_color_texture() {
+ if (swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
+ return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_COLOR].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_COLOR].image_index);
+ } else {
+ return RID();
+ }
+}
+
+RID OpenXRAPI::get_depth_texture() {
+ if (submit_depth_buffer && swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) {
+ return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index);
+ } else {
+ return RID();
+ }
+}
+
void OpenXRAPI::post_draw_viewport(RID p_render_target) {
if (!can_render()) {
return;
}
- // TODO: at some point in time we may support multiple viewports in which case we need to handle that...
-
- // TODO: if we can get PR 51179 to work properly we can change away from this approach and move this into get_external_texture or something
- if (!image_acquired) {
- if (!acquire_image(swapchain, image_index)) {
- return;
- }
- image_acquired = true;
-
- // print_line("OpenXR: acquired image " + itos(image_index) + ", copying...");
-
- // Copy our buffer into our swap chain (remove once PR 51179 is done)
- graphics_extension->copy_render_target_to_image(p_render_target, swapchain_graphics_data, image_index);
- }
+ // Nothing to do here at this point in time...
};
void OpenXRAPI::end_frame() {
@@ -1504,7 +1808,7 @@ void OpenXRAPI::end_frame() {
return;
}
- if (frame_state.shouldRender && view_pose_valid && !image_acquired) {
+ if (frame_state.shouldRender && view_pose_valid && !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
print_line("OpenXR: No viewport was marked with use_xr, there is no rendered output!");
}
@@ -1512,7 +1816,7 @@ void OpenXRAPI::end_frame() {
// - shouldRender set to true
// - a valid view pose for projection_views[eye].pose to submit layer
// - an image to render
- if (!frame_state.shouldRender || !view_pose_valid || !image_acquired) {
+ if (!frame_state.shouldRender || !view_pose_valid || !swapchains[OPENXR_SWAPCHAIN_COLOR].image_acquired) {
// submit 0 layers when we shouldn't render
XrFrameEndInfo frame_end_info = {
XR_TYPE_FRAME_END_INFO, // type
@@ -1533,10 +1837,12 @@ void OpenXRAPI::end_frame() {
}
// release our swapchain image if we acquired it
- if (image_acquired) {
- image_acquired = false; // whether we succeed or not, consider this released.
+ for (int i = 0; i < OPENXR_SWAPCHAIN_MAX; i++) {
+ if (swapchains[i].image_acquired) {
+ swapchains[i].image_acquired = false; // whether we succeed or not, consider this released.
- release_image(swapchain);
+ release_image(swapchains[i]);
+ }
}
for (uint32_t eye = 0; eye < view_count; eye++) {
@@ -1557,7 +1863,7 @@ void OpenXRAPI::end_frame() {
XrCompositionLayerProjection projection_layer = {
XR_TYPE_COMPOSITION_LAYER_PROJECTION, // type
nullptr, // next
- layers_list.size() > 1 ? XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT : XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, // layerFlags
+ layers_list.size() > 0 ? XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT : XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, // layerFlags
play_space, // space
view_count, // viewCount
projection_views, // views
@@ -1579,6 +1885,31 @@ void OpenXRAPI::end_frame() {
}
}
+float OpenXRAPI::get_display_refresh_rate() const {
+ OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
+ if (drrext) {
+ return drrext->get_refresh_rate();
+ }
+
+ return 0.0;
+}
+
+void OpenXRAPI::set_display_refresh_rate(float p_refresh_rate) {
+ OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
+ if (drrext != nullptr) {
+ drrext->set_refresh_rate(p_refresh_rate);
+ }
+}
+
+Array OpenXRAPI::get_available_display_refresh_rates() const {
+ OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton();
+ if (drrext != nullptr) {
+ return drrext->get_available_refresh_rates();
+ }
+
+ return Array();
+}
+
OpenXRAPI::OpenXRAPI() {
// OpenXRAPI is only constructed if OpenXR is enabled.
singleton = this;
@@ -1631,19 +1962,13 @@ OpenXRAPI::OpenXRAPI() {
default:
break;
}
+
+ submit_depth_buffer = GLOBAL_GET("xr/openxr/submit_depth_buffer");
}
// reset a few things that can't be done in our class definition
frame_state.predictedDisplayTime = 0;
frame_state.predictedDisplayPeriod = 0;
-
-#ifdef ANDROID_ENABLED
- // 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() {
@@ -1653,12 +1978,6 @@ OpenXRAPI::~OpenXRAPI() {
}
composition_layer_providers.clear();
- // cleanup our extension wrappers
- for (OpenXRExtensionWrapper *extension_wrapper : registered_extension_wrappers) {
- memdelete(extension_wrapper);
- }
- registered_extension_wrappers.clear();
-
if (supported_extensions != nullptr) {
memfree(supported_extensions);
supported_extensions = nullptr;
@@ -1669,6 +1988,13 @@ OpenXRAPI::~OpenXRAPI() {
layer_properties = nullptr;
}
+#ifdef ANDROID_ENABLED
+ if (openxr_loader_library_handle) {
+ OS::get_singleton()->close_dynamic_library(openxr_loader_library_handle);
+ openxr_loader_library_handle = nullptr;
+ }
+#endif
+
singleton = nullptr;
}
@@ -1745,6 +2071,18 @@ void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_l
}
}
+bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const {
+ if (XR_SUCCEEDED(result))
+ return true;
+
+ char resultString[XR_MAX_RESULT_STRING_SIZE];
+ xrResultToString(instance, result, resultString);
+
+ print_error(String("OpenXR ") + String(format).format(args) + String(" [") + String(resultString) + String("]"));
+
+ return false;
+}
+
RID OpenXRAPI::get_tracker_rid(XrPath p_path) {
List<RID> current;
tracker_owner.get_owned_list(&current);
@@ -1850,8 +2188,6 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
copy_string_to_char_buffer(p_name, action_set_info.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE);
copy_string_to_char_buffer(p_localized_name, action_set_info.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE);
- // print_line("Creating action set ", action_set_info.actionSetName, " - ", action_set_info.localizedActionSetName, " (", itos(action_set_info.priority), ")");
-
XrResult result = xrCreateActionSet(instance, &action_set_info, &action_set.handle);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to create action set ", p_name, "! [", get_error_string(result), "]");
@@ -2005,8 +2341,6 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
copy_string_to_char_buffer(p_name, action_info.actionName, XR_MAX_ACTION_NAME_SIZE);
copy_string_to_char_buffer(p_localized_name, action_info.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
- // print_line("Creating action ", action_info.actionName, action_info.localizedActionName, action_info.countSubactionPaths);
-
XrResult result = xrCreateAction(action_set->handle, &action_info, &action.handle);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to create action ", p_name, "! [", get_error_string(result), "]");
@@ -2063,6 +2397,11 @@ XrPath OpenXRAPI::get_interaction_profile_path(RID p_interaction_profile) {
}
RID OpenXRAPI::interaction_profile_create(const String p_name) {
+ if (!is_interaction_profile_supported(p_name)) {
+ // The extension enabling this path must not be active, we will silently skip this interaction profile
+ return RID();
+ }
+
InteractionProfile new_interaction_profile;
XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_interaction_profile.path);
@@ -2102,6 +2441,10 @@ bool OpenXRAPI::interaction_profile_add_binding(RID p_interaction_profile, RID p
InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
ERR_FAIL_NULL_V(ip, false);
+ if (!interaction_profile_supports_io_path(ip->name, p_path)) {
+ return false;
+ }
+
XrActionSuggestedBinding binding;
Action *action = action_owner.get_or_null(p_action);
@@ -2426,3 +2769,11 @@ bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_tracker, float p_freque
return true;
}
+
+void OpenXRAPI::register_composition_layer_provider(OpenXRCompositionLayerProvider *provider) {
+ composition_layer_providers.append(provider);
+}
+
+void OpenXRAPI::unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider) {
+ composition_layer_providers.erase(provider);
+}
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index dc224c4237..52a1af5a09 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_api.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_api.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_API_H
#define OPENXR_API_H
@@ -36,6 +36,7 @@
#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/os/memory.h"
+#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "core/templates/rb_map.h"
#include "core/templates/rid_owner.h"
@@ -50,6 +51,8 @@
#include "extensions/openxr_composition_layer_provider.h"
#include "extensions/openxr_extension_wrapper.h"
+#include "util.h"
+
// Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initialising structs which ensures zeroing out unspecified members.
// Godot is currently restricted to C++17 which doesn't allow this notation. Make sure critical fields are set.
@@ -62,6 +65,9 @@ private:
// our singleton
static OpenXRAPI *singleton;
+ // Registered extension wrappers
+ static Vector<OpenXRExtensionWrapper *> registered_extension_wrappers;
+
// linked XR interface
OpenXRInterface *xr_interface = nullptr;
@@ -72,15 +78,8 @@ private:
// extensions
uint32_t num_supported_extensions = 0;
XrExtensionProperties *supported_extensions = nullptr;
- Vector<OpenXRExtensionWrapper *> registered_extension_wrappers;
Vector<CharString> enabled_extensions;
- bool ext_hp_mixed_reality_available = false;
- bool ext_samsung_odyssey_available = false;
- bool ext_vive_cosmos_available = false;
- bool ext_vive_focus3_available = false;
- bool ext_huawei_controller_available = false;
-
// composition layer providers
Vector<OpenXRCompositionLayerProvider *> composition_layer_providers;
@@ -100,7 +99,8 @@ private:
XrFormFactor form_factor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
XrViewConfigurationType view_configuration = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
XrReferenceSpaceType reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
- XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+ // XrEnvironmentBlendMode environment_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
+ bool submit_depth_buffer = false; // if set to true we submit depth buffers to OpenXR if a suitable extension is enabled.
// state
XrInstance instance = XR_NULL_HANDLE;
@@ -115,15 +115,28 @@ private:
OpenXRGraphicsExtensionWrapper *graphics_extension = nullptr;
XrSystemGraphicsProperties graphics_properties;
- void *swapchain_graphics_data = nullptr;
- uint32_t image_index = 0;
- bool image_acquired = false;
uint32_t view_count = 0;
XrViewConfigurationView *view_configuration_views = nullptr;
XrView *views = nullptr;
XrCompositionLayerProjectionView *projection_views = nullptr;
- XrSwapchain swapchain = XR_NULL_HANDLE;
+ XrCompositionLayerDepthInfoKHR *depth_views = nullptr; // Only used by Composition Layer Depth Extension if available
+
+ enum OpenXRSwapChainTypes {
+ OPENXR_SWAPCHAIN_COLOR,
+ OPENXR_SWAPCHAIN_DEPTH,
+ // OPENXR_SWAPCHAIN_VELOCITY,
+ OPENXR_SWAPCHAIN_MAX
+ };
+
+ struct OpenXRSwapChainInfo {
+ XrSwapchain swapchain = XR_NULL_HANDLE;
+ void *swapchain_graphics_data = nullptr;
+ uint32_t image_index = 0;
+ bool image_acquired = false;
+ };
+
+ OpenXRSwapChainInfo swapchains[OPENXR_SWAPCHAIN_MAX];
XrSpace play_space = XR_NULL_HANDLE;
XrSpace view_space = XR_NULL_HANDLE;
@@ -133,6 +146,65 @@ private:
bool load_layer_properties();
bool load_supported_extensions();
bool is_extension_supported(const String &p_extension) const;
+ bool is_extension_enabled(const String &p_extension) const;
+
+ bool openxr_loader_init();
+ bool resolve_instance_openxr_symbols();
+
+#ifdef ANDROID_ENABLED
+ // On Android we keep tracker of our external OpenXR loader
+ void *openxr_loader_library_handle = nullptr;
+#endif
+
+ // function pointers
+#ifdef ANDROID_ENABLED
+ // On non-Android platforms we use the OpenXR symbol linked into the engine binary.
+ PFN_xrGetInstanceProcAddr xrGetInstanceProcAddr = nullptr;
+#endif
+ EXT_PROTO_XRRESULT_FUNC3(xrAcquireSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageAcquireInfo *), acquireInfo, (uint32_t *), index)
+ EXT_PROTO_XRRESULT_FUNC3(xrApplyHapticFeedback, (XrSession), session, (const XrHapticActionInfo *), hapticActionInfo, (const XrHapticBaseHeader *), hapticFeedback)
+ EXT_PROTO_XRRESULT_FUNC2(xrAttachSessionActionSets, (XrSession), session, (const XrSessionActionSetsAttachInfo *), attachInfo)
+ EXT_PROTO_XRRESULT_FUNC2(xrBeginFrame, (XrSession), session, (const XrFrameBeginInfo *), frameBeginInfo)
+ EXT_PROTO_XRRESULT_FUNC2(xrBeginSession, (XrSession), session, (const XrSessionBeginInfo *), beginInfo)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateAction, (XrActionSet), actionSet, (const XrActionCreateInfo *), createInfo, (XrAction *), action)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateActionSet, (XrInstance), instance, (const XrActionSetCreateInfo *), createInfo, (XrActionSet *), actionSet)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateActionSpace, (XrSession), session, (const XrActionSpaceCreateInfo *), createInfo, (XrSpace *), space)
+ EXT_PROTO_XRRESULT_FUNC2(xrCreateInstance, (const XrInstanceCreateInfo *), createInfo, (XrInstance *), instance)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateReferenceSpace, (XrSession), session, (const XrReferenceSpaceCreateInfo *), createInfo, (XrSpace *), space)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateSession, (XrInstance), instance, (const XrSessionCreateInfo *), createInfo, (XrSession *), session)
+ EXT_PROTO_XRRESULT_FUNC3(xrCreateSwapchain, (XrSession), session, (const XrSwapchainCreateInfo *), createInfo, (XrSwapchain *), swapchain)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyAction, (XrAction), action)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyActionSet, (XrActionSet), actionSet)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroyInstance, (XrInstance), instance)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroySession, (XrSession), session)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroySpace, (XrSpace), space)
+ EXT_PROTO_XRRESULT_FUNC1(xrDestroySwapchain, (XrSwapchain), swapchain)
+ EXT_PROTO_XRRESULT_FUNC2(xrEndFrame, (XrSession), session, (const XrFrameEndInfo *), frameEndInfo)
+ EXT_PROTO_XRRESULT_FUNC1(xrEndSession, (XrSession), session)
+ EXT_PROTO_XRRESULT_FUNC3(xrEnumerateApiLayerProperties, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrApiLayerProperties *), properties)
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateInstanceExtensionProperties, (const char *), layerName, (uint32_t), propertyCapacityInput, (uint32_t *), propertyCountOutput, (XrExtensionProperties *), properties)
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateReferenceSpaces, (XrSession), session, (uint32_t), spaceCapacityInput, (uint32_t *), spaceCountOutput, (XrReferenceSpaceType *), spaces)
+ EXT_PROTO_XRRESULT_FUNC4(xrEnumerateSwapchainFormats, (XrSession), session, (uint32_t), formatCapacityInput, (uint32_t *), formatCountOutput, (int64_t *), formats)
+ EXT_PROTO_XRRESULT_FUNC5(xrEnumerateViewConfigurations, (XrInstance), instance, (XrSystemId), systemId, (uint32_t), viewConfigurationTypeCapacityInput, (uint32_t *), viewConfigurationTypeCountOutput, (XrViewConfigurationType *), viewConfigurationTypes)
+ EXT_PROTO_XRRESULT_FUNC6(xrEnumerateViewConfigurationViews, (XrInstance), instance, (XrSystemId), systemId, (XrViewConfigurationType), viewConfigurationType, (uint32_t), viewCapacityInput, (uint32_t *), viewCountOutput, (XrViewConfigurationView *), views)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateBoolean, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateBoolean *), state)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateFloat, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateFloat *), state)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetActionStateVector2f, (XrSession), session, (const XrActionStateGetInfo *), getInfo, (XrActionStateVector2f *), state)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetCurrentInteractionProfile, (XrSession), session, (XrPath), topLevelUserPath, (XrInteractionProfileState *), interactionProfile)
+ EXT_PROTO_XRRESULT_FUNC2(xrGetInstanceProperties, (XrInstance), instance, (XrInstanceProperties *), instanceProperties)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetSystem, (XrInstance), instance, (const XrSystemGetInfo *), getInfo, (XrSystemId *), systemId)
+ EXT_PROTO_XRRESULT_FUNC3(xrGetSystemProperties, (XrInstance), instance, (XrSystemId), systemId, (XrSystemProperties *), properties)
+ EXT_PROTO_XRRESULT_FUNC4(xrLocateSpace, (XrSpace), space, (XrSpace), baseSpace, (XrTime), time, (XrSpaceLocation *), location)
+ EXT_PROTO_XRRESULT_FUNC6(xrLocateViews, (XrSession), session, (const XrViewLocateInfo *), viewLocateInfo, (XrViewState *), viewState, (uint32_t), viewCapacityInput, (uint32_t *), viewCountOutput, (XrView *), views)
+ EXT_PROTO_XRRESULT_FUNC5(xrPathToString, (XrInstance), instance, (XrPath), path, (uint32_t), bufferCapacityInput, (uint32_t *), bufferCountOutput, (char *), buffer)
+ EXT_PROTO_XRRESULT_FUNC2(xrPollEvent, (XrInstance), instance, (XrEventDataBuffer *), eventData)
+ EXT_PROTO_XRRESULT_FUNC2(xrReleaseSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageReleaseInfo *), releaseInfo)
+ EXT_PROTO_XRRESULT_FUNC3(xrResultToString, (XrInstance), instance, (XrResult), value, (char *), buffer)
+ EXT_PROTO_XRRESULT_FUNC3(xrStringToPath, (XrInstance), instance, (const char *), pathString, (XrPath *), path)
+ EXT_PROTO_XRRESULT_FUNC2(xrSuggestInteractionProfileBindings, (XrInstance), instance, (const XrInteractionProfileSuggestedBinding *), suggestedBindings)
+ EXT_PROTO_XRRESULT_FUNC2(xrSyncActions, (XrSession), session, (const XrActionsSyncInfo *), syncInfo)
+ EXT_PROTO_XRRESULT_FUNC3(xrWaitFrame, (XrSession), session, (const XrFrameWaitInfo *), frameWaitInfo, (XrFrameState *), frameState)
+ EXT_PROTO_XRRESULT_FUNC2(xrWaitSwapchainImage, (XrSwapchain), swapchain, (const XrSwapchainImageWaitInfo *), waitInfo)
// instance
bool create_instance();
@@ -149,13 +221,13 @@ private:
bool setup_spaces();
bool load_supported_swapchain_formats();
bool is_swapchain_format_supported(int64_t p_swapchain_format);
- bool create_main_swapchain();
+ bool create_swapchains();
void destroy_session();
// swapchains
- bool create_swapchain(int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
- bool acquire_image(XrSwapchain p_swapchain, uint32_t &r_image_index);
- bool release_image(XrSwapchain p_swapchain);
+ bool create_swapchain(XrSwapchainUsageFlags p_usage_flags, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, XrSwapchain &r_swapchain, void **r_swapchain_graphics_data);
+ bool acquire_image(OpenXRSwapChainInfo &p_swapchain);
+ bool release_image(OpenXRSwapChainInfo &p_swapchain);
// action map
struct Tracker { // Trackers represent tracked physical objects such as controllers, pucks, etc.
@@ -212,9 +284,7 @@ private:
// convencience
void copy_string_to_char_buffer(const String p_string, char *p_buffer, int p_buffer_len);
-protected:
- friend class OpenXRVulkanExtension;
-
+public:
XrInstance get_instance() const { return instance; };
XrSystemId get_system_id() const { return system_id; };
XrSession get_session() const { return session; };
@@ -227,15 +297,36 @@ protected:
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);
-public:
+ bool xr_result(XrResult result, const char *format, Array args = Array()) const;
+ bool is_top_level_path_supported(const String &p_toplevel_path);
+ bool is_interaction_profile_supported(const String &p_ip_path);
+ bool interaction_profile_supports_io_path(const String &p_ip_path, const String &p_io_path);
+
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
- static OpenXRAPI *get_singleton();
+ _FORCE_INLINE_ static OpenXRAPI *get_singleton() { return singleton; }
+ XrResult try_get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
+ XrResult get_instance_proc_addr(const char *p_name, PFN_xrVoidFunction *p_addr);
String get_error_string(XrResult result);
String get_swapchain_format_name(int64_t p_swapchain_format) const;
void set_xr_interface(OpenXRInterface *p_xr_interface);
- void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
+ static void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
+ static void unregister_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
+ static void register_extension_metadata();
+ static void cleanup_extension_wrappers();
+
+ void set_form_factor(XrFormFactor p_form_factor);
+ XrFormFactor get_form_factor() const { return form_factor; }
+
+ void set_view_configuration(XrViewConfigurationType p_view_configuration);
+ XrViewConfigurationType get_view_configuration() const { return view_configuration; }
+
+ void set_reference_space(XrReferenceSpaceType p_reference_space);
+ XrReferenceSpaceType get_reference_space() const { return reference_space; }
+
+ void set_submit_depth_buffer(bool p_submit_depth_buffer);
+ bool get_submit_depth_buffer() const { return submit_depth_buffer; }
bool is_initialized();
bool is_running();
@@ -243,8 +334,9 @@ public:
bool initialize_session();
void finish();
- XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; };
- bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; };
+ XrSpace get_play_space() const { return play_space; }
+ XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
+ 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, Vector3 &r_angular_velocity);
@@ -254,9 +346,16 @@ public:
void pre_render();
bool pre_draw_viewport(RID p_render_target);
+ RID get_color_texture();
+ RID get_depth_texture();
void post_draw_viewport(RID p_render_target);
void end_frame();
+ // Display refresh rate
+ float get_display_refresh_rate() const;
+ void set_display_refresh_rate(float p_refresh_rate);
+ Array get_available_display_refresh_rates() const;
+
// action map
String get_default_action_map_resource_name();
@@ -288,6 +387,9 @@ public:
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);
+ void register_composition_layer_provider(OpenXRCompositionLayerProvider *provider);
+ void unregister_composition_layer_provider(OpenXRCompositionLayerProvider *provider);
+
OpenXRAPI();
~OpenXRAPI();
};
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 6c2f08e21d..f9afdf2d4a 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_interface.h"
@@ -41,6 +41,13 @@ void OpenXRInterface::_bind_methods() {
ADD_SIGNAL(MethodInfo("session_focussed"));
ADD_SIGNAL(MethodInfo("session_visible"));
ADD_SIGNAL(MethodInfo("pose_recentered"));
+
+ // Display refresh rate
+ ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate);
+ ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");
+
+ ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);
}
StringName OpenXRInterface::get_name() const {
@@ -89,7 +96,7 @@ void OpenXRInterface::_load_action_map() {
// This may seem a bit duplicitous to a little bit of background info here.
// OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in.
- // This gives the user the ability to edit the action map in a UI and customise the actions.
+ // This gives the user the ability to edit the action map in a UI and customize the actions.
// OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it.
// This system does that push and we store the info needed to then work with this action map going forward.
@@ -132,10 +139,10 @@ void OpenXRInterface::_load_action_map() {
if (action_map.is_valid()) {
HashMap<Ref<OpenXRAction>, Action *> xr_actions;
- Array action_sets = action_map->get_action_sets();
- for (int i = 0; i < action_sets.size(); i++) {
+ Array action_set_array = action_map->get_action_sets();
+ for (int i = 0; i < action_set_array.size(); i++) {
// Create our action set
- Ref<OpenXRActionSet> xr_action_set = action_sets[i];
+ Ref<OpenXRActionSet> xr_action_set = action_set_array[i];
ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority());
if (!action_set) {
continue;
@@ -147,62 +154,65 @@ void OpenXRInterface::_load_action_map() {
Ref<OpenXRAction> xr_action = actions[j];
PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();
- Vector<Tracker *> trackers;
+ Vector<Tracker *> trackers_for_action;
for (int k = 0; k < toplevel_paths.size(); k++) {
- Tracker *tracker = find_tracker(toplevel_paths[k], true);
- if (tracker) {
- trackers.push_back(tracker);
+ // Only check for our tracker if our path is supported.
+ if (openxr_api->is_top_level_path_supported(toplevel_paths[k])) {
+ Tracker *tracker = find_tracker(toplevel_paths[k], true);
+ if (tracker) {
+ trackers_for_action.push_back(tracker);
+ }
}
}
- Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers);
- if (action) {
- // we link our actions back to our trackers so we know which actions to check when we're processing our trackers
- for (int t = 0; t < trackers.size(); t++) {
- link_action_to_tracker(trackers[t], action);
+ // Only add our action if we have at least one valid toplevel path
+ if (trackers_for_action.size() > 0) {
+ Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action);
+ if (action) {
+ // add this to our map for creating our interaction profiles
+ xr_actions[xr_action] = action;
}
-
- // add this to our map for creating our interaction profiles
- xr_actions[xr_action] = action;
}
}
}
// now do our suggestions
- Array interaction_profiles = action_map->get_interaction_profiles();
- for (int i = 0; i < interaction_profiles.size(); i++) {
- Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profiles[i];
+ Array interaction_profile_array = action_map->get_interaction_profiles();
+ for (int i = 0; i < interaction_profile_array.size(); i++) {
+ Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i];
// Note, we can only have one entry per interaction profile so if it already exists we clear it out
RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path());
- openxr_api->interaction_profile_clear_bindings(ip);
-
- Array xr_bindings = xr_interaction_profile->get_bindings();
- for (int j = 0; j < xr_bindings.size(); j++) {
- Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];
- Ref<OpenXRAction> xr_action = xr_binding->get_action();
-
- Action *action = nullptr;
- if (xr_actions.has(xr_action)) {
- action = xr_actions[xr_action];
- } else {
- print_line("Action ", xr_action->get_name(), " isn't part of an action set!");
- continue;
- }
+ if (ip.is_valid()) {
+ openxr_api->interaction_profile_clear_bindings(ip);
+
+ Array xr_bindings = xr_interaction_profile->get_bindings();
+ for (int j = 0; j < xr_bindings.size(); j++) {
+ Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];
+ Ref<OpenXRAction> xr_action = xr_binding->get_action();
+
+ Action *action = nullptr;
+ if (xr_actions.has(xr_action)) {
+ action = xr_actions[xr_action];
+ } else {
+ print_line("Action ", xr_action->get_name(), " isn't part of an action set!");
+ continue;
+ }
- PackedStringArray paths = xr_binding->get_paths();
- for (int k = 0; k < paths.size(); k++) {
- openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]);
+ PackedStringArray paths = xr_binding->get_paths();
+ for (int k = 0; k < paths.size(); k++) {
+ openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]);
+ }
}
- }
- // Now submit our suggestions
- openxr_api->interaction_profile_suggest_bindings(ip);
+ // Now submit our suggestions
+ openxr_api->interaction_profile_suggest_bindings(ip);
- // And record it in our array so we can clean it up later on
- if (interaction_profiles.has(ip)) {
- interaction_profiles.push_back(ip);
+ // And record it in our array so we can clean it up later on
+ if (interaction_profile_array.has(ip)) {
+ interaction_profile_array.push_back(ip);
+ }
}
}
}
@@ -280,6 +290,13 @@ OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set,
action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids);
p_action_set->actions.push_back(action);
+ // we link our actions back to our trackers so we know which actions to check when we're processing our trackers
+ for (int i = 0; i < p_trackers.size(); i++) {
+ if (p_trackers[i]->actions.find(action) == -1) {
+ p_trackers[i]->actions.push_back(action);
+ }
+ }
+
return action;
}
@@ -328,6 +345,8 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_
return nullptr;
}
+ ERR_FAIL_COND_V(!openxr_api->is_top_level_path_supported(p_tracker_name), nullptr);
+
// Create our RID
RID tracker_rid = openxr_api->tracker_create(p_tracker_name);
ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);
@@ -336,7 +355,7 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_
Ref<XRPositionalTracker> positional_tracker;
positional_tracker.instantiate();
- // We have standardised some names to make things nicer to the user so lets recognise the toplevel paths related to these.
+ // We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these.
if (p_tracker_name == "/user/hand/left") {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name("left_hand");
@@ -387,12 +406,6 @@ void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_p
}
}
-void OpenXRInterface::link_action_to_tracker(Tracker *p_tracker, Action *p_action) {
- if (p_tracker->actions.find(p_action) == -1) {
- p_tracker->actions.push_back(p_action);
- }
-}
-
void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
ERR_FAIL_NULL(openxr_api);
ERR_FAIL_COND(p_tracker->positional_tracker.is_null());
@@ -445,9 +458,18 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {
ERR_FAIL_NULL(openxr_api);
+
Action *action = find_action(p_action_name);
ERR_FAIL_NULL(action);
- Tracker *tracker = find_tracker(p_tracker_name);
+
+ // We need to map our tracker name to our OpenXR name for our inbuild names.
+ String tracker_name = p_tracker_name;
+ if (tracker_name == "left_hand") {
+ tracker_name = "/user/hand/left";
+ } else if (tracker_name == "right_hand") {
+ tracker_name = "/user/hand/right";
+ }
+ Tracker *tracker = find_tracker(tracker_name);
ERR_FAIL_NULL(tracker);
// TODO OpenXR does not support delay, so we may need to add support for that somehow...
@@ -569,6 +591,36 @@ bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
return false;
}
+float OpenXRInterface::get_display_refresh_rate() const {
+ if (openxr_api == nullptr) {
+ return 0.0;
+ } else if (!openxr_api->is_initialized()) {
+ return 0.0;
+ } else {
+ return openxr_api->get_display_refresh_rate();
+ }
+}
+
+void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) {
+ if (openxr_api == nullptr) {
+ return;
+ } else if (!openxr_api->is_initialized()) {
+ return;
+ } else {
+ openxr_api->set_display_refresh_rate(p_refresh_rate);
+ }
+}
+
+Array OpenXRInterface::get_available_display_refresh_rates() const {
+ if (openxr_api == nullptr) {
+ return Array();
+ } else if (!openxr_api->is_initialized()) {
+ return Array();
+ } else {
+ return openxr_api->get_available_display_refresh_rates();
+ }
+}
+
Size2 OpenXRInterface::get_render_target_size() {
if (openxr_api == nullptr) {
return Size2();
@@ -614,6 +666,7 @@ Transform3D OpenXRInterface::get_camera_transform() {
Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, Transform3D());
+ ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds.");
Transform3D t;
if (openxr_api && openxr_api->get_view_transform(p_view, t)) {
@@ -633,6 +686,7 @@ Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Trans
Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
Projection cm;
+ ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds.");
if (openxr_api) {
if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {
@@ -646,6 +700,22 @@ Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_as
return cm;
}
+RID OpenXRInterface::get_color_texture() {
+ if (openxr_api) {
+ return openxr_api->get_color_texture();
+ } else {
+ return RID();
+ }
+}
+
+RID OpenXRInterface::get_depth_texture() {
+ if (openxr_api) {
+ return openxr_api->get_depth_texture();
+ } else {
+ return RID();
+ }
+}
+
void OpenXRInterface::process() {
if (openxr_api) {
// do our normal process
@@ -705,6 +775,7 @@ bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {
Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
Vector<BlitToScreen> blit_to_screen;
+#ifndef ANDROID_ENABLED
// If separate HMD we should output one eye to screen
if (p_screen_rect != Rect2()) {
BlitToScreen blit;
@@ -730,6 +801,7 @@ Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, co
blit.dst_rect = dst_rect;
blit_to_screen.push_back(blit);
}
+#endif
if (openxr_api) {
openxr_api->post_draw_viewport(p_render_target);
@@ -744,6 +816,24 @@ void OpenXRInterface::end_frame() {
}
}
+bool OpenXRInterface::is_passthrough_supported() {
+ return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_supported();
+}
+
+bool OpenXRInterface::is_passthrough_enabled() {
+ return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_enabled();
+}
+
+bool OpenXRInterface::start_passthrough() {
+ return passthrough_wrapper != nullptr && passthrough_wrapper->start_passthrough();
+}
+
+void OpenXRInterface::stop_passthrough() {
+ if (passthrough_wrapper) {
+ passthrough_wrapper->stop_passthrough();
+ }
+}
+
void OpenXRInterface::on_state_ready() {
emit_signal(SNAME("session_begun"));
}
@@ -774,6 +864,8 @@ OpenXRInterface::OpenXRInterface() {
_set_default_pos(head_transform, 1.0, 0);
_set_default_pos(transform_for_view[0], 1.0, 1);
_set_default_pos(transform_for_view[1], 1.0, 2);
+
+ passthrough_wrapper = OpenXRFbPassthroughExtensionWrapper::get_singleton();
}
OpenXRInterface::~OpenXRInterface() {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index a99012fd1d..2b43369523 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_INTERFACE_H
#define OPENXR_INTERFACE_H
@@ -37,6 +37,8 @@
#include "action_map/openxr_action_map.h"
#include "openxr_api.h"
+#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
+
// declare some default strings
#define INTERACTION_PROFILE_NONE "/interaction_profiles/none"
@@ -47,6 +49,7 @@ private:
OpenXRAPI *openxr_api = nullptr;
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
+ OpenXRFbPassthroughExtensionWrapper *passthrough_wrapper = nullptr;
// At a minimum we need a tracker for our head
Ref<XRPositionalTracker> head;
@@ -88,7 +91,6 @@ private:
void free_actions(ActionSet *p_action_set);
Tracker *find_tracker(const String &p_tracker_name, bool p_create = false);
- void link_action_to_tracker(Tracker *p_tracker, Action *p_action);
void handle_tracker(Tracker *p_tracker);
void free_trackers();
@@ -117,18 +119,30 @@ public:
virtual XRInterface::PlayAreaMode get_play_area_mode() const override;
virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
+ float get_display_refresh_rate() const;
+ void set_display_refresh_rate(float p_refresh_rate);
+ Array get_available_display_refresh_rates() const;
+
virtual Size2 get_render_target_size() override;
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 Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual RID get_color_texture() override;
+ virtual RID get_depth_texture() override;
+
virtual void process() override;
virtual void pre_render() override;
bool pre_draw_viewport(RID p_render_target) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void end_frame() override;
+ virtual bool is_passthrough_supported() override;
+ virtual bool is_passthrough_enabled() override;
+ virtual bool start_passthrough() override;
+ virtual void stop_passthrough() override;
+
void on_state_ready();
void on_state_visible();
void on_state_focused();
diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp
index 230b10c5f1..50ebf468b9 100644
--- a/modules/openxr/openxr_util.cpp
+++ b/modules/openxr/openxr_util.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_util.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_util.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_util.h"
diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h
index a5cc7cd512..dfda537474 100644
--- a/modules/openxr/openxr_util.h
+++ b/modules/openxr/openxr_util.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* openxr_util.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* openxr_util.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_UTIL_H
#define OPENXR_UTIL_H
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index c765f169dc..1c4d53c43a 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "main/main.h"
@@ -37,6 +37,28 @@
#include "action_map/openxr_action_map.h"
#include "action_map/openxr_action_set.h"
#include "action_map/openxr_interaction_profile.h"
+#include "action_map/openxr_interaction_profile_meta_data.h"
+
+#include "scene/openxr_hand.h"
+
+#ifdef ANDROID_ENABLED
+#include "extensions/openxr_android_extension.h"
+#endif
+
+#include "extensions/openxr_composition_layer_depth_extension.h"
+#include "extensions/openxr_fb_display_refresh_rate_extension.h"
+#include "extensions/openxr_fb_passthrough_extension_wrapper.h"
+#include "extensions/openxr_hand_tracking_extension.h"
+#include "extensions/openxr_htc_controller_extension.h"
+#include "extensions/openxr_htc_vive_tracker_extension.h"
+#include "extensions/openxr_huawei_controller_extension.h"
+#include "extensions/openxr_palm_pose_extension.h"
+#include "extensions/openxr_pico_controller_extension.h"
+#include "extensions/openxr_wmr_controller_extension.h"
+
+static OpenXRAPI *openxr_api = nullptr;
+static OpenXRInteractionProfileMetaData *openxr_interaction_profile_meta_data = nullptr;
+static Ref<OpenXRInterface> openxr_interface;
#ifdef TOOLS_ENABLED
@@ -47,6 +69,12 @@ static void _editor_init() {
if (OpenXRAPI::openxr_is_enabled(false)) {
// Only add our OpenXR action map editor if OpenXR is enabled for our project
+ if (openxr_interaction_profile_meta_data == nullptr) {
+ // If we didn't initialize our actionmap meta data at startup, we initialise it now.
+ openxr_interaction_profile_meta_data = memnew(OpenXRInteractionProfileMetaData);
+ ERR_FAIL_NULL(openxr_interaction_profile_meta_data);
+ }
+
OpenXREditorPlugin *openxr_plugin = memnew(OpenXREditorPlugin());
EditorNode::get_singleton()->add_editor_plugin(openxr_plugin);
}
@@ -54,18 +82,39 @@ static void _editor_init() {
#endif
-static OpenXRAPI *openxr_api = nullptr;
-static Ref<OpenXRInterface> openxr_interface;
-
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(false)) {
+ // Always register our extension wrappers even if we don't initialise OpenXR.
+ // Some of these wrappers will add functionality to our editor.
+#ifdef ANDROID_ENABLED
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRAndroidExtension));
+#endif
+
+ // register our other extensions
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRPalmPoseExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRPicoControllerExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRCompositionLayerDepthExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCControllerExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRHuaweiControllerExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandTrackingExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRWMRControllerExtension));
+ }
if (OpenXRAPI::openxr_is_enabled()) {
+ openxr_interaction_profile_meta_data = memnew(OpenXRInteractionProfileMetaData);
+ ERR_FAIL_NULL(openxr_interaction_profile_meta_data);
openxr_api = memnew(OpenXRAPI);
ERR_FAIL_NULL(openxr_api);
if (!openxr_api->initialize(Main::get_rendering_driver_name())) {
+ OS::get_singleton()->alert("OpenXR was requested but failed to start.\n"
+ "Please check if your HMD is connected.\n"
+ "When using Windows MR please note that WMR only has DirectX support, make sure SteamVR is your default OpenXR runtime.\n"
+ "Godot will start in normal mode.\n");
memdelete(openxr_api);
openxr_api = nullptr;
return;
@@ -79,9 +128,12 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(OpenXRAction);
GDREGISTER_CLASS(OpenXRActionSet);
GDREGISTER_CLASS(OpenXRActionMap);
+ GDREGISTER_CLASS(OpenXRInteractionProfileMetaData);
GDREGISTER_CLASS(OpenXRIPBinding);
GDREGISTER_CLASS(OpenXRInteractionProfile);
+ GDREGISTER_CLASS(OpenXRHand);
+
XRServer *xr_server = XRServer::get_singleton();
if (xr_server) {
openxr_interface.instantiate();
@@ -124,7 +176,16 @@ void uninitialize_openxr_module(ModuleInitializationLevel p_level) {
if (openxr_api) {
openxr_api->finish();
+
memdelete(openxr_api);
openxr_api = nullptr;
}
+
+ if (openxr_interaction_profile_meta_data) {
+ memdelete(openxr_interaction_profile_meta_data);
+ openxr_interaction_profile_meta_data = nullptr;
+ }
+
+ // cleanup our extension wrappers
+ OpenXRAPI::cleanup_extension_wrappers();
}
diff --git a/modules/openxr/register_types.h b/modules/openxr/register_types.h
index 1b3d98422d..f783905149 100644
--- a/modules/openxr/register_types.h
+++ b/modules/openxr/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_REGISTER_TYPES_H
#define OPENXR_REGISTER_TYPES_H
diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp
new file mode 100644
index 0000000000..e4bd2dab52
--- /dev/null
+++ b/modules/openxr/scene/openxr_hand.cpp
@@ -0,0 +1,307 @@
+/**************************************************************************/
+/* openxr_hand.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "../extensions/openxr_hand_tracking_extension.h"
+#include "../openxr_api.h"
+
+#include "openxr_hand.h"
+#include "scene/3d/skeleton_3d.h"
+#include "servers/xr_server.h"
+
+void OpenXRHand::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
+ ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
+
+ ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
+ ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
+
+ ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
+ ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
+
+ BIND_ENUM_CONSTANT(HAND_LEFT);
+ BIND_ENUM_CONSTANT(HAND_RIGHT);
+ BIND_ENUM_CONSTANT(HAND_MAX);
+
+ BIND_ENUM_CONSTANT(MOTION_RANGE_UNOBSTRUCTED);
+ BIND_ENUM_CONSTANT(MOTION_RANGE_CONFORM_TO_CONTROLLER);
+ BIND_ENUM_CONSTANT(MOTION_RANGE_MAX);
+}
+
+OpenXRHand::OpenXRHand() {
+ openxr_api = OpenXRAPI::get_singleton();
+ hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+}
+
+void OpenXRHand::set_hand(const Hands p_hand) {
+ ERR_FAIL_INDEX(p_hand, HAND_MAX);
+
+ hand = p_hand;
+}
+
+OpenXRHand::Hands OpenXRHand::get_hand() const {
+ return hand;
+}
+
+void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
+ hand_skeleton = p_hand_skeleton;
+
+ // TODO if inside tree call _get_bones()
+}
+
+void OpenXRHand::set_motion_range(const MotionRange p_motion_range) {
+ ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
+ motion_range = p_motion_range;
+
+ _set_motion_range();
+}
+
+OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
+ return motion_range;
+}
+
+NodePath OpenXRHand::get_hand_skeleton() const {
+ return hand_skeleton;
+}
+
+void OpenXRHand::_set_motion_range() {
+ if (!hand_tracking_ext) {
+ return;
+ }
+
+ XrHandJointsMotionRangeEXT xr_motion_range;
+ switch (motion_range) {
+ case MOTION_RANGE_UNOBSTRUCTED:
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
+ break;
+ case MOTION_RANGE_CONFORM_TO_CONTROLLER:
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
+ break;
+ default:
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
+ break;
+ }
+
+ hand_tracking_ext->set_motion_range(hand, xr_motion_range);
+}
+
+Skeleton3D *OpenXRHand::get_skeleton() {
+ if (!has_node(hand_skeleton)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(hand_skeleton);
+ if (!node) {
+ return nullptr;
+ }
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ return skeleton;
+}
+
+void OpenXRHand::_get_bones() {
+ const char *bone_names[XR_HAND_JOINT_COUNT_EXT] = {
+ "Palm",
+ "Wrist",
+ "Thumb_Metacarpal",
+ "Thumb_Proximal",
+ "Thumb_Distal",
+ "Thumb_Tip",
+ "Index_Metacarpal",
+ "Index_Proximal",
+ "Index_Intermediate",
+ "Index_Distal",
+ "Index_Tip",
+ "Middle_Metacarpal",
+ "Middle_Proximal",
+ "Middle_Intermediate",
+ "Middle_Distal",
+ "Middle_Tip",
+ "Ring_Metacarpal",
+ "Ring_Proximal",
+ "Ring_Intermediate",
+ "Ring_Distal",
+ "Ring_Tip",
+ "Little_Metacarpal",
+ "Little_Proximal",
+ "Little_Intermediate",
+ "Little_Distal",
+ "Little_Tip",
+ };
+
+ // reset JIC
+ for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
+ bones[i] = -1;
+ }
+
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+
+ // We cast to spatials which should allow us to use any subclass of that.
+ for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
+ String bone_name = bone_names[i];
+ if (hand == 0) {
+ bone_name += String("_L");
+ } else {
+ bone_name += String("_R");
+ }
+
+ bones[i] = skeleton->find_bone(bone_name);
+ if (bones[i] == -1) {
+ print_line("Couldn't obtain bone for", bone_name);
+ }
+ }
+}
+
+void OpenXRHand::_update_skeleton() {
+ if (openxr_api == nullptr || !openxr_api->is_initialized()) {
+ return;
+ } else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
+ return;
+ }
+
+ Skeleton3D *skeleton = get_skeleton();
+ if (!skeleton) {
+ return;
+ }
+
+ // we cache our transforms so we can quickly calculate local transforms
+ XRPose::TrackingConfidence confidences[XR_HAND_JOINT_COUNT_EXT];
+ Quaternion quaternions[XR_HAND_JOINT_COUNT_EXT];
+ Quaternion inv_quaternions[XR_HAND_JOINT_COUNT_EXT];
+ Vector3 positions[XR_HAND_JOINT_COUNT_EXT];
+
+ const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(hand);
+ const float ws = XRServer::get_singleton()->get_world_scale();
+
+ if (hand_tracker->is_initialized && hand_tracker->locations.isActive) {
+ for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
+ confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_NONE;
+ quaternions[i] = Quaternion();
+ positions[i] = Vector3();
+
+ const auto &location = hand_tracker->joint_locations[i];
+ const auto &pose = location.pose;
+
+ if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
+ if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.y != 0 || pose.orientation.w != 0) {
+ quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w);
+ inv_quaternions[i] = quaternions[i].inverse();
+
+ if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
+ confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
+ positions[i] = Vector3(pose.position.x * ws, pose.position.y * ws, pose.position.z * ws);
+
+ // TODO get inverse of position, we'll do this later. For now we're ignoring bone positions which generally works better anyway
+ } else {
+ confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_LOW;
+ }
+ }
+ }
+ }
+
+ if (confidences[XR_HAND_JOINT_PALM_EXT] != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
+ // now update our skeleton
+ for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
+ if (bones[i] != -1) {
+ int bone = bones[i];
+ int parent = skeleton->get_bone_parent(bone);
+
+ // Get our target quaternion
+ Quaternion q = quaternions[i];
+
+ // get local translation, parent should already be processed
+ if (parent == -1) {
+ // use our palm location here, that is what we are tracking
+ q = inv_quaternions[XR_HAND_JOINT_PALM_EXT] * q;
+ } else {
+ int found = false;
+ for (int b = 0; b < XR_HAND_JOINT_COUNT_EXT && !found; b++) {
+ if (bones[b] == parent) {
+ q = inv_quaternions[b] * q;
+ found = true;
+ }
+ }
+ }
+
+ // And get the movement from our rest position
+ // Transform3D rest = skeleton->get_bone_rest(bones[i]);
+ // q = rest.basis.get_quaternion().inverse() * q;
+
+ // and set our pose
+ // skeleton->set_bone_pose_position(bones[i], v);
+ skeleton->set_bone_pose_rotation(bones[i], q);
+ }
+ }
+
+ Transform3D t;
+ t.basis = Basis(quaternions[XR_HAND_JOINT_PALM_EXT]);
+ t.origin = positions[XR_HAND_JOINT_PALM_EXT];
+ set_transform(t);
+
+ // show it
+ set_visible(true);
+ } else {
+ // hide it
+ set_visible(false);
+ }
+ } else {
+ // hide it
+ set_visible(false);
+ }
+}
+
+void OpenXRHand::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _get_bones();
+
+ set_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ set_process_internal(false);
+
+ // reset
+ for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
+ bones[i] = -1;
+ }
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_skeleton();
+ } break;
+ default: {
+ } break;
+ }
+}
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
new file mode 100644
index 0000000000..e79cbe2cfe
--- /dev/null
+++ b/modules/openxr/scene/openxr_hand.h
@@ -0,0 +1,93 @@
+/**************************************************************************/
+/* openxr_hand.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_HAND_H
+#define OPENXR_HAND_H
+
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+
+class OpenXRAPI;
+class OpenXRHandTrackingExtension;
+
+class OpenXRHand : public Node3D {
+ GDCLASS(OpenXRHand, Node3D);
+
+public:
+ enum Hands {
+ HAND_LEFT,
+ HAND_RIGHT,
+ HAND_MAX
+ };
+
+ enum MotionRange {
+ MOTION_RANGE_UNOBSTRUCTED,
+ MOTION_RANGE_CONFORM_TO_CONTROLLER,
+ MOTION_RANGE_MAX
+ };
+
+private:
+ OpenXRAPI *openxr_api = nullptr;
+ OpenXRHandTrackingExtension *hand_tracking_ext = nullptr;
+
+ Hands hand = HAND_LEFT;
+ MotionRange motion_range = MOTION_RANGE_UNOBSTRUCTED;
+ NodePath hand_skeleton;
+
+ int64_t bones[XR_HAND_JOINT_COUNT_EXT];
+
+ void _set_motion_range();
+
+ Skeleton3D *get_skeleton();
+ void _get_bones();
+ void _update_skeleton();
+
+protected:
+ static void _bind_methods();
+
+public:
+ OpenXRHand();
+
+ void set_hand(const Hands p_hand);
+ Hands get_hand() const;
+
+ void set_motion_range(const MotionRange p_motion_range);
+ MotionRange get_motion_range() const;
+
+ void set_hand_skeleton(const NodePath &p_hand_skeleton);
+ NodePath get_hand_skeleton() const;
+
+ void _notification(int p_what);
+};
+
+VARIANT_ENUM_CAST(OpenXRHand::Hands)
+VARIANT_ENUM_CAST(OpenXRHand::MotionRange)
+
+#endif // OPENXR_HAND_H
diff --git a/modules/openxr/util.h b/modules/openxr/util.h
new file mode 100644
index 0000000000..6665d45007
--- /dev/null
+++ b/modules/openxr/util.h
@@ -0,0 +1,116 @@
+/**************************************************************************/
+/* util.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 UTIL_H
+#define UTIL_H
+
+#define UNPACK(...) __VA_ARGS__
+
+#define INIT_XR_FUNC_V(openxr_api, name) \
+ do { \
+ XrResult get_instance_proc_addr_result; \
+ get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \
+ ERR_FAIL_COND_V(XR_FAILED(get_instance_proc_addr_result), false); \
+ } while (0)
+
+#define EXT_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(OpenXRAPI::get_singleton(), name)
+#define OPENXR_API_INIT_XR_FUNC_V(name) INIT_XR_FUNC_V(this, name)
+
+#define INIT_XR_FUNC(openxr_api, name) \
+ do { \
+ XrResult get_instance_proc_addr_result; \
+ get_instance_proc_addr_result = openxr_api->get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr); \
+ ERR_FAIL_COND(XR_FAILED(get_instance_proc_addr_result)); \
+ } while (0)
+
+#define EXT_INIT_XR_FUNC(name) INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
+#define OPENXR_API_INIT_XR_FUNC(name) INIT_XR_FUNC(this, name)
+
+#define TRY_INIT_XR_FUNC(openxr_api, name) \
+ openxr_api->try_get_instance_proc_addr(#name, (PFN_xrVoidFunction *)&name##_ptr)
+
+#define EXT_TRY_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(OpenXRAPI::get_singleton(), name)
+#define OPENXR_TRY_API_INIT_XR_FUNC(name) TRY_INIT_XR_FUNC(this, name)
+
+#define EXT_PROTO_XRRESULT_FUNC1(func_name, arg1_type, arg1) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1); \
+ }
+
+#define EXT_PROTO_XRRESULT_FUNC2(func_name, arg1_type, arg1, arg2_type, arg2) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1, p_##arg2); \
+ }
+
+#define EXT_PROTO_XRRESULT_FUNC3(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3); \
+ }
+
+#define EXT_PROTO_XRRESULT_FUNC4(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4); \
+ }
+
+#define EXT_PROTO_XRRESULT_FUNC5(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4, UNPACK arg5_type p_##arg5) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4, p_##arg5); \
+ }
+
+#define EXT_PROTO_XRRESULT_FUNC6(func_name, arg1_type, arg1, arg2_type, arg2, arg3_type, arg3, arg4_type, arg4, arg5_type, arg5, arg6_type, arg6) \
+ PFN_##func_name func_name##_ptr = nullptr; \
+ XRAPI_ATTR XrResult XRAPI_CALL func_name(UNPACK arg1_type p_##arg1, UNPACK arg2_type p_##arg2, UNPACK arg3_type p_##arg3, UNPACK arg4_type p_##arg4, UNPACK arg5_type p_##arg5, UNPACK arg6_type p_##arg6) const { \
+ if (!func_name##_ptr) { \
+ return XR_ERROR_HANDLE_INVALID; \
+ } \
+ return (*func_name##_ptr)(p_##arg1, p_##arg2, p_##arg3, p_##arg4, p_##arg5, p_##arg6); \
+ }
+
+#endif // UTIL_H
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index ef4c598194..209ebab388 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -63,14 +63,15 @@ if env["builtin_embree"]:
thirdparty_sources = [thirdparty_dir + file for file in embree_src]
env_raycast.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "include"])
- env_raycast.Append(CPPDEFINES=["EMBREE_TARGET_SSE2", "EMBREE_LOWEST_ISA", "TASKING_INTERNAL", "NDEBUG"])
+ env_raycast.Append(CPPDEFINES=["EMBREE_TARGET_SSE2", "EMBREE_LOWEST_ISA", "TASKING_INTERNAL"])
+ env_raycast.AppendUnique(CPPDEFINES=["NDEBUG"]) # No assert() even in debug builds.
if not env.msvc:
- if env["arch"] in ["x86", "x86_64"]:
- env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"])
+ if env["arch"] in ["x86_64", "x86_32"]:
+ env_raycast.Append(CCFLAGS=["-msse2", "-mxsave"])
if env["platform"] == "windows":
- env_raycast.Append(CPPFLAGS=["-mstackrealign"])
+ env_raycast.Append(CCFLAGS=["-mstackrealign"])
if env["platform"] == "windows":
if env.msvc:
@@ -78,18 +79,28 @@ if env["builtin_embree"]:
else:
env.Append(LIBS=["psapi"])
+ if env.msvc: # Disable bogus warning about intentional struct padding.
+ env_raycast.Append(CCFLAGS=["/wd4324"])
+
env_thirdparty = env_raycast.Clone()
env_thirdparty.force_optimization_on_debug()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- if not env["arch"] in ["x86", "x86_64"] or env.msvc:
+ if env["arch"] != "x86_64" or env.msvc:
# Embree needs those, it will automatically use SSE2NEON in ARM
env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"])
+ if env["platform"] == "web":
+ env_thirdparty.Append(CXXFLAGS=["-msimd128"])
+
if not env.msvc:
+ # Flags synced with upstream gnu.cmake.
+ if env["arch"] == "arm64" and env["platform"] == "linuxbsd" and not env["use_llvm"]:
+ env_thirdparty.Append(CXXFLAGS=["-flax-vector-conversions"])
+
env_thirdparty.Append(
- CPPFLAGS=[
+ CXXFLAGS=[
"-fno-strict-overflow",
"-fno-delete-null-pointer-checks",
"-fwrapv",
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
index 7e8b3e9840..26329d813a 100644
--- a/modules/raycast/config.py
+++ b/modules/raycast/config.py
@@ -1,18 +1,11 @@
def can_build(env, platform):
- # Depends on Embree library, which only supports x86_64 and aarch64.
- if env["arch"].startswith("rv") or env["arch"].startswith("ppc"):
- return False
-
- if platform == "android":
- return env["android_arch"] in ["arm64v8", "x86_64"]
-
- if platform == "javascript":
- return False # No SIMD support yet
-
- if env["bits"] == "32":
- return False
-
- return True
+ # Supported architectures depend on the Embree library.
+ if env["arch"] in ["x86_64", "arm64", "wasm32"]:
+ return True
+ # x86_32 only seems supported on Windows for now.
+ if env["arch"] == "x86_32" and platform == "windows":
+ return True
+ return False
def configure(env):
diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py
index e31d88b741..527e02f855 100644
--- a/modules/raycast/godot_update_embree.py
+++ b/modules/raycast/godot_update_embree.py
@@ -1,5 +1,7 @@
import glob, os, shutil, subprocess, re
+git_tag = "v3.13.5"
+
include_dirs = [
"common/tasking",
"kernels/bvh",
@@ -12,6 +14,7 @@ include_dirs = [
"common/lexers",
"common/simd",
"common/simd/arm",
+ "common/simd/wasm",
"include/embree3",
"kernels/subdiv",
"kernels/geometry",
@@ -76,6 +79,7 @@ if os.path.exists(dir_name):
subprocess.run(["git", "clone", "https://github.com/embree/embree.git", "embree-tmp"])
os.chdir("embree-tmp")
+subprocess.run(["git", "checkout", git_tag])
commit_hash = str(subprocess.check_output(["git", "rev-parse", "HEAD"], universal_newlines=True)).strip()
@@ -94,8 +98,7 @@ for f in all_files:
with open(os.path.join(dest_dir, "kernels/hash.h"), "w") as hash_file:
hash_file.write(
- f"""
-// Copyright 2009-2020 Intel Corporation
+ f"""// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#define RTC_HASH "{commit_hash}"
@@ -104,8 +107,7 @@ with open(os.path.join(dest_dir, "kernels/hash.h"), "w") as hash_file:
with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
config_file.write(
- """
-// Copyright 2009-2020 Intel Corporation
+ """// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
/* #undef EMBREE_RAY_MASK */
@@ -126,6 +128,7 @@ with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
/* #undef EMBREE_COMPACT_POLYS */
#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
+#define EMBREE_DISC_POINT_SELF_INTERSECTION_AVOIDANCE
#if defined(EMBREE_GEOMETRY_TRIANGLE)
#define IF_ENABLED_TRIS(x) x
@@ -192,8 +195,7 @@ with open("CMakeLists.txt", "r") as cmake_file:
with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as config_file:
config_file.write(
- f"""
-// Copyright 2009-2021 Intel Corporation
+ f"""// Copyright 2009-2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#pragma once
@@ -209,14 +211,16 @@ with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as con
#define EMBREE_MIN_WIDTH 0
#define RTC_MIN_WIDTH EMBREE_MIN_WIDTH
-#define EMBREE_STATIC_LIB
-/* #undef EMBREE_API_NAMESPACE */
+#if !defined(EMBREE_STATIC_LIB)
+# define EMBREE_STATIC_LIB
+#endif
+/* #undef EMBREE_API_NAMESPACE*/
#if defined(EMBREE_API_NAMESPACE)
# define RTC_NAMESPACE
-# define RTC_NAMESPACE_BEGIN namespace {{
+# define RTC_NAMESPACE_BEGIN namespace {{
# define RTC_NAMESPACE_END }}
-# define RTC_NAMESPACE_USE using namespace ;
+# define RTC_NAMESPACE_USE using namespace;
# define RTC_API_EXTERN_C
# undef EMBREE_API_NAMESPACE
#else
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster_embree.cpp
index 9b35b5616e..2a66c36d53 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster_embree.cpp
@@ -1,36 +1,36 @@
-/*************************************************************************/
-/* lightmap_raycaster.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmap_raycaster_embree.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#ifdef TOOLS_ENABLED
-#include "lightmap_raycaster.h"
+#include "lightmap_raycaster_embree.h"
#ifdef __SSE2__
#include <pmmintrin.h>
@@ -193,4 +193,4 @@ LightmapRaycasterEmbree::~LightmapRaycasterEmbree() {
}
}
-#endif
+#endif // TOOLS_ENABLED
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster_embree.h
index 2e9f59dda4..d1999e329e 100644
--- a/modules/raycast/lightmap_raycaster.h
+++ b/modules/raycast/lightmap_raycaster_embree.h
@@ -1,32 +1,35 @@
-/*************************************************************************/
-/* lightmap_raycaster.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* lightmap_raycaster_embree.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 LIGHTMAP_RAYCASTER_EMBREE_H
+#define LIGHTMAP_RAYCASTER_EMBREE_H
#ifdef TOOLS_ENABLED
@@ -74,4 +77,6 @@ public:
~LightmapRaycasterEmbree();
};
-#endif // LIGHTMAP_RAYCASTER_H
+#endif // TOOLS_ENABLED
+
+#endif // LIGHTMAP_RAYCASTER_EMBREE_H
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 13824c3830..c74799caa3 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* raycast_occlusion_cull.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* raycast_occlusion_cull.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "raycast_occlusion_cull.h"
#include "core/config/project_settings.h"
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index 056b808640..c4e733b664 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* raycast_occlusion_cull.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* raycast_occlusion_cull.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 RAYCAST_OCCLUSION_CULL_H
#define RAYCAST_OCCLUSION_CULL_H
diff --git a/modules/raycast/register_types.cpp b/modules/raycast/register_types.cpp
index 42de1d971d..dc7bb0c9dd 100644
--- a/modules/raycast/register_types.cpp
+++ b/modules/raycast/register_types.cpp
@@ -1,38 +1,38 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "lightmap_raycaster.h"
+#include "lightmap_raycaster_embree.h"
#include "raycast_occlusion_cull.h"
-#include "static_raycaster.h"
+#include "static_raycaster_embree.h"
RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
diff --git a/modules/raycast/register_types.h b/modules/raycast/register_types.h
index a917285390..33181e6d42 100644
--- a/modules/raycast/register_types.h
+++ b/modules/raycast/register_types.h
@@ -1,34 +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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 RAYCAST_REGISTER_TYPES_H
+#define RAYCAST_REGISTER_TYPES_H
#include "modules/register_module_types.h"
void initialize_raycast_module(ModuleInitializationLevel p_level);
void uninitialize_raycast_module(ModuleInitializationLevel p_level);
+
+#endif // RAYCAST_REGISTER_TYPES_H
diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster_embree.cpp
index 7659eea27f..b2e58812bd 100644
--- a/modules/raycast/static_raycaster.cpp
+++ b/modules/raycast/static_raycaster_embree.cpp
@@ -1,36 +1,36 @@
-/*************************************************************************/
-/* static_raycaster.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* static_raycaster_embree.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
#ifdef TOOLS_ENABLED
-#include "static_raycaster.h"
+#include "static_raycaster_embree.h"
#ifdef __SSE2__
#include <pmmintrin.h>
@@ -134,4 +134,4 @@ StaticRaycasterEmbree::~StaticRaycasterEmbree() {
}
}
-#endif
+#endif // TOOLS_ENABLED
diff --git a/modules/raycast/static_raycaster.h b/modules/raycast/static_raycaster_embree.h
index 607a392683..24e1c7b92f 100644
--- a/modules/raycast/static_raycaster.h
+++ b/modules/raycast/static_raycaster_embree.h
@@ -1,32 +1,35 @@
-/*************************************************************************/
-/* static_raycaster.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* static_raycaster_embree.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 STATIC_RAYCASTER_EMBREE_H
+#define STATIC_RAYCASTER_EMBREE_H
#ifdef TOOLS_ENABLED
@@ -61,4 +64,6 @@ public:
~StaticRaycasterEmbree();
};
-#endif // STATIC_RAYCASTER_H
+#endif // TOOLS_ENABLED
+
+#endif // STATIC_RAYCASTER_EMBREE_H
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
index deb9db7591..6fd7cd47f3 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -58,10 +58,10 @@ if env["builtin_pcre2"]:
env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"]
env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)])
env_pcre2.add_source_files(thirdparty_obj, thirdparty_sources)
- env.modules_sources += thirdparty_obj
pcre2_builtin("16")
pcre2_builtin("32")
+ env.modules_sources += thirdparty_obj
# Godot source files
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index deabc5ccd3..02260c837e 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -4,7 +4,7 @@
Class for searching text for patterns using regular expressions.
</brief_description>
<description>
- A regular expression (or regex) is a compact language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet.
+ A regular expression (or regex) is a compact language that can be used to recognize strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For example, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet.
To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used.
[codeblock]
var regex = RegEx.new()
@@ -57,11 +57,18 @@
</method>
<method name="compile">
<return type="int" enum="Error" />
- <argument index="0" name="pattern" type="String" />
+ <param index="0" name="pattern" type="String" />
<description>
Compiles and assign the search pattern to use. Returns [constant OK] if the compilation is successful. If an error is encountered, details are printed to standard output and an error is returned.
</description>
</method>
+ <method name="create_from_string" qualifiers="static">
+ <return type="RegEx" />
+ <param index="0" name="pattern" type="String" />
+ <description>
+ Creates and compiles a new [RegEx] object.
+ </description>
+ </method>
<method name="get_group_count" qualifiers="const">
<return type="int" />
<description>
@@ -69,7 +76,7 @@
</description>
</method>
<method name="get_names" qualifiers="const">
- <return type="Array" />
+ <return type="PackedStringArray" />
<description>
Returns an array of names of named capturing groups in the compiled pattern. They are ordered by appearance.
</description>
@@ -88,31 +95,34 @@
</method>
<method name="search" qualifiers="const">
<return type="RegExMatch" />
- <argument index="0" name="subject" type="String" />
- <argument index="1" name="offset" type="int" default="0" />
- <argument index="2" name="end" type="int" default="-1" />
+ <param index="0" name="subject" type="String" />
+ <param index="1" name="offset" type="int" default="0" />
+ <param index="2" name="end" type="int" default="-1" />
<description>
- Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise [code]null[/code]. The region to search within can be specified without modifying where the start and end anchor would be.
+ Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise [code]null[/code].
+ The region to search within can be specified with [param offset] and [param end]. This is useful when searching for another match in the same [param subject] by calling this method again after a previous success. Note that setting these parameters differs from passing over a shortened string. For example, the start anchor [code]^[/code] is not affected by [param offset], and the character before [param offset] will be checked for the word boundary [code]\b[/code].
</description>
</method>
<method name="search_all" qualifiers="const">
- <return type="Array" />
- <argument index="0" name="subject" type="String" />
- <argument index="1" name="offset" type="int" default="0" />
- <argument index="2" name="end" type="int" default="-1" />
+ <return type="RegExMatch[]" />
+ <param index="0" name="subject" type="String" />
+ <param index="1" name="offset" type="int" default="0" />
+ <param index="2" name="end" type="int" default="-1" />
<description>
- Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found, an empty array is returned instead. The region to search within can be specified without modifying where the start and end anchor would be.
+ Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found, an empty array is returned instead.
+ The region to search within can be specified with [param offset] and [param end]. This is useful when searching for another match in the same [param subject] by calling this method again after a previous success. Note that setting these parameters differs from passing over a shortened string. For example, the start anchor [code]^[/code] is not affected by [param offset], and the character before [param offset] will be checked for the word boundary [code]\b[/code].
</description>
</method>
<method name="sub" qualifiers="const">
<return type="String" />
- <argument index="0" name="subject" type="String" />
- <argument index="1" name="replacement" type="String" />
- <argument index="2" name="all" type="bool" default="false" />
- <argument index="3" name="offset" type="int" default="0" />
- <argument index="4" name="end" type="int" default="-1" />
+ <param index="0" name="subject" type="String" />
+ <param index="1" name="replacement" type="String" />
+ <param index="2" name="all" type="bool" default="false" />
+ <param index="3" name="offset" type="int" default="0" />
+ <param index="4" name="end" type="int" default="-1" />
<description>
- Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default, only the first instance is replaced, but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
+ Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default, only the first instance is replaced, but it can be changed for all instances (global replacement).
+ The region to search within can be specified with [param offset] and [param end]. This is useful when searching for another match in the same [param subject] by calling this method again after a previous success. Note that setting these parameters differs from passing over a shortened string. For example, the start anchor [code]^[/code] is not affected by [param offset], and the character before [param offset] will be checked for the word boundary [code]\b[/code].
</description>
</method>
</methods>
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 530a541ae8..31e2207d84 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -11,7 +11,7 @@
<methods>
<method name="get_end" qualifiers="const">
<return type="int" />
- <argument index="0" name="name" type="Variant" default="0" />
+ <param index="0" name="name" type="Variant" default="0" />
<description>
Returns the end position of the match within the source string. The end position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns -1 if the group did not match or doesn't exist.
@@ -25,7 +25,7 @@
</method>
<method name="get_start" qualifiers="const">
<return type="int" />
- <argument index="0" name="name" type="Variant" default="0" />
+ <param index="0" name="name" type="Variant" default="0" />
<description>
Returns the starting position of the match within the source string. The starting position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns -1 if the group did not match or doesn't exist.
@@ -33,7 +33,7 @@
</method>
<method name="get_string" qualifiers="const">
<return type="String" />
- <argument index="0" name="name" type="Variant" default="0" />
+ <param index="0" name="name" type="Variant" default="0" />
<description>
Returns the substring of the match from the source string. Capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns an empty string if the group did not match or doesn't exist.
@@ -44,7 +44,7 @@
<member name="names" type="Dictionary" setter="" getter="get_names" default="{}">
A dictionary of named groups and its corresponding group number. Only groups that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
</member>
- <member name="strings" type="Array" setter="" getter="get_strings" default="[]">
+ <member name="strings" type="PackedStringArray" setter="" getter="get_strings" default="PackedStringArray()">
An [Array] of the match and its capturing groups.
</member>
<member name="subject" type="String" setter="" getter="get_subject" default="&quot;&quot;">
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index 67ce37219b..999a61e11a 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* regex.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* regex.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "regex.h"
#include "core/os/memory.h"
@@ -50,8 +50,7 @@ int RegExMatch::_find(const Variant &p_name) const {
return -1;
}
return i;
-
- } else if (p_name.get_type() == Variant::STRING) {
+ } else if (p_name.get_type() == Variant::STRING || p_name.get_type() == Variant::STRING_NAME) {
HashMap<String, int>::ConstIterator found = names.find((String)p_name);
if (found) {
return found->value;
@@ -82,8 +81,8 @@ Dictionary RegExMatch::get_names() const {
return result;
}
-Array RegExMatch::get_strings() const {
- Array result;
+PackedStringArray RegExMatch::get_strings() const {
+ PackedStringArray result;
int size = data.size();
@@ -159,6 +158,13 @@ void RegEx::_pattern_info(uint32_t what, void *where) const {
pcre2_pattern_info_32((pcre2_code_32 *)code, what, where);
}
+Ref<RegEx> RegEx::create_from_string(const String &p_pattern) {
+ Ref<RegEx> ret;
+ ret.instantiate();
+ ret->compile(p_pattern);
+ return ret;
+}
+
void RegEx::clear() {
if (code) {
pcre2_code_free_32((pcre2_code_32 *)code);
@@ -258,11 +264,11 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
return result;
}
-Array RegEx::search_all(const String &p_subject, int p_offset, int p_end) const {
+TypedArray<RegExMatch> 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;
+ TypedArray<RegExMatch> result;
Ref<RegExMatch> match = search(p_subject, p_offset, p_end);
while (match.is_valid()) {
if (last_end == match->get_end(0)) {
@@ -344,8 +350,8 @@ int RegEx::get_group_count() const {
return count;
}
-Array RegEx::get_names() const {
- Array result;
+PackedStringArray RegEx::get_names() const {
+ PackedStringArray result;
ERR_FAIL_COND_V(!is_valid(), result);
@@ -384,6 +390,8 @@ RegEx::~RegEx() {
}
void RegEx::_bind_methods() {
+ ClassDB::bind_static_method("RegEx", D_METHOD("create_from_string", "pattern"), &RegEx::create_from_string);
+
ClassDB::bind_method(D_METHOD("clear"), &RegEx::clear);
ClassDB::bind_method(D_METHOD("compile", "pattern"), &RegEx::compile);
ClassDB::bind_method(D_METHOD("search", "subject", "offset", "end"), &RegEx::search, DEFVAL(0), DEFVAL(-1));
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
index 1455188670..2ed20c9b14 100644
--- a/modules/regex/regex.h
+++ b/modules/regex/regex.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* regex.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* regex.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 REGEX_H
#define REGEX_H
@@ -37,6 +37,7 @@
#include "core/templates/vector.h"
#include "core/variant/array.h"
#include "core/variant/dictionary.h"
+#include "core/variant/typed_array.h"
class RegExMatch : public RefCounted {
GDCLASS(RegExMatch, RefCounted);
@@ -62,7 +63,7 @@ public:
int get_group_count() const;
Dictionary get_names() const;
- Array get_strings() const;
+ PackedStringArray get_strings() const;
String get_string(const Variant &p_name) const;
int get_start(const Variant &p_name) const;
int get_end(const Variant &p_name) const;
@@ -81,17 +82,19 @@ protected:
static void _bind_methods();
public:
+ static Ref<RegEx> create_from_string(const String &p_pattern);
+
void clear();
Error compile(const String &p_pattern);
Ref<RegExMatch> search(const String &p_subject, int p_offset = 0, int p_end = -1) const;
- Array search_all(const String &p_subject, int p_offset = 0, int p_end = -1) const;
+ TypedArray<RegExMatch> search_all(const String &p_subject, int p_offset = 0, int p_end = -1) const;
String sub(const String &p_subject, const String &p_replacement, bool p_all = false, int p_offset = 0, int p_end = -1) const;
bool is_valid() const;
String get_pattern() const;
int get_group_count() const;
- Array get_names() const;
+ PackedStringArray get_names() const;
RegEx();
RegEx(const String &p_pattern);
diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp
index 2103c57f77..715e98a79a 100644
--- a/modules/regex/register_types.cpp
+++ b/modules/regex/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/object/class_db.h"
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
index c3edf23562..f05c35b1ec 100644
--- a/modules/regex/register_types.h
+++ b/modules/regex/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 REGEX_REGISTER_TYPES_H
#define REGEX_REGISTER_TYPES_H
diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h
index 91af393db1..ac6192e373 100644
--- a/modules/regex/tests/test_regex.h
+++ b/modules/regex/tests/test_regex.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* test_regex.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* test_regex.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#ifndef TEST_REGEX_H
#define TEST_REGEX_H
@@ -46,7 +46,7 @@ TEST_CASE("[RegEx] Initialization") {
CHECK(re1.get_pattern() == pattern);
CHECK(re1.get_group_count() == 1);
- Array names = re1.get_names();
+ PackedStringArray names = re1.get_names();
CHECK(names.size() == 1);
CHECK(names[0] == "vowel");
diff --git a/modules/register_module_types.h b/modules/register_module_types.h
index cfd1b355d4..61cda428cf 100644
--- a/modules/register_module_types.h
+++ b/modules/register_module_types.h
@@ -1,43 +1,43 @@
-/*************************************************************************/
-/* register_module_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_module_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 REGISTER_MODULE_TYPES_H
#define REGISTER_MODULE_TYPES_H
-#include "core/extension/gdnative_interface.h"
+#include "core/extension/gdextension_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
+ MODULE_INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
+ MODULE_INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
+ MODULE_INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE,
+ MODULE_INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR
};
void initialize_modules(ModuleInitializationLevel p_level);
diff --git a/modules/squish/image_decompress_squish.cpp b/modules/squish/image_decompress_squish.cpp
index 3a810e5259..01be72a27e 100644
--- a/modules/squish/image_decompress_squish.cpp
+++ b/modules/squish/image_decompress_squish.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_decompress_squish.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_decompress_squish.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_decompress_squish.h"
@@ -70,7 +70,7 @@ void image_decompress_squish(Image *p_image) {
h >>= 1;
}
- p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+ p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
if (p_image->get_format() == Image::FORMAT_DXT5_RA_AS_RG) {
p_image->convert_ra_rgba8_to_rg();
diff --git a/modules/squish/image_decompress_squish.h b/modules/squish/image_decompress_squish.h
index f1382b2610..53c5d344a5 100644
--- a/modules/squish/image_decompress_squish.h
+++ b/modules/squish/image_decompress_squish.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_decompress_squish.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_decompress_squish.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_DECOMPRESS_SQUISH_H
#define IMAGE_DECOMPRESS_SQUISH_H
diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp
index a80419e0eb..af7cf8f4f1 100644
--- a/modules/squish/register_types.cpp
+++ b/modules/squish/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h
index 5e9a4dfd50..1786b28ed3 100644
--- a/modules/squish/register_types.h
+++ b/modules/squish/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 SQUISH_REGISTER_TYPES_H
#define SQUISH_REGISTER_TYPES_H
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index 93262f4f87..ae3e1bdedb 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -11,6 +11,16 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/thorvg/"
thirdparty_sources = [
+ "src/lib/sw_engine/tvgSwFill.cpp",
+ "src/lib/sw_engine/tvgSwImage.cpp",
+ "src/lib/sw_engine/tvgSwMath.cpp",
+ "src/lib/sw_engine/tvgSwMemPool.cpp",
+ "src/lib/sw_engine/tvgSwRaster.cpp",
+ "src/lib/sw_engine/tvgSwRenderer.cpp",
+ "src/lib/sw_engine/tvgSwRle.cpp",
+ "src/lib/sw_engine/tvgSwShape.cpp",
+ "src/lib/sw_engine/tvgSwStroke.cpp",
+ "src/lib/tvgAccessor.cpp",
"src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgFill.cpp",
@@ -28,27 +38,18 @@ thirdparty_sources = [
"src/lib/tvgShape.cpp",
"src/lib/tvgSwCanvas.cpp",
"src/lib/tvgTaskScheduler.cpp",
+ "src/loaders/external_png/tvgPngLoader.cpp",
+ "src/loaders/jpg/tvgJpgd.cpp",
+ "src/loaders/jpg/tvgJpgLoader.cpp",
"src/loaders/raw/tvgRawLoader.cpp",
- "src/loaders/svg/tvgXmlParser.cpp",
- "src/loaders/svg/tvgSvgUtil.cpp",
- "src/loaders/svg/tvgSvgSceneBuilder.cpp",
- "src/loaders/svg/tvgSvgPath.cpp",
- "src/loaders/svg/tvgSvgLoader.cpp",
"src/loaders/svg/tvgSvgCssStyle.cpp",
+ "src/loaders/svg/tvgSvgLoader.cpp",
+ "src/loaders/svg/tvgSvgPath.cpp",
+ "src/loaders/svg/tvgSvgSceneBuilder.cpp",
+ "src/loaders/svg/tvgSvgUtil.cpp",
+ "src/loaders/svg/tvgXmlParser.cpp",
"src/loaders/tvg/tvgTvgBinInterpreter.cpp",
"src/loaders/tvg/tvgTvgLoader.cpp",
- "src/loaders/jpg/tvgJpgLoader.cpp",
- "src/loaders/jpg/tvgJpgd.cpp",
- "src/loaders/external_png/tvgPngLoader.cpp",
- "src/lib/sw_engine/tvgSwFill.cpp",
- "src/lib/sw_engine/tvgSwImage.cpp",
- "src/lib/sw_engine/tvgSwMath.cpp",
- "src/lib/sw_engine/tvgSwMemPool.cpp",
- "src/lib/sw_engine/tvgSwRaster.cpp",
- "src/lib/sw_engine/tvgSwRenderer.cpp",
- "src/lib/sw_engine/tvgSwRle.cpp",
- "src/lib/sw_engine/tvgSwShape.cpp",
- "src/lib/sw_engine/tvgSwStroke.cpp",
"src/savers/tvg/tvgTvgSaver.cpp",
]
@@ -62,14 +63,18 @@ env_thirdparty.Prepend(
CPPPATH=[
thirdparty_dir + "src/lib",
thirdparty_dir + "src/lib/sw_engine",
+ thirdparty_dir + "src/loaders/external_png",
+ thirdparty_dir + "src/loaders/jpg",
thirdparty_dir + "src/loaders/raw",
thirdparty_dir + "src/loaders/svg",
- thirdparty_dir + "src/loaders/jpg",
- thirdparty_dir + "src/loaders/png",
thirdparty_dir + "src/loaders/tvg",
thirdparty_dir + "src/savers/tvg",
]
)
+# Also requires libpng headers
+if env["builtin_libpng"]:
+ env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"])
+
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 87e2fae2d0..e239b33374 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_svg.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_svg.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_svg.h"
@@ -35,13 +35,19 @@
#include <thorvg.h>
-void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_string) {
- // Replace colors in the SVG based on what is configured in `replace_colors`.
+HashMap<Color, Color> ImageLoaderSVG::forced_color_map = HashMap<Color, Color>();
+
+void ImageLoaderSVG::set_forced_color_map(const HashMap<Color, Color> &p_color_map) {
+ forced_color_map = p_color_map;
+}
+
+void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string) {
+ // Replace colors in the SVG based on what is passed in `p_color_map`.
// Used to change the colors of editor icons based on the used theme.
// The strings being replaced are typically of the form:
// fill="#5abbef"
// But can also be 3-letter codes, include alpha, be "none" or a named color
- // string ("blue"). So we convert to Godot Color to compare with `replace_colors`.
+ // string ("blue"). So we convert to Godot Color to compare with `p_color_map`.
const int prefix_len = p_prefix.length();
int pos = r_string.find(p_prefix);
@@ -52,8 +58,8 @@ void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_s
const String color_code = r_string.substr(pos, end_pos - pos);
if (color_code != "none" && !color_code.begins_with("url(")) {
const Color color = Color(color_code); // Handles both HTML codes and named colors.
- if (replace_colors.has(color)) {
- r_string = r_string.left(pos) + "#" + replace_colors[color].operator Color().to_html(false) + r_string.substr(end_pos);
+ if (p_color_map.has(color)) {
+ r_string = r_string.left(pos) + "#" + p_color_map[color].to_html(false) + r_string.substr(end_pos);
}
}
// Search for other occurrences.
@@ -61,27 +67,30 @@ void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_s
}
}
-void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color) {
- ERR_FAIL_COND(Math::is_zero_approx(p_scale));
-
- if (p_convert_color) {
- _replace_color_property("stop-color=\"", p_string);
- _replace_color_property("fill=\"", p_string);
- _replace_color_property("stroke=\"", p_string);
- }
+Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) {
+ ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0.");
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
- PackedByteArray bytes = p_string.to_utf8_buffer();
- tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true);
+ tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true);
if (result != tvg::Result::Success) {
- return;
+ return ERR_INVALID_DATA;
}
float fw, fh;
picture->size(&fw, &fh);
- uint32_t width = MIN(round(fw * p_scale), 16 * 1024);
- uint32_t height = MIN(round(fh * p_scale), 16 * 1024);
+ uint32_t width = round(fw * p_scale);
+ uint32_t height = round(fh * p_scale);
+
+ const uint32_t max_dimension = 16384;
+ if (width > max_dimension || height > max_dimension) {
+ WARN_PRINT(vformat(
+ String::utf8("ImageLoaderSVG: Target canvas dimensions %d×%d (with scale %.2f) exceed the max supported dimensions %d×%d. The target canvas will be scaled down."),
+ width, height, p_scale, max_dimension, max_dimension));
+ width = MIN(width, max_dimension);
+ height = MIN(height, max_dimension);
+ }
+
picture->size(width, height);
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
@@ -91,25 +100,25 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT);
if (res != tvg::Result::Success) {
memfree(buffer);
- ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas.");
}
res = sw_canvas->push(std::move(picture));
if (res != tvg::Result::Success) {
memfree(buffer);
- ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas.");
}
res = sw_canvas->draw();
if (res != tvg::Result::Success) {
memfree(buffer);
- ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas.");
}
res = sw_canvas->sync();
if (res != tvg::Result::Success) {
memfree(buffer);
- ERR_FAIL_MSG("ImageLoaderSVG can't create image.");
+ ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas.");
}
Vector<uint8_t> image;
@@ -129,18 +138,43 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
res = sw_canvas->clear(true);
memfree(buffer);
- p_image->create(width, height, false, Image::FORMAT_RGBA8, image);
+ p_image->set_data(width, height, false, Image::FORMAT_RGBA8, image);
+ return OK;
+}
+
+Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) {
+ if (p_color_map.size()) {
+ _replace_color_property(p_color_map, "stop-color=\"", p_string);
+ _replace_color_property(p_color_map, "fill=\"", p_string);
+ _replace_color_property(p_color_map, "stroke=\"", p_string);
+ }
+
+ PackedByteArray bytes = p_string.to_utf8_buffer();
+
+ return create_image_from_utf8_buffer(p_image, bytes, p_scale, p_upsample);
}
void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("svg");
}
-Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) {
+Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
String svg = p_fileaccess->get_as_utf8_string();
- create_image_from_string(p_image, svg, p_scale, false, false);
- ERR_FAIL_COND_V(p_image->is_empty(), FAILED);
- if (p_force_linear) {
+
+ Error err;
+ if (p_flags & FLAG_CONVERT_COLORS) {
+ err = create_image_from_string(p_image, svg, p_scale, false, forced_color_map);
+ } else {
+ err = create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>());
+ }
+
+ if (err != OK) {
+ return err;
+ } else if (p_image->is_empty()) {
+ return ERR_INVALID_DATA;
+ }
+
+ if (p_flags & FLAG_FORCE_LINEAR) {
p_image->srgb_to_linear();
}
return OK;
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index 94c17fda43..e0d2849a62 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_svg.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_svg.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_SVG_H
#define IMAGE_LOADER_SVG_H
@@ -34,15 +34,17 @@
#include "core/io/image_loader.h"
class ImageLoaderSVG : public ImageFormatLoader {
- Dictionary replace_colors;
- void _replace_color_property(const String &p_prefix, String &r_string);
+ static HashMap<Color, Color> forced_color_map;
+
+ void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
public:
- // Called by the editor to handle theme icon colors.
- void set_replace_colors(Dictionary p_replace_colors) { replace_colors = p_replace_colors; }
- void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color);
+ static void set_forced_color_map(const HashMap<Color, Color> &p_color_map);
+
+ Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample);
+ Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) override;
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
};
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index 5b4d1d31ca..7b61749f61 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -34,7 +34,7 @@
#include <thorvg.h>
-static ImageLoaderSVG *image_loader_svg = nullptr;
+static Ref<ImageLoaderSVG> image_loader_svg;
void initialize_svg_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
@@ -45,7 +45,8 @@ void initialize_svg_module(ModuleInitializationLevel p_level) {
if (tvg::Initializer::init(tvgEngine, 1) != tvg::Result::Success) {
return;
}
- image_loader_svg = memnew(ImageLoaderSVG);
+
+ image_loader_svg.instantiate();
ImageLoader::add_image_format_loader(image_loader_svg);
}
@@ -54,9 +55,12 @@ void uninitialize_svg_module(ModuleInitializationLevel p_level) {
return;
}
- if (!image_loader_svg) {
+ if (image_loader_svg.is_null()) {
+ // It failed to initialize so it was not added.
return;
}
- memdelete(image_loader_svg);
+
+ ImageLoader::remove_image_format_loader(image_loader_svg);
+ image_loader_svg.unref();
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
diff --git a/modules/svg/register_types.h b/modules/svg/register_types.h
index 66f3e94600..8458eadcdd 100644
--- a/modules/svg/register_types.h
+++ b/modules/svg/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 SVG_REGISTER_TYPES_H
#define SVG_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 2d764a4006..4a9c7b3567 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -36,8 +36,11 @@ def make_icu_data(target, source, env):
# Thirdparty source files
thirdparty_obj = []
-freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
+
+if "svg" in env.module_list:
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
if env["builtin_harfbuzz"]:
env_harfbuzz = env_modules.Clone()
@@ -113,40 +116,42 @@ if env["builtin_harfbuzz"]:
if freetype_enabled:
thirdparty_sources += [
"src/hb-ft.cc",
- "src/hb-graphite2.cc",
]
+ if env["graphite"]:
+ thirdparty_sources += [
+ "src/hb-graphite2.cc",
+ ]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_harfbuzz.Append(CPPPATH=["#thirdparty/harfbuzz/src"])
+ env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"])
if env["builtin_icu"]:
- env_harfbuzz.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+ env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"])
if freetype_enabled:
env_harfbuzz.Append(
CCFLAGS=[
"-DHAVE_FREETYPE",
- "-DHAVE_GRAPHITE2",
]
)
+ if env["graphite"]:
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DHAVE_GRAPHITE2",
+ ]
+ )
if env["builtin_freetype"]:
- env_harfbuzz.Append(CPPPATH=["#thirdparty/freetype/include"])
- if env["builtin_graphite"]:
- env_harfbuzz.Append(CPPPATH=["#thirdparty/graphite/include"])
+ env_harfbuzz.Prepend(CPPPATH=["#thirdparty/freetype/include"])
+ if env["builtin_graphite"] and env["graphite"]:
+ env_harfbuzz.Prepend(CPPPATH=["#thirdparty/graphite/include"])
env_harfbuzz.Append(CCFLAGS=["-DGRAPHITE2_STATIC"])
- if env["platform"] == "android" or env["platform"] == "linuxbsd":
+ if env["platform"] in ["android", "linuxbsd", "web"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
- if env["platform"] == "javascript":
- if env["threads_enabled"]:
- env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
- else:
- env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"])
-
- env_text_server_adv.Append(CPPPATH=["#thirdparty/harfbuzz/src"])
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"])
lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)
thirdparty_obj += lib
@@ -165,7 +170,7 @@ if env["builtin_harfbuzz"]:
env.Append(LIBS=[lib])
-if env["builtin_graphite"] and freetype_enabled:
+if env["builtin_graphite"] and freetype_enabled and env["graphite"]:
env_graphite = env_modules.Clone()
env_graphite.disable_warnings()
@@ -209,7 +214,7 @@ if env["builtin_graphite"] and freetype_enabled:
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
+ env_graphite.Prepend(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
env_graphite.Append(
CCFLAGS=[
"-DGRAPHITE2_STATIC",
@@ -439,19 +444,23 @@ if env["builtin_icu"]:
"common/uvectr32.cpp",
"common/uvectr64.cpp",
"common/wintz.cpp",
+ "i18n/scriptset.cpp",
+ "i18n/ucln_in.cpp",
+ "i18n/uspoof.cpp",
+ "i18n/uspoof_impl.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- icu_data_name = "icudt71l.dat"
+ icu_data_name = "icudt72l.dat"
- if env_icu["tools"]:
+ if env.editor_build:
env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name)
env_icu.Command("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name, make_icu_data)
- env_text_server_adv.Append(CPPPATH=["#thirdparty/icu4c/"])
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/"])
else:
thirdparty_sources += ["icu_data/icudata_stub.cpp"]
- env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+ env_icu.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
env_icu.Append(
CXXFLAGS=[
"-DU_STATIC_IMPLEMENTATION",
@@ -463,6 +472,7 @@ if env["builtin_icu"]:
"-DUCONFIG_NO_IDNA",
"-DUCONFIG_NO_FILE_IO",
"-DUCONFIG_NO_TRANSLITERATION",
+ "-DUCONFIG_NO_REGULAR_EXPRESSIONS",
"-DPKGDATA_MODE=static",
"-DU_ENABLE_DYLOAD=0",
"-DU_HAVE_LIB_SUFFIX=1",
@@ -477,10 +487,10 @@ if env["builtin_icu"]:
"-DICU_DATA_NAME=" + icu_data_name,
]
)
- if env_text_server_adv["tools"]:
+ if env.editor_build:
env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"])
- env_text_server_adv.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
lib = env_icu.add_library("icu_builtin", thirdparty_sources)
thirdparty_obj += lib
@@ -504,13 +514,14 @@ if env["builtin_icu"]:
module_obj = []
if env["builtin_msdfgen"] and msdfgen_enabled:
- env_text_server_adv.Append(CPPPATH=["#thirdparty/msdfgen"])
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/msdfgen"])
if env["builtin_freetype"] and freetype_enabled:
- env_text_server_adv.Append(CPPPATH=["#thirdparty/freetype/include"])
+ env_text_server_adv.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/freetype/include"])
-if env["builtin_graphite"] and freetype_enabled:
- env_text_server_adv.Append(CPPPATH=["#thirdparty/graphite/include"])
+if env["builtin_graphite"] and freetype_enabled and env["graphite"]:
+ env_text_server_adv.Prepend(CPPPATH=["#thirdparty/graphite/include"])
env_text_server_adv.add_source_files(module_obj, "*.cpp")
env.modules_sources += module_obj
diff --git a/modules/text_server_adv/config.py b/modules/text_server_adv/config.py
index 8c8df9b05e..179a2ff378 100644
--- a/modules/text_server_adv/config.py
+++ b/modules/text_server_adv/config.py
@@ -2,6 +2,14 @@ def can_build(env, platform):
return True
+def get_opts(platform):
+ from SCons.Variables import BoolVariable
+
+ return [
+ BoolVariable("graphite", "Enable SIL Graphite smart fonts support", True),
+ ]
+
+
def configure(env):
pass
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 0170c007ae..4a363fdd7a 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -19,9 +19,11 @@ env = SConscript("./godot-cpp/SConstruct")
env.__class__.disable_warnings = methods.disable_warnings
opts = Variables([], ARGUMENTS)
+opts.Add(BoolVariable("brotli_enabled", "Use Brotli library", True))
opts.Add(BoolVariable("freetype_enabled", "Use FreeType library", True))
opts.Add(BoolVariable("msdfgen_enabled", "Use MSDFgen library (require FreeType)", True))
opts.Add(BoolVariable("graphite_enabled", "Use Graphite library (require FreeType)", True))
+opts.Add(BoolVariable("thorvg_enabled", "Use ThorVG library (require FreeType)", True))
opts.Add(BoolVariable("static_icu_data", "Use built-in ICU data", True))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
@@ -33,6 +35,79 @@ if not env["verbose"]:
if env["platform"] == "windows" and not env["use_mingw"]:
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
+# ThorVG
+if env["thorvg_enabled"] and env["freetype_enabled"]:
+ env_tvg = env.Clone()
+ env_tvg.disable_warnings()
+
+ thirdparty_tvg_dir = "../../../thirdparty/thorvg/"
+ thirdparty_tvg_sources = [
+ "src/lib/sw_engine/tvgSwFill.cpp",
+ "src/lib/sw_engine/tvgSwImage.cpp",
+ "src/lib/sw_engine/tvgSwMath.cpp",
+ "src/lib/sw_engine/tvgSwMemPool.cpp",
+ "src/lib/sw_engine/tvgSwRaster.cpp",
+ "src/lib/sw_engine/tvgSwRenderer.cpp",
+ "src/lib/sw_engine/tvgSwRle.cpp",
+ "src/lib/sw_engine/tvgSwShape.cpp",
+ "src/lib/sw_engine/tvgSwStroke.cpp",
+ "src/lib/tvgAccessor.cpp",
+ "src/lib/tvgBezier.cpp",
+ "src/lib/tvgCanvas.cpp",
+ "src/lib/tvgFill.cpp",
+ "src/lib/tvgGlCanvas.cpp",
+ "src/lib/tvgInitializer.cpp",
+ "src/lib/tvgLinearGradient.cpp",
+ "src/lib/tvgLoader.cpp",
+ "src/lib/tvgLzw.cpp",
+ "src/lib/tvgPaint.cpp",
+ "src/lib/tvgPicture.cpp",
+ "src/lib/tvgRadialGradient.cpp",
+ "src/lib/tvgRender.cpp",
+ "src/lib/tvgSaver.cpp",
+ "src/lib/tvgScene.cpp",
+ "src/lib/tvgShape.cpp",
+ "src/lib/tvgSwCanvas.cpp",
+ "src/lib/tvgTaskScheduler.cpp",
+ "src/loaders/external_png/tvgPngLoader.cpp",
+ "src/loaders/jpg/tvgJpgd.cpp",
+ "src/loaders/jpg/tvgJpgLoader.cpp",
+ "src/loaders/raw/tvgRawLoader.cpp",
+ "src/loaders/svg/tvgSvgCssStyle.cpp",
+ "src/loaders/svg/tvgSvgLoader.cpp",
+ "src/loaders/svg/tvgSvgPath.cpp",
+ "src/loaders/svg/tvgSvgSceneBuilder.cpp",
+ "src/loaders/svg/tvgSvgUtil.cpp",
+ "src/loaders/svg/tvgXmlParser.cpp",
+ "src/loaders/tvg/tvgTvgBinInterpreter.cpp",
+ "src/loaders/tvg/tvgTvgLoader.cpp",
+ "src/savers/tvg/tvgTvgSaver.cpp",
+ ]
+ thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources]
+
+ env_tvg.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/lib/sw_engine",
+ "../../../thirdparty/thorvg/src/loaders/external_png",
+ "../../../thirdparty/thorvg/src/loaders/jpg",
+ "../../../thirdparty/thorvg/src/loaders/raw",
+ "../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/loaders/tvg",
+ "../../../thirdparty/thorvg/src/savers/tvg",
+ "../../../thirdparty/libpng",
+ ]
+ )
+ env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
+
+ lib = env_tvg.Library(
+ f'tvg_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
+ thirdparty_tvg_sources,
+ )
+ env.Append(LIBS=[lib])
+
# MSDFGEN
if env["msdfgen_enabled"] and env["freetype_enabled"]:
env_msdfgen = env.Clone()
@@ -67,7 +142,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
env.Append(CPPDEFINES=["MODULE_MSDFGEN_ENABLED"])
lib = env_msdfgen.Library(
- f'msdfgen_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'msdfgen_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_msdfgen_sources,
)
env.Append(LIBS=[lib])
@@ -162,6 +237,25 @@ if env["freetype_enabled"]:
]
thirdparty_freetype_sources += [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources]
+ if env["brotli_enabled"]:
+ thirdparty_brotli_dir = "../../../thirdparty/brotli/"
+ thirdparty_brotli_sources = [
+ "common/constants.c",
+ "common/context.c",
+ "common/dictionary.c",
+ "common/platform.c",
+ "common/shared_dictionary.c",
+ "common/transform.c",
+ "dec/bit_reader.c",
+ "dec/decode.c",
+ "dec/huffman.c",
+ "dec/state.c",
+ ]
+ thirdparty_freetype_sources += [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources]
+ env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+ env_freetype.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"])
+ env.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
@@ -173,13 +267,13 @@ if env["freetype_enabled"]:
"FT_CONFIG_OPTION_SYSTEM_ZLIB",
]
)
- if env["target"] == "debug":
+ if env.dev_build:
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
env.Append(CPPDEFINES=["MODULE_FREETYPE_ENABLED"])
lib = env_freetype.Library(
- f'freetype_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'freetype_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_freetype_sources,
)
env.Append(LIBS=[lib])
@@ -265,6 +359,7 @@ env_harfbuzz.Append(
CPPPATH=[
"../../../thirdparty/harfbuzz/src",
"../../../thirdparty/icu4c/common/",
+ "../../../thirdparty/icu4c/i18n/",
]
)
@@ -300,7 +395,7 @@ if env["freetype_enabled"]:
env.Append(CPPPATH=["../../../thirdparty/harfbuzz/src"])
lib = env_harfbuzz.Library(
- f'harfbuzz_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'harfbuzz_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_harfbuzz_sources,
)
env.Prepend(LIBS=[lib])
@@ -360,7 +455,7 @@ if env["graphite_enabled"] and env["freetype_enabled"]:
)
lib = env_graphite.Library(
- f'graphite_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'graphite_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_graphite_sources,
)
env.Append(LIBS=[lib])
@@ -569,10 +664,14 @@ thirdparty_icu_sources = [
"common/uvectr32.cpp",
"common/uvectr64.cpp",
"common/wintz.cpp",
+ "i18n/scriptset.cpp",
+ "i18n/ucln_in.cpp",
+ "i18n/uspoof.cpp",
+ "i18n/uspoof_impl.cpp",
]
thirdparty_icu_sources = [thirdparty_icu_dir + file for file in thirdparty_icu_sources]
-icu_data_name = "icudt71l.dat"
+icu_data_name = "icudt72l.dat"
if env["static_icu_data"]:
env_icu.Depends("../../../thirdparty/icu4c/icudata.gen.h", "../../../thirdparty/icu4c/" + icu_data_name)
@@ -584,7 +683,7 @@ if env["static_icu_data"]:
else:
thirdparty_sources += ["../icu_data/icudata_stub.cpp"]
-env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/"])
+env_icu.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"])
env_icu.Append(
CXXFLAGS=[
"-DU_STATIC_IMPLEMENTATION",
@@ -610,14 +709,12 @@ env.Append(
"-DICU_DATA_NAME=" + icu_data_name,
]
)
-env.Append(CPPPATH=["../../../thirdparty/icu4c/common/"])
+env.Append(CPPPATH=["../../../thirdparty/icu4c/common/", "../../../thirdparty/icu4c/i18n/"])
if env["platform"] == "windows":
env.Append(LIBS=["advapi32"])
-lib = env_icu.Library(
- f'icu_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}', thirdparty_icu_sources
-)
+lib = env_icu.Library(f'icu_builtin{env["suffix"]}{env["LIBSUFFIX"]}', thirdparty_icu_sources)
env.Append(LIBS=[lib])
env.Append(CPPDEFINES=["GDEXTENSION"])
@@ -637,7 +734,7 @@ if env["platform"] == "macos":
)
else:
library = env.SharedLibrary(
- f'./bin/libtextserver_advanced.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["SHLIBSUFFIX"]}',
+ f'./bin/libtextserver_advanced{env["suffix"]}{env["SHLIBSUFFIX"]}',
source=sources,
)
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 11ed271ae9..c12fcfdfdf 100644
--- a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension
+++ b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension
@@ -4,9 +4,21 @@ entry_symbol = "textserver_advanced_init"
[libraries]
-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.macos.debug.framework"
-macos.release = "bin/libtextserver_advanced.macos.release.framework"
+linux.x86_64.debug = "bin/libtextserver_advanced.linux.template_debug.x86_64.so"
+linux.x86_64.release = "bin/libtextserver_advanced.linux.template_release.x86_64.so"
+linux.x86_32.debug = "bin/libtextserver_advanced.linux.template_debug.x86_32.so"
+linux.x86_32.release = "bin/libtextserver_advanced.linux.template_release.x86_32.so"
+linux.arm64.debug = "bin/libtextserver_advanced.linux.template_debug.arm64.so"
+linux.arm64.release = "bin/libtextserver_advanced.linux.template_release.arm64.so"
+linux.rv64.debug = "bin/libtextserver_advanced.linux.template_debug.rv64.so"
+linux.rv64.release = "bin/libtextserver_advanced.linux.template_release.rv64.so"
+
+windows.x86_64.debug = "bin/libtextserver_advanced.windows.template_debug.x86_64.dll"
+windows.x86_64.release = "bin/libtextserver_advanced.windows.template_release.x86_64.dll"
+windows.x86_32.debug = "bin/libtextserver_advanced.windows.template_debug.x86_32.dll"
+windows.x86_32.release = "bin/libtextserver_advanced.windows.template_release.x86_32.dll"
+windows.arm64.debug = "bin/libtextserver_advanced.windows.template_debug.arm64.dll"
+windows.arm64.release = "bin/libtextserver_advanced.windows.template_release.arm64.dll"
+
+macos.debug = "bin/libtextserver_advanced.macos.template_debug.framework"
+macos.release = "bin/libtextserver_advanced.macos.template_release.framework"
diff --git a/modules/text_server_adv/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp
index 47dfa5ce26..a6a9e1cc1d 100644
--- a/modules/text_server_adv/icu_data/icudata_stub.cpp
+++ b/modules/text_server_adv/icu_data/icudata_stub.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* icudata_stub.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* icudata_stub.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "unicode/udata.h"
#include "unicode/utypes.h"
diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp
index 6a26584506..7820cd5e61 100644
--- a/modules/text_server_adv/register_types.cpp
+++ b/modules/text_server_adv/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -62,7 +62,7 @@ using namespace godot;
extern "C" {
-GDNativeBool GDN_EXPORT textserver_advanced_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) {
+GDExtensionBool GDN_EXPORT textserver_advanced_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
init_obj.register_initializer(&initialize_text_server_adv_module);
diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h
index dfe20c860c..c11765048d 100644
--- a/modules/text_server_adv/register_types.h
+++ b/modules/text_server_adv/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TEXT_SERVER_ADV_REGISTER_TYPES_H
#define TEXT_SERVER_ADV_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp
index 3331254b20..fbe6373538 100644
--- a/modules/text_server_adv/script_iterator.cpp
+++ b/modules/text_server_adv/script_iterator.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* script_iterator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* script_iterator.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "script_iterator.h"
diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h
index 025b62c6fb..164fdec784 100644
--- a/modules/text_server_adv/script_iterator.h
+++ b/modules/text_server_adv/script_iterator.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* script_iterator.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* script_iterator.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 SCRIPT_ITERATOR_H
#define SCRIPT_ITERATOR_H
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index fa234081f0..79ca4a7024 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1,57 +1,59 @@
-/*************************************************************************/
-/* text_server_adv.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* text_server_adv.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "text_server_adv.h"
-#include "core/object/worker_thread_pool.h"
#ifdef GDEXTENSION
// Headers for building as GDExtension plug-in.
-#include <godot_cpp/classes/file.hpp>
+#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
+#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
#include <godot_cpp/core/error_macros.hpp>
using namespace godot;
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
+
#else
// Headers for building as built-in module.
-#include "core/core_bind.h"
+#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
+#include "core/object/worker_thread_pool.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
-#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
-
-using namespace core_bind;
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
#endif
@@ -70,6 +72,10 @@ using namespace core_bind;
#include "msdfgen.h"
#endif
+#ifdef MODULE_SVG_ENABLED
+#include "thorvg_svg_in_ot.h"
+#endif
+
/*************************************************************************/
/* bmp_font_t HarfBuzz Bitmap font interface */
/*************************************************************************/
@@ -328,7 +334,7 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
/*************************************************************************/
-bool TextServerAdvanced::has_feature(Feature p_feature) const {
+bool TextServerAdvanced::_has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SIMPLE_LAYOUT:
case FEATURE_BIDI_LAYOUT:
@@ -346,6 +352,8 @@ bool TextServerAdvanced::has_feature(Feature p_feature) const {
case FEATURE_FONT_VARIABLE:
case FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION:
case FEATURE_USE_SUPPORT_DATA:
+ case FEATURE_UNICODE_IDENTIFIERS:
+ case FEATURE_UNICODE_SECURITY:
return true;
default: {
}
@@ -353,7 +361,7 @@ bool TextServerAdvanced::has_feature(Feature p_feature) const {
return false;
}
-String TextServerAdvanced::get_name() const {
+String TextServerAdvanced::_get_name() const {
#ifdef GDEXTENSION
return "ICU / HarfBuzz / Graphite (GDExtension)";
#else
@@ -361,7 +369,7 @@ String TextServerAdvanced::get_name() const {
#endif
}
-int64_t TextServerAdvanced::get_features() const {
+int64_t TextServerAdvanced::_get_features() const {
int64_t interface_features = FEATURE_SIMPLE_LAYOUT | FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_FONT_BITMAP | FEATURE_FONT_VARIABLE | FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION | FEATURE_USE_SUPPORT_DATA;
#ifdef MODULE_FREETYPE_ENABLED
interface_features |= FEATURE_FONT_DYNAMIC;
@@ -373,25 +381,31 @@ int64_t TextServerAdvanced::get_features() const {
return interface_features;
}
-void TextServerAdvanced::free_rid(const RID &p_rid) {
+void TextServerAdvanced::_free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
FontAdvanced *fd = font_owner.get_or_null(p_rid);
- font_owner.free(p_rid);
+ {
+ MutexLock lock(fd->mutex);
+ font_owner.free(p_rid);
+ }
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_rid);
- shaped_owner.free(p_rid);
+ {
+ MutexLock lock(sd->mutex);
+ shaped_owner.free(p_rid);
+ }
memdelete(sd);
}
}
-bool TextServerAdvanced::has(const RID &p_rid) {
+bool TextServerAdvanced::_has(const RID &p_rid) {
_THREAD_SAFE_METHOD_
return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
}
-bool TextServerAdvanced::load_support_data(const String &p_filename) {
+bool TextServerAdvanced::_load_support_data(const String &p_filename) {
_THREAD_SAFE_METHOD_
#ifdef ICU_STATIC_DATA
@@ -404,9 +418,8 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) {
if (!icu_data_loaded) {
String filename = (p_filename.is_empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename;
- Ref<File> f;
- f.instantiate();
- if (f->open(filename, File::READ) != OK) {
+ Ref<FileAccess> f = FileAccess::open(filename, FileAccess::READ);
+ if (f.is_null()) {
return false;
}
uint64_t len = f->get_length();
@@ -429,7 +442,7 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) {
return true;
}
-String TextServerAdvanced::get_support_data_filename() const {
+String TextServerAdvanced::_get_support_data_filename() const {
#ifdef ICU_STATIC_DATA
return _MKSTR(ICU_DATA_NAME);
#else
@@ -437,7 +450,7 @@ String TextServerAdvanced::get_support_data_filename() const {
#endif
}
-String TextServerAdvanced::get_support_data_info() const {
+String TextServerAdvanced::_get_support_data_info() const {
#ifdef ICU_STATIC_DATA
return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(").");
#else
@@ -445,15 +458,14 @@ String TextServerAdvanced::get_support_data_info() const {
#endif
}
-bool TextServerAdvanced::save_support_data(const String &p_filename) const {
+bool TextServerAdvanced::_save_support_data(const String &p_filename) const {
_THREAD_SAFE_METHOD_
#ifdef ICU_STATIC_DATA
// Store data to the res file if it's available.
- Ref<File> f;
- f.instantiate();
- if (f->open(p_filename, File::WRITE) != OK) {
+ Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::WRITE);
+ if (f.is_null()) {
return false;
}
@@ -468,7 +480,7 @@ bool TextServerAdvanced::save_support_data(const String &p_filename) const {
#endif
}
-bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const {
+bool TextServerAdvanced::_is_locale_right_to_left(const String &p_locale) const {
String l = p_locale.get_slicec('_', 0);
if ((l == "ar") || (l == "dv") || (l == "he") || (l == "fa") || (l == "ff") || (l == "ku") || (l == "ur")) {
return true;
@@ -740,7 +752,7 @@ void TextServerAdvanced::_insert_feature_sets() {
_insert_feature("weight", HB_TAG('w', 'g', 'h', 't'), Variant::Type::INT, false);
}
-int64_t TextServerAdvanced::name_to_tag(const String &p_name) const {
+int64_t TextServerAdvanced::_name_to_tag(const String &p_name) const {
if (feature_sets.has(p_name)) {
return feature_sets[p_name];
}
@@ -763,7 +775,7 @@ bool TextServerAdvanced::_get_tag_hidden(int64_t p_tag) const {
return false;
}
-String TextServerAdvanced::tag_to_name(int64_t p_tag) const {
+String TextServerAdvanced::_tag_to_name(int64_t p_tag) const {
if (feature_sets_inv.has(p_tag)) {
return feature_sets_inv[p_tag].name;
}
@@ -781,87 +793,45 @@ String TextServerAdvanced::tag_to_name(int64_t p_tag) 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;
int mw = p_width;
int mh = p_height;
- for (int i = 0; i < p_data->textures.size(); i++) {
- const FontTexture &ct = p_data->textures[i];
-
- if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture.
+ ShelfPackTexture *ct = p_data->textures.ptrw();
+ for (int32_t i = 0; i < p_data->textures.size(); i++) {
+ if (p_image_format != ct[i].format) {
continue;
}
-
- if (ct.offsets.size() < ct.texture_w) {
+ if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.
continue;
}
- ret.y = 0x7fffffff;
- ret.x = 0;
-
- for (int j = 0; j < ct.texture_w - mw; j++) {
- int max_y = 0;
-
- for (int k = j; k < j + mw; k++) {
- int y = ct.offsets[k];
- if (y > max_y) {
- max_y = y;
- }
- }
-
- if (max_y < ret.y) {
- ret.y = max_y;
- ret.x = j;
- }
- }
-
- if (ret.y == 0x7fffffff || ret.y + mh > ct.texture_h) {
- continue; // Fail, could not fit it here.
+ ret = ct[i].pack_rect(i, mh, mw);
+ if (ret.index != -1) {
+ break;
}
-
- ret.index = i;
- break;
}
if (ret.index == -1) {
// Could not find texture to fit, create one.
- ret.x = 0;
- ret.y = 0;
-
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(texsize);
-#else
texsize = next_power_of_2(texsize);
-#endif
if (p_msdf) {
texsize = MIN(texsize, 2048);
} else {
texsize = MIN(texsize, 1024);
}
if (mw > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mw);
-#else
texsize = next_power_of_2(mw);
-#endif
}
if (mh > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mh);
-#else
texsize = next_power_of_2(mh);
-#endif
}
- FontTexture tex;
- tex.texture_w = texsize;
- tex.texture_h = texsize;
+ ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
tex.format = p_image_format;
tex.imgdata.resize(texsize * texsize * p_color_size);
-
{
// Zero texture.
uint8_t *w = tex.imgdata.ptrw();
@@ -884,14 +854,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
ERR_FAIL_V(ret);
}
}
- tex.offsets.resize(texsize);
- int32_t *offw = tex.offsets.ptrw();
- for (int i = 0; i < texsize; i++) { // Zero offsets.
- offw[i] = 0;
- }
-
p_data->textures.push_back(tex);
- ret.index = p_data->textures.size() - 1;
+
+ int32_t idx = p_data->textures.size() - 1;
+ ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);
}
return ret;
@@ -963,14 +929,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con
return 0;
}
-void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+void TextServerAdvanced::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);
msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
- int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;
for (int col = 0; col < td->output->width(); ++col) {
- int x = (y % 2) ? td->output->width() - col - 1 : col;
- msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ int x = (p_y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));
msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
}
@@ -1025,7 +991,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
- FontTexture &tex = p_data->textures.write[tex_pos.index];
+ ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
edgeColoringSimple(shape, 3.0); // Max. angle.
msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
@@ -1040,7 +1006,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
- WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, SNAME("FontServerRasterizeMSDF"));
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerAdvanced::_generateMTSDF_threaded, &td, h, -1, true, String("FontServerRasterizeMSDF"));
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
@@ -1062,12 +1028,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
tex.dirty = true;
- // Update height array.
- int32_t *offw = tex.offsets.ptrw();
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- offw[k] = tex_pos.y + mh;
- }
-
chr.texture_idx = tex_pos.index;
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
@@ -1080,9 +1040,28 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_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 {
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const {
int w = bitmap.width;
int h = bitmap.rows;
+ int color_size = 2;
+
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO:
+ case FT_PIXEL_MODE_GRAY: {
+ color_size = 2;
+ } break;
+ case FT_PIXEL_MODE_BGRA: {
+ color_size = 4;
+ } break;
+ case FT_PIXEL_MODE_LCD: {
+ color_size = 4;
+ w /= 3;
+ } break;
+ case FT_PIXEL_MODE_LCD_V: {
+ color_size = 4;
+ h /= 3;
+ } break;
+ }
int mw = w + p_rect_margin * 4;
int mh = h + p_rect_margin * 4;
@@ -1090,15 +1069,13 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
ERR_FAIL_COND_V(mw > 4096, FontGlyph());
ERR_FAIL_COND_V(mh > 4096, FontGlyph());
- int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
// Fit character in char texture.
-
- FontTexture &tex = p_data->textures.write[tex_pos.index];
+ ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
{
uint8_t *wr = tex.imgdata.ptrw();
@@ -1125,6 +1102,34 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
} break;
+ case FT_PIXEL_MODE_LCD: {
+ int ofs_color = i * bitmap.pitch + (j * 3);
+ if (p_bgra) {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = 255;
+ } else {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 3] = 255;
+ }
+ } break;
+ case FT_PIXEL_MODE_LCD_V: {
+ int ofs_color = i * bitmap.pitch * 3 + j;
+ if (p_bgra) {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 3] = 255;
+ } else {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
+ wr[ofs + 3] = 255;
+ }
+ } break;
default:
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + ".");
break;
@@ -1135,12 +1140,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
tex.dirty = true;
- // Update height array.
- int32_t *offw = tex.offsets.ptrw();
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- offw[k] = tex_pos.y + mh;
- }
-
FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
@@ -1228,9 +1227,44 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data,
FT_Outline_Transform(&fd->face->glyph->outline, &mat);
}
+ FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL;
+ bool bgra = false;
+ switch (p_font_data->antialiasing) {
+ case FONT_ANTIALIASING_NONE: {
+ aa_mode = FT_RENDER_MODE_MONO;
+ } break;
+ case FONT_ANTIALIASING_GRAY: {
+ aa_mode = FT_RENDER_MODE_NORMAL;
+ } break;
+ case FONT_ANTIALIASING_LCD: {
+ int aa_layout = (int)((p_glyph >> 24) & 7);
+ switch (aa_layout) {
+ case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: {
+ aa_mode = FT_RENDER_MODE_LCD;
+ bgra = false;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: {
+ aa_mode = FT_RENDER_MODE_LCD;
+ bgra = true;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: {
+ aa_mode = FT_RENDER_MODE_LCD_V;
+ bgra = false;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: {
+ aa_mode = FT_RENDER_MODE_LCD_V;
+ bgra = true;
+ } break;
+ default: {
+ aa_mode = FT_RENDER_MODE_NORMAL;
+ } break;
+ }
+ } break;
+ }
+
if (!outline) {
if (!p_font_data->msdf) {
- error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ error = FT_Render_Glyph(fd->face->glyph, aa_mode);
}
FT_GlyphSlot slot = fd->face->glyph;
if (!error) {
@@ -1242,7 +1276,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data,
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
#endif
} else {
- gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
}
}
} else {
@@ -1262,11 +1296,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data,
if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
goto cleanup_glyph;
}
- if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) {
goto cleanup_glyph;
}
glyph_bitmap = (FT_BitmapGlyph)glyph;
- gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra);
cleanup_glyph:
FT_Done_Glyph(glyph);
@@ -1295,7 +1329,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
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)) + "'.");
+ if (error != 0) {
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+#ifdef MODULE_SVG_ENABLED
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+#endif
}
memset(&fd->stream, 0, sizeof(FT_StreamRec));
@@ -1311,17 +1351,20 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
fargs.stream = &fd->stream;
int max_index = 0;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
- if (error == 0) {
+ if (tmp_face && error == 0) {
max_index = tmp_face->num_faces - 1;
}
- FT_Done_Face(tmp_face);
+ if (tmp_face) {
+ 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;
+ memdelete(fd);
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
}
@@ -1329,7 +1372,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
fd->oversampling = 1.0;
fd->size.x = p_font_data->msdf_source_size;
} else if (p_font_data->oversampling <= 0.0) {
- fd->oversampling = font_get_global_oversampling();
+ fd->oversampling = _font_get_global_oversampling();
} else {
fd->oversampling = p_font_data->oversampling;
}
@@ -1359,7 +1402,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
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;
+#if HB_VERSION_ATLEAST(3, 3, 0)
hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform[0][1]);
+#else
+#ifndef _MSC_VER
+#warning Building with HarfBuzz < 3.3.0, synthetic slant offset correction disabled.
+#endif
+#endif
if (!p_font_data->face_init) {
// Get style flags and name.
@@ -1369,11 +1418,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -1626,6 +1677,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
for (unsigned int i = 0; i < count; i++) {
Dictionary ftr;
+#if HB_VERSION_ATLEAST(2, 1, 0)
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;
@@ -1635,6 +1687,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
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());
}
+#else
+#ifndef _MSC_VER
+#warning Building with HarfBuzz < 2.1.0, readable OpenType feature names disabled.
+#endif
+#endif
ftr["type"] = _get_tag_type(feature_tags[i]);
ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
@@ -1649,6 +1706,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
for (unsigned int i = 0; i < count; i++) {
Dictionary ftr;
+#if HB_VERSION_ATLEAST(2, 1, 0)
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;
@@ -1658,6 +1716,11 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
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());
}
+#else
+#ifndef _MSC_VER
+#warning Building with HarfBuzz < 2.1.0, readable OpenType feature names disabled.
+#endif
+#endif
ftr["type"] = _get_tag_type(feature_tags[i]);
ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
@@ -1704,8 +1767,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
coords.write[i] = CLAMP(var.value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
- if (p_font_data->variation_coordinates.has(tag_to_name(var.tag))) {
- var.value = p_font_data->variation_coordinates[tag_to_name(var.tag)];
+ if (p_font_data->variation_coordinates.has(_tag_to_name(var.tag))) {
+ var.value = p_font_data->variation_coordinates[_tag_to_name(var.tag)];
coords.write[i] = CLAMP(var.value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
@@ -1717,6 +1780,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
FT_Done_MM_Var(ft_library, amaster);
}
#else
+ memdelete(fd);
ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
} else {
@@ -1750,7 +1814,7 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_
return fd->cache[size]->hb_handle;
}
-RID TextServerAdvanced::create_font() {
+RID TextServerAdvanced::_create_font() {
_THREAD_SAFE_METHOD_
FontAdvanced *fd = memnew(FontAdvanced);
@@ -1758,7 +1822,7 @@ RID TextServerAdvanced::create_font() {
return font_owner.make_rid(fd);
}
-void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
+void TextServerAdvanced::_font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1769,7 +1833,7 @@ void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteAr
fd->data_size = fd->data.size();
}
-void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
+void TextServerAdvanced::_font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1780,7 +1844,7 @@ void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
-void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+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);
@@ -1794,7 +1858,7 @@ void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_fa
}
}
-int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const {
+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);
@@ -1802,7 +1866,7 @@ int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const {
return fd->face_index;
}
-int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
+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);
@@ -1816,6 +1880,9 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
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)) + "'.");
+#ifdef MODULE_SVG_ENABLED
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+#endif
}
FT_StreamRec stream;
@@ -1831,19 +1898,19 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
face_count = tmp_face->num_faces;
+ FT_Done_Face(tmp_face);
}
- FT_Done_Face(tmp_face);
#endif
}
return face_count;
}
-void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {
+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);
@@ -1853,7 +1920,7 @@ void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyl
fd->style_flags = p_style;
}
-BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_font_rid) const {
+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);
@@ -1863,7 +1930,7 @@ BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_
return fd->style_flags;
}
-void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String &p_name) {
+void TextServerAdvanced::_font_set_style_name(const RID &p_font_rid, const String &p_name) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1873,7 +1940,7 @@ void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String
fd->style_name = p_name;
}
-String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
+String TextServerAdvanced::_font_get_style_name(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -1883,7 +1950,47 @@ String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
-void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_name) {
+void TextServerAdvanced::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerAdvanced::_font_get_weight(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerAdvanced::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerAdvanced::_font_get_stretch(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
+void TextServerAdvanced::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1893,7 +2000,7 @@ void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_na
fd->font_name = p_name;
}
-String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
+String TextServerAdvanced::_font_get_name(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -1903,26 +2010,26 @@ String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
return fd->font_name;
}
-void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
+void TextServerAdvanced::_font_set_antialiasing(const RID &p_font_rid, TextServer::FontAntialiasing p_antialiasing) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- if (fd->antialiased != p_antialiased) {
+ if (fd->antialiasing != p_antialiasing) {
_font_clear_cache(fd);
- fd->antialiased = p_antialiased;
+ fd->antialiasing = p_antialiasing;
}
}
-bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const {
+TextServer::FontAntialiasing TextServerAdvanced::_font_get_antialiasing(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, false);
+ ERR_FAIL_COND_V(!fd, TextServer::FONT_ANTIALIASING_NONE);
MutexLock lock(fd->mutex);
- return fd->antialiased;
+ return fd->antialiasing;
}
-void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
+void TextServerAdvanced::_font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1931,13 +2038,14 @@ void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p
for (KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
+ E.value->textures.write[i].texture = Ref<ImageTexture>();
}
}
fd->mipmaps = p_generate_mipmaps;
}
}
-bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const {
+bool TextServerAdvanced::_font_get_generate_mipmaps(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1945,7 +2053,7 @@ bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const
return fd->mipmaps;
}
-void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
+void TextServerAdvanced::_font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1956,7 +2064,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 {
+bool TextServerAdvanced::_font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1964,7 +2072,7 @@ bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p
return fd->msdf;
}
-void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
+void TextServerAdvanced::_font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1975,7 +2083,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 {
+int64_t TextServerAdvanced::_font_get_msdf_pixel_range(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1983,7 +2091,7 @@ int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) con
return fd->msdf_range;
}
-void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
+void TextServerAdvanced::_font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1994,7 +2102,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 {
+int64_t TextServerAdvanced::_font_get_msdf_size(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2002,7 +2110,7 @@ int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const {
return fd->msdf_source_size;
}
-void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
+void TextServerAdvanced::_font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2010,7 +2118,7 @@ void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
fd->fixed_size = p_fixed_size;
}
-int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
+int64_t TextServerAdvanced::_font_get_fixed_size(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2018,7 +2126,23 @@ int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
-void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
+void TextServerAdvanced::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->allow_system_fallback = p_allow_system_fallback;
+}
+
+bool TextServerAdvanced::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
+void TextServerAdvanced::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2029,7 +2153,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 {
+bool TextServerAdvanced::_font_is_force_autohinter(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2037,7 +2161,7 @@ bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const {
return fd->force_autohinter;
}
-void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
+void TextServerAdvanced::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2048,7 +2172,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 {
+TextServer::Hinting TextServerAdvanced::_font_get_hinting(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
@@ -2056,7 +2180,7 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid)
return fd->hinting;
}
-void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
+void TextServerAdvanced::_font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2064,7 +2188,7 @@ void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, Te
fd->subpixel_positioning = p_subpixel;
}
-TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positioning(const RID &p_font_rid) const {
+TextServer::SubpixelPositioning TextServerAdvanced::_font_get_subpixel_positioning(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
@@ -2072,7 +2196,7 @@ TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positionin
return fd->subpixel_positioning;
}
-void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_strength) {
+void TextServerAdvanced::_font_set_embolden(const RID &p_font_rid, double p_strength) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2083,7 +2207,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 {
+double TextServerAdvanced::_font_get_embolden(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2091,7 +2215,7 @@ double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const {
return fd->embolden;
}
-void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
+void TextServerAdvanced::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2102,7 +2226,7 @@ void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transfo
}
}
-Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const {
+Transform2D TextServerAdvanced::_font_get_transform(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
@@ -2110,7 +2234,7 @@ Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const
return fd->transform;
}
-void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
+void TextServerAdvanced::_font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2121,7 +2245,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 {
+Dictionary TextServerAdvanced::_font_get_variation_coordinates(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -2129,7 +2253,7 @@ Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_
return fd->variation_coordinates;
}
-void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
+void TextServerAdvanced::_font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2140,7 +2264,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 {
+double TextServerAdvanced::_font_get_oversampling(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2148,19 +2272,19 @@ double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const {
return fd->oversampling;
}
-Array TextServerAdvanced::font_get_size_cache_list(const RID &p_font_rid) const {
+TypedArray<Vector2i> TextServerAdvanced::_font_get_size_cache_list(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>());
MutexLock lock(fd->mutex);
- Array ret;
+ TypedArray<Vector2i> ret;
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) {
+void TextServerAdvanced::_font_clear_size_cache(const RID &p_font_rid) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2171,7 +2295,7 @@ void TextServerAdvanced::font_clear_size_cache(const RID &p_font_rid) {
fd->cache.clear();
}
-void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerAdvanced::_font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2182,7 +2306,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) {
+void TextServerAdvanced::_font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2193,7 +2317,7 @@ void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->ascent = p_ascent;
}
-double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
+double TextServerAdvanced::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2209,7 +2333,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) {
+void TextServerAdvanced::_font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2219,7 +2343,7 @@ void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->descent = p_descent;
}
-double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
+double TextServerAdvanced::_font_get_descent(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2235,7 +2359,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) {
+void TextServerAdvanced::_font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2246,7 +2370,7 @@ void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int6
fd->cache[size]->underline_position = p_underline_position;
}
-double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
+double TextServerAdvanced::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2262,7 +2386,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) {
+void TextServerAdvanced::_font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2273,7 +2397,7 @@ void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int
fd->cache[size]->underline_thickness = p_underline_thickness;
}
-double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
+double TextServerAdvanced::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2289,7 +2413,7 @@ 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) {
+void TextServerAdvanced::_font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2305,7 +2429,7 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d
fd->cache[size]->scale = p_scale;
}
-double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
+double TextServerAdvanced::_font_get_scale(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -2321,7 +2445,7 @@ double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
+int64_t TextServerAdvanced::_font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
@@ -2333,7 +2457,7 @@ int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const
return fd->cache[size]->textures.size();
}
-void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerAdvanced::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2343,7 +2467,7 @@ void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector
fd->cache[size]->textures.clear();
}
-void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
+void TextServerAdvanced::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2355,7 +2479,7 @@ void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector
fd->cache[size]->textures.remove_at(p_texture_index);
}
-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) {
+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) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -2368,16 +2492,14 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec
fd->cache[size]->textures.resize(p_texture_index + 1);
}
- FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.imgdata = p_image->get_data();
tex.texture_w = p_image->get_width();
tex.texture_h = p_image->get_height();
tex.format = p_image->get_format();
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -2386,7 +2508,7 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec
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 {
+Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
@@ -2395,15 +2517,12 @@ Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, con
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
- const FontTexture &tex = fd->cache[size]->textures[p_texture_index];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
-
- return img;
+ const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
+ return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
}
-void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
+void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {
+ ERR_FAIL_COND(p_offsets.size() % 4 != 0);
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2415,11 +2534,14 @@ void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const V
fd->cache[size]->textures.resize(p_texture_index + 1);
}
- FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
- tex.offsets = p_offset;
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.shelves.clear();
+ for (int32_t i = 0; i < p_offsets.size(); i += 4) {
+ tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));
+ }
}
-PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
+PackedInt32Array TextServerAdvanced::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
@@ -2428,19 +2550,31 @@ PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
- const FontTexture &tex = fd->cache[size]->textures[p_texture_index];
- return tex.offsets;
+ const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
+ PackedInt32Array ret;
+ ret.resize(tex.shelves.size() * 4);
+
+ int32_t *wr = ret.ptrw();
+ int32_t i = 0;
+ for (const Shelf &E : tex.shelves) {
+ wr[i * 4] = E.x;
+ wr[i * 4 + 1] = E.y;
+ wr[i * 4 + 2] = E.w;
+ wr[i * 4 + 3] = E.h;
+ i++;
+ }
+ return ret;
}
-Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
+PackedInt32Array TextServerAdvanced::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
- Array ret;
+ PackedInt32Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
for (const KeyValue<int32_t, FontGlyph> &E : gl) {
ret.push_back(E.key);
@@ -2448,7 +2582,7 @@ Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vecto
return ret;
}
-void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerAdvanced::_font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2459,7 +2593,7 @@ void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i
fd->cache[size]->glyph_map.clear();
}
-void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
+void TextServerAdvanced::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2484,7 +2618,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 {
+Vector2 TextServerAdvanced::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -2492,7 +2626,16 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
@@ -2503,16 +2646,17 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_
ea.x = fd->embolden * double(size.x) / 64.0;
}
+ double scale = _font_get_scale(p_font_rid, p_size);
if (fd->msdf) {
- return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- return (gl[p_glyph].advance + ea).round();
+ return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
+ } else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
+ return (gl[p_glyph | mod].advance + ea).round();
} else {
- return gl[p_glyph].advance + ea;
+ return gl[p_glyph | mod].advance + ea;
}
}
-void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
+void TextServerAdvanced::_font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2527,7 +2671,7 @@ void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p
gl[p_glyph].found = true;
}
-Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Vector2 TextServerAdvanced::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -2535,20 +2679,29 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
+ return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
} else {
- return gl[p_glyph].rect.position;
+ return gl[p_glyph | mod].rect.position;
}
}
-void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
+void TextServerAdvanced::_font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2563,7 +2716,7 @@ void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vect
gl[p_glyph].found = true;
}
-Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Vector2 TextServerAdvanced::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -2571,20 +2724,29 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
+ return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
} else {
- return gl[p_glyph].rect.size;
+ return gl[p_glyph | mod].rect.size;
}
}
-void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
+void TextServerAdvanced::_font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2599,7 +2761,7 @@ void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector
gl[p_glyph].found = true;
}
-Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Rect2 TextServerAdvanced::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
@@ -2607,15 +2769,24 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Rect2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- return gl[p_glyph].uv_rect;
+ return gl[p_glyph | mod].uv_rect;
}
-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) {
+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) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2630,7 +2801,7 @@ void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
gl[p_glyph].found = true;
}
-int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+int64_t TextServerAdvanced::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
@@ -2638,15 +2809,24 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1);
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return -1; // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- return gl[p_glyph].texture_idx;
+ return gl[p_glyph | mod].texture_idx;
}
-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) {
+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) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2661,7 +2841,7 @@ void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const
gl[p_glyph].found = true;
}
-RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
@@ -2669,20 +2849,27 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return RID(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID());
+ ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID());
if (RenderingServer::get_singleton() != nullptr) {
- if (gl[p_glyph].texture_idx != -1) {
- if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ if (gl[p_glyph | mod].texture_idx != -1) {
+ if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -2693,14 +2880,14 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
tex.dirty = false;
}
- return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid();
+ return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid();
}
}
return RID();
}
-Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
@@ -2708,20 +2895,27 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Size2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2());
+ ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2());
if (RenderingServer::get_singleton() != nullptr) {
- if (gl[p_glyph].texture_idx != -1) {
- if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ if (gl[p_glyph | mod].texture_idx != -1) {
+ if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -2732,14 +2926,14 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
}
tex.dirty = false;
}
- return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size();
+ return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size();
}
}
return Size2();
}
-Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
+Dictionary TextServerAdvanced::_font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -2789,23 +2983,23 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in
#endif
}
-Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
+TypedArray<Vector2i> TextServerAdvanced::_font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>());
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), TypedArray<Vector2i>());
- Array ret;
+ TypedArray<Vector2i> ret;
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) {
+void TextServerAdvanced::_font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2816,7 +3010,7 @@ void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p
fd->cache[size]->kerning_map.clear();
}
-void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
+void TextServerAdvanced::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2827,7 +3021,7 @@ void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_si
fd->cache[size]->kerning_map.erase(p_glyph_pair);
}
-void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+void TextServerAdvanced::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2838,7 +3032,7 @@ void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
}
-Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
+Vector2 TextServerAdvanced::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -2871,7 +3065,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
return Vector2();
}
-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 {
+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 {
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) + ".");
@@ -2896,10 +3090,12 @@ int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t
#endif
}
-bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) const {
+bool TextServerAdvanced::_font_has_char(const RID &p_font_rid, int64_t p_char) const {
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) + ".");
+ if (!fd) {
+ return false;
+ }
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
@@ -2915,7 +3111,7 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
}
-String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const {
+String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -2948,7 +3144,7 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const
return chars;
}
-void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
+void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
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) + ".");
@@ -2964,16 +3160,18 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i
if (fd->msdf) {
_ensure_glyph(fd, size, (int32_t)idx);
} else {
- if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- } else {
- _ensure_glyph(fd, size, (int32_t)idx);
+ for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {
+ if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24));
+ } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ } else {
+ _ensure_glyph(fd, size, (int32_t)idx | (aa << 24));
+ }
}
}
}
@@ -2981,7 +3179,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) {
+void TextServerAdvanced::_font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2994,23 +3192,25 @@ void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i
if (fd->msdf) {
_ensure_glyph(fd, size, (int32_t)idx);
} else {
- if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- } else {
- _ensure_glyph(fd, size, (int32_t)idx);
+ for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {
+ if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24));
+ } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ } else {
+ _ensure_glyph(fd, size, (int32_t)idx | (aa << 24));
+ }
}
}
}
#endif
}
-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 {
+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 {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3019,9 +3219,19 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
int32_t index = p_index & 0xffffff; // Remove subpixel shifts.
+ bool lcd_aa = false;
#ifdef MODULE_FREETYPE_ENABLED
if (!fd->msdf && fd->cache[size]->face) {
+ // LCD layout, bits 24, 25, 26
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ lcd_aa = true;
+ index = index | (layout << 24);
+ }
+ }
+ // Subpixel X-shift, bits 27, 28
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
index = index | (xshift << 27);
@@ -3043,16 +3253,14 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
if (gl.texture_idx != -1) {
Color modulate = p_color;
#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
modulate.r = modulate.g = modulate.b = 1.0;
}
#endif
if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -3068,27 +3276,33 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
Point2 cpos = p_pos;
cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size;
Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size;
- RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);
} else {
+ double scale = _font_get_scale(p_font_rid, p_size);
Point2 cpos = p_pos;
- cpos.y = Math::floor(cpos.y);
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.125));
+ cpos.x = cpos.x + 0.125;
} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.25));
- } else {
+ cpos.x = cpos.x + 0.25;
+ }
+ if (scale == 1.0) {
+ cpos.y = Math::floor(cpos.y);
cpos.x = Math::floor(cpos.x);
}
cpos += gl.rect.position;
Size2 csize = gl.rect.size;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ if (lcd_aa) {
+ RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ }
}
}
}
}
}
-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 {
+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 {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3097,9 +3311,19 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
int32_t index = p_index & 0xffffff; // Remove subpixel shifts.
+ bool lcd_aa = false;
#ifdef MODULE_FREETYPE_ENABLED
if (!fd->msdf && fd->cache[size]->face) {
+ // LCD layout, bits 24, 25, 26
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ lcd_aa = true;
+ index = index | (layout << 24);
+ }
+ }
+ // Subpixel X-shift, bits 27, 28
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
index = index | (xshift << 27);
@@ -3121,16 +3345,14 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
if (gl.texture_idx != -1) {
Color modulate = p_color;
#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
modulate.r = modulate.g = modulate.b = 1.0;
}
#endif
if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -3146,27 +3368,33 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
Point2 cpos = p_pos;
cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size;
Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size;
- RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);
} else {
Point2 cpos = p_pos;
- cpos.y = Math::floor(cpos.y);
+ double scale = _font_get_scale(p_font_rid, p_size);
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.125));
+ cpos.x = cpos.x + 0.125;
} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.25));
- } else {
+ cpos.x = cpos.x + 0.25;
+ }
+ if (scale == 1.0) {
+ cpos.y = Math::floor(cpos.y);
cpos.x = Math::floor(cpos.x);
}
cpos += gl.rect.position;
Size2 csize = gl.rect.size;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ if (lcd_aa) {
+ RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ }
}
}
}
}
}
-bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
+bool TextServerAdvanced::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -3178,7 +3406,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) {
+void TextServerAdvanced::_font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3186,7 +3414,7 @@ void TextServerAdvanced::font_set_language_support_override(const RID &p_font_ri
fd->language_support_overrides[p_language] = p_supported;
}
-bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
+bool TextServerAdvanced::_font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -3194,7 +3422,7 @@ bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_ri
return fd->language_support_overrides[p_language];
}
-void TextServerAdvanced::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
+void TextServerAdvanced::_font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3202,7 +3430,7 @@ void TextServerAdvanced::font_remove_language_support_override(const RID &p_font
fd->language_support_overrides.erase(p_language);
}
-PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const RID &p_font_rid) {
+PackedStringArray TextServerAdvanced::_font_get_language_support_overrides(const RID &p_font_rid) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
@@ -3214,7 +3442,7 @@ PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const
return out;
}
-bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
+bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -3228,7 +3456,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) {
+void TextServerAdvanced::_font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3236,7 +3464,7 @@ void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid,
fd->script_support_overrides[p_script] = p_supported;
}
-bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
+bool TextServerAdvanced::_font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -3244,7 +3472,7 @@ bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid,
return fd->script_support_overrides[p_script];
}
-void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
+void TextServerAdvanced::_font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3252,7 +3480,7 @@ void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_r
fd->script_support_overrides.erase(p_script);
}
-PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RID &p_font_rid) {
+PackedStringArray TextServerAdvanced::_font_get_script_support_overrides(const RID &p_font_rid) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
@@ -3264,7 +3492,7 @@ PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RI
return out;
}
-void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
+void TextServerAdvanced::_font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -3274,7 +3502,7 @@ void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_r
fd->feature_overrides = p_overrides;
}
-Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
+Dictionary TextServerAdvanced::_font_get_opentype_feature_overrides(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -3282,7 +3510,7 @@ Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_
return fd->feature_overrides;
}
-Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid) const {
+Dictionary TextServerAdvanced::_font_supported_feature_list(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -3292,7 +3520,7 @@ Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid
return fd->supported_features;
}
-Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_rid) const {
+Dictionary TextServerAdvanced::_font_supported_variation_list(const RID &p_font_rid) const {
FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -3302,11 +3530,11 @@ Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_r
return fd->supported_varaitions;
}
-double TextServerAdvanced::font_get_global_oversampling() const {
+double TextServerAdvanced::_font_get_global_oversampling() const {
return oversampling;
}
-void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) {
+void TextServerAdvanced::_font_set_global_oversampling(double p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@@ -3314,8 +3542,8 @@ void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) {
font_owner.get_owned_list(&fonts);
bool font_cleared = false;
for (const RID &E : fonts) {
- if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
- font_clear_size_cache(E);
+ if (!_font_is_multichannel_signed_distance_field(E) && _font_get_oversampling(E) <= 0) {
+ _font_clear_size_cache(E);
font_cleared = true;
}
}
@@ -3424,8 +3652,9 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
p_shaped->parent = RID();
}
-RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
@@ -3434,7 +3663,7 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te
return shaped_owner.make_rid(sd);
}
-void TextServerAdvanced::shaped_text_clear(const RID &p_shaped) {
+void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3449,8 +3678,9 @@ void TextServerAdvanced::shaped_text_clear(const RID &p_shaped) {
invalidate(sd, true);
}
-void TextServerAdvanced::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
+void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -3463,7 +3693,7 @@ void TextServerAdvanced::shaped_text_set_direction(const RID &p_shaped, TextServ
}
}
-TextServer::Direction TextServerAdvanced::shaped_text_get_direction(const RID &p_shaped) const {
+TextServer::Direction TextServerAdvanced::_shaped_text_get_direction(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
@@ -3471,7 +3701,7 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_direction(const RID &p
return sd->direction;
}
-TextServer::Direction TextServerAdvanced::shaped_text_get_inferred_direction(const RID &p_shaped) const {
+TextServer::Direction TextServerAdvanced::_shaped_text_get_inferred_direction(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
@@ -3479,7 +3709,7 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_inferred_direction(con
return sd->para_direction;
}
-void TextServerAdvanced::shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {
+void TextServerAdvanced::_shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {
_THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3493,14 +3723,14 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(const RID &p_shaped,
}
}
-String TextServerAdvanced::shaped_text_get_custom_punctuation(const RID &p_shaped) const {
+String TextServerAdvanced::_shaped_text_get_custom_punctuation(const RID &p_shaped) const {
_THREAD_SAFE_METHOD_
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, String());
return sd->custom_punct;
}
-void TextServerAdvanced::shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {
+void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3510,14 +3740,18 @@ void TextServerAdvanced::shaped_text_set_bidi_override(const RID &p_shaped, cons
}
sd->bidi_override.clear();
for (int i = 0; i < p_override.size(); i++) {
- if (p_override[i].get_type() == Variant::VECTOR2I) {
- sd->bidi_override.push_back(p_override[i]);
+ if (p_override[i].get_type() == Variant::VECTOR3I) {
+ const Vector3i &r = p_override[i];
+ sd->bidi_override.push_back(r);
+ } else if (p_override[i].get_type() == Variant::VECTOR2I) {
+ const Vector2i &r = p_override[i];
+ sd->bidi_override.push_back(Vector3i(r.x, r.y, DIRECTION_INHERITED));
}
}
invalidate(sd, false);
}
-void TextServerAdvanced::shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {
+void TextServerAdvanced::_shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3531,7 +3765,7 @@ void TextServerAdvanced::shaped_text_set_orientation(const RID &p_shaped, TextSe
}
}
-void TextServerAdvanced::shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {
+void TextServerAdvanced::_shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3543,7 +3777,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(const RID &p_shaped, b
}
}
-bool TextServerAdvanced::shaped_text_get_preserve_invalid(const RID &p_shaped) const {
+bool TextServerAdvanced::_shaped_text_get_preserve_invalid(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3551,7 +3785,7 @@ bool TextServerAdvanced::shaped_text_get_preserve_invalid(const RID &p_shaped) c
return sd->preserve_invalid;
}
-void TextServerAdvanced::shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {
+void TextServerAdvanced::_shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3565,7 +3799,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(const RID &p_shaped, b
}
}
-bool TextServerAdvanced::shaped_text_get_preserve_control(const RID &p_shaped) const {
+bool TextServerAdvanced::_shaped_text_get_preserve_control(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3573,7 +3807,7 @@ 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) {
+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);
@@ -3588,7 +3822,7 @@ void TextServerAdvanced::shaped_text_set_spacing(const RID &p_shaped, SpacingTyp
}
}
-int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+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);
@@ -3598,7 +3832,7 @@ int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, Spacing
return sd->extra_spacing[p_spacing];
}
-TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RID &p_shaped) const {
+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);
@@ -3606,41 +3840,33 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RI
return sd->orientation;
}
-int64_t TextServerAdvanced::shaped_get_span_count(const RID &p_shaped) const {
+int64_t TextServerAdvanced::_shaped_get_span_count(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
return sd->spans.size();
}
-Variant TextServerAdvanced::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
+Variant TextServerAdvanced::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Variant());
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].meta;
}
-void TextServerAdvanced::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
+void TextServerAdvanced::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
ERR_FAIL_INDEX(p_index, sd->spans.size());
ShapedTextDataAdvanced::Span &span = sd->spans.ptrw()[p_index];
- bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size());
- if (!changed) {
- for (int i = 0; i < p_fonts.size(); i++) {
- changed = changed || (span.fonts[i] != p_fonts[i]);
- }
- }
- if (changed) {
- span.fonts = p_fonts;
- span.font_size = p_size;
- span.features = p_opentype_features;
+ span.fonts = p_fonts;
+ span.font_size = p_size;
+ span.features = p_opentype_features;
- invalidate(sd, false);
- }
+ invalidate(sd, false);
}
-bool TextServerAdvanced::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, const String &p_language, const Variant &p_meta) {
+bool TextServerAdvanced::_shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
@@ -3675,7 +3901,7 @@ bool TextServerAdvanced::shaped_text_add_string(const RID &p_shaped, const Strin
return true;
}
-bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) {
+bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) {
_THREAD_SAFE_METHOD_
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3695,6 +3921,7 @@ bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Varia
obj.inline_align = p_inline_align;
obj.rect.size = p_size;
obj.pos = span.start;
+ obj.baseline = p_baseline;
sd->spans.push_back(span);
sd->text = sd->text + String::chr(0xfffc).repeat(p_length);
@@ -3705,7 +3932,7 @@ bool TextServerAdvanced::shaped_text_add_object(const RID &p_shaped, const Varia
return true;
}
-bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
+bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3713,6 +3940,7 @@ bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Va
ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
sd->objects[p_key].rect.size = p_size;
sd->objects[p_key].inline_align = p_inline_align;
+ sd->objects[p_key].baseline = p_baseline;
if (sd->valid) {
// Recalc string metrics.
sd->ascent = 0;
@@ -3746,14 +3974,14 @@ bool TextServerAdvanced::shaped_text_resize_object(const RID &p_shaped, const Va
} else {
if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
- sd->descent = MAX(sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ sd->ascent = MAX(sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
+ sd->descent = MAX(sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
} else {
- sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
- 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));
+ 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));
} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -3799,6 +4027,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
+ case INLINE_ALIGNMENT_BASELINE_TO: {
+ E.value.rect.position.y -= E.value.baseline;
+ } break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
@@ -3827,6 +4058,9 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
+ case INLINE_ALIGNMENT_BASELINE_TO: {
+ E.value.rect.position.x -= E.value.baseline;
+ } break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
@@ -3840,7 +4074,7 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
p_sd->descent = full_descent;
}
-RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) 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);
@@ -3848,10 +4082,10 @@ RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start,
MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
- return shaped_text_substr(sd->parent, p_start, p_length);
+ return _shaped_text_substr(sd->parent, p_start, p_length);
}
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
@@ -3955,11 +4189,11 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
} else {
if (gl.font_rid.is_valid()) {
if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
- p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
- p_new_sd->descent = MAX(p_new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
+ p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
} else {
- p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- p_new_sd->descent = MAX(p_new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ p_new_sd->descent = MAX(p_new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
} else if (p_new_sd->preserve_invalid || (p_new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
@@ -3985,7 +4219,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
return true;
}
-RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const {
+RID TextServerAdvanced::_shaped_text_get_parent(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -3993,16 +4227,16 @@ 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, BitField<TextServer::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);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
if (!sd->justification_ops_valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_update_justification_ops(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_update_justification_ops(p_shaped);
}
sd->fit_width_minimum_reached = false;
@@ -4150,16 +4384,16 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
return Math::ceil(justification_width);
}
-double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {
+double TextServerAdvanced::_shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
if (!sd->line_breaks_valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_update_breaks(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_update_breaks(p_shaped);
}
for (int i = 0; i < p_tab_stops.size(); i++) {
@@ -4206,13 +4440,13 @@ 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, BitField<TextServer::TextOverrunFlag> 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.");
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped_line);
+ _shaped_text_shape(p_shaped_line);
}
sd->text_trimmed = false;
@@ -4225,7 +4459,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_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;
@@ -4251,30 +4485,30 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!font_has_char(dot_gl_font_rid, '.')) {
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], '.')) {
dot_gl_font_rid = fonts[i];
break;
}
}
}
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!font_has_char(whitespace_gl_font_rid, '.')) {
+ if (!_font_has_char(whitespace_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (font_has_char(fonts[i], ' ')) {
+ if (_font_has_char(fonts[i], ' ')) {
whitespace_gl_font_rid = fonts[i];
break;
}
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.') : -10;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
- int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ') : -10;
- Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
+ int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.', 0) : -10;
+ Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -10;
+ Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
@@ -4296,35 +4530,40 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int glyphs_to = (is_rtl) ? sd_size - 1 : -1;
int glyphs_delta = (is_rtl) ? +1 : -1;
- for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) {
- if (!is_rtl) {
- width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
- }
- if (sd_glyphs[i].count > 0) {
- bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
+ if (enforce_ellipsis && (width + ellipsis_width <= p_width)) {
+ trim_pos = -1;
+ ellipsis_pos = (is_rtl) ? 0 : sd_size;
+ } else {
+ for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) {
+ if (!is_rtl) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ }
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
- if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
- if (cut_per_word && above_min_char_threshold) {
- if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+ if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
+ if (cut_per_word && above_min_char_threshold) {
+ if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+ last_valid_cut = i;
+ found = true;
+ }
+ } else {
last_valid_cut = i;
found = true;
}
- } else {
- last_valid_cut = i;
- found = true;
- }
- if (found) {
- trim_pos = last_valid_cut;
+ if (found) {
+ trim_pos = last_valid_cut;
- if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
- ellipsis_pos = trim_pos;
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
}
- break;
}
}
- }
- if (is_rtl) {
- width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ if (is_rtl) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ }
}
}
@@ -4368,7 +4607,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
}
}
-int64_t TextServerAdvanced::shaped_text_get_trim_pos(const RID &p_shaped) const {
+int64_t TextServerAdvanced::_shaped_text_get_trim_pos(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid.");
@@ -4376,7 +4615,7 @@ int64_t TextServerAdvanced::shaped_text_get_trim_pos(const RID &p_shaped) const
return sd->overrun_trim_data.trim_pos;
}
-int64_t TextServerAdvanced::shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
+int64_t TextServerAdvanced::_shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid.");
@@ -4384,7 +4623,7 @@ int64_t TextServerAdvanced::shaped_text_get_ellipsis_pos(const RID &p_shaped) co
return sd->overrun_trim_data.ellipsis_pos;
}
-const Glyph *TextServerAdvanced::shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
+const Glyph *TextServerAdvanced::_shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataAdvanced invalid.");
@@ -4392,7 +4631,7 @@ const Glyph *TextServerAdvanced::shaped_text_get_ellipsis_glyphs(const RID &p_sh
return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
}
-int64_t TextServerAdvanced::shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
+int64_t TextServerAdvanced::_shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataAdvanced invalid.");
@@ -4400,13 +4639,13 @@ int64_t TextServerAdvanced::shaped_text_get_ellipsis_glyph_count(const RID &p_sh
return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
-bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
+bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped);
+ _shaped_text_shape(p_shaped);
}
if (sd->line_breaks_valid) {
@@ -4417,6 +4656,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
if (!sd->break_ops_valid) {
sd->breaks.clear();
+ sd->break_inserts = 0;
UErrorCode err = U_ZERO_ERROR;
int i = 0;
while (i < sd->spans.size()) {
@@ -4446,6 +4686,11 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
sd->breaks[pos] = false;
}
+ int pos_p = pos - 1 - sd->start;
+ char32_t c = sd->text[pos_p];
+ if (pos - sd->start != sd->end && !is_whitespace(c) && (c != 0xfffc)) {
+ sd->break_inserts++;
+ }
}
}
ubrk_close(bi);
@@ -4454,60 +4699,83 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
sd->break_ops_valid = true;
}
+ Vector<Glyph> glyphs_new;
+
+ bool rewrite = false;
+ int sd_shift = 0;
+ int sd_size = sd->glyphs.size();
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+ Glyph *sd_glyphs_new = nullptr;
+
+ if (sd->break_inserts > 0) {
+ glyphs_new.resize(sd->glyphs.size() + sd->break_inserts);
+ sd_glyphs_new = glyphs_new.ptrw();
+ rewrite = true;
+ } else {
+ sd_glyphs_new = sd_glyphs;
+ }
+
sd->sort_valid = false;
sd->glyphs_logical.clear();
- int sd_size = sd->glyphs.size();
const char32_t *ch = sd->text.ptr();
- Glyph *sd_glyphs = sd->glyphs.ptrw();
int c_punct_size = sd->custom_punct.length();
const char32_t *c_punct = sd->custom_punct.ptr();
for (int i = 0; i < sd_size; i++) {
+ if (rewrite) {
+ for (int j = 0; j < sd_glyphs[i].count; j++) {
+ sd_glyphs_new[sd_shift + i + j] = sd_glyphs[i + j];
+ }
+ }
if (sd_glyphs[i].count > 0) {
char32_t c = ch[sd_glyphs[i].start - sd->start];
if (c == 0xfffc) {
+ i += (sd_glyphs[i].count - 1);
continue;
}
if (c == 0x0009 || c == 0x000b) {
- sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_TAB;
}
if (is_whitespace(c)) {
- sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_SPACE;
}
if (c_punct_size == 0) {
if (u_ispunct(c) && c != 0x005f) {
- sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_PUNCTUATION;
}
} else {
for (int j = 0; j < c_punct_size; j++) {
if (c_punct[j] == c) {
- sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_PUNCTUATION;
break;
}
}
}
if (is_underscore(c)) {
- sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_UNDERSCORE;
}
if (sd->breaks.has(sd_glyphs[i].end)) {
if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
- sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_HARD;
} else if (is_whitespace(c)) {
- sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ sd_glyphs_new[sd_shift + i].flags |= GRAPHEME_IS_BREAK_SOFT;
} else {
int count = sd_glyphs[i].count;
// Do not add extra space at the end of the line.
if (sd_glyphs[i].end == sd->end) {
+ i += (sd_glyphs[i].count - 1);
continue;
}
// Do not add extra space after existing space.
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
if ((i + count < sd_size - 1) && ((sd_glyphs[i + count].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
+ i += (sd_glyphs[i].count - 1);
continue;
}
} else {
- if ((i > 0) && ((sd_glyphs[i - 1].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
+ if ((sd_glyphs[i].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) {
+ i += (sd_glyphs[i].count - 1);
continue;
}
}
@@ -4520,22 +4788,28 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | GRAPHEME_IS_SPACE;
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
gl.flags |= GRAPHEME_IS_RTL;
- sd->glyphs.insert(i, gl); // Insert before.
+ for (int j = sd_glyphs[i].count - 1; j >= 0; j--) {
+ sd_glyphs_new[sd_shift + i + j + 1] = sd_glyphs_new[sd_shift + i + j];
+ }
+ sd_glyphs_new[sd_shift + i] = gl;
} else {
- sd->glyphs.insert(i + count, gl); // Insert after.
+ sd_glyphs_new[sd_shift + i + count] = gl;
}
- i += count;
-
- // Update write pointer and size.
- sd_size = sd->glyphs.size();
- sd_glyphs = sd->glyphs.ptrw();
- continue;
+ sd_shift++;
+ ERR_FAIL_COND_V_MSG(sd_shift > sd->break_inserts, false, "Invalid break insert count!");
}
}
-
i += (sd_glyphs[i].count - 1);
}
}
+ if (sd_shift < sd->break_inserts) {
+ // Note: should not happen with a normal text, but might be a case with special fonts that substitute a long string (with breaks opportunities in it) with a single glyph (like Font Awesome).
+ glyphs_new.resize(sd->glyphs.size() + sd_shift);
+ }
+
+ if (sd->break_inserts > 0) {
+ sd->glyphs = glyphs_new;
+ }
sd->line_breaks_valid = true;
@@ -4617,16 +4891,16 @@ _FORCE_INLINE_ int64_t _generate_kashida_justification_opportunies(const String
return kashida_pos;
}
-bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shaped) {
+bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shaped) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped);
+ _shaped_text_shape(p_shaped);
}
if (!sd->line_breaks_valid) {
- shaped_text_update_breaks(p_shaped);
+ _shaped_text_update_breaks(p_shaped);
}
if (sd->justification_ops_valid) {
@@ -4686,7 +4960,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape
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];
- if (c == 0x0640) {
+ if (c == 0x0640 && sd_glyphs[i].start == sd_glyphs[i].end - 1) {
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
}
if (sd->jstops.has(sd_glyphs[i].start)) {
@@ -4698,6 +4972,11 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape
if (sd_glyphs[i].font_rid != RID()) {
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
if ((sd_glyphs[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
+#if HB_VERSION_ATLEAST(5, 1, 0)
+ if ((i > 0) && ((sd_glyphs[i - 1].flags & GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL) != GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL)) {
+ continue;
+ }
+#endif
gl.start = sd_glyphs[i].start;
gl.end = sd_glyphs[i].end;
gl.repeat = 0;
@@ -4766,7 +5045,8 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(const RID &p_shape
Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size) {
hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size);
- bool subpos = (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
+ double scale = _font_get_scale(p_font, p_font_size);
+ bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(p_font) == SUBPIXEL_POSITIONING_AUTO && p_font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
ERR_FAIL_COND_V(hb_font == nullptr, Glyph());
hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -4792,25 +5072,24 @@ Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char
gl.font_size = p_font_size;
if (glyph_count > 0) {
- double scale = font_get_scale(p_font, p_font_size);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
if (subpos) {
- gl.advance = glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size);
+ gl.advance = (double)glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size);
} else {
- gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size));
+ gl.advance = Math::round((double)glyph_pos[0].x_advance / (64.0 / scale) + _get_extra_advance(p_font, p_font_size));
}
} else {
- gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale));
+ gl.advance = -Math::round((double)glyph_pos[0].y_advance / (64.0 / scale));
}
gl.count = 1;
gl.index = glyph_info[0].codepoint;
if (subpos) {
- gl.x_off = glyph_pos[0].x_offset / (64.0 / scale);
+ gl.x_off = (double)glyph_pos[0].x_offset / (64.0 / scale);
} else {
- gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale));
+ gl.x_off = Math::round((double)glyph_pos[0].x_offset / (64.0 / scale));
}
- gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale));
+ gl.y_off = -Math::round((double)glyph_pos[0].y_offset / (64.0 / scale));
if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
gl.flags |= GRAPHEME_IS_VALID;
@@ -4827,7 +5106,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
if (value >= 0) {
hb_feature_t feature;
if (keys[i].get_type() == Variant::STRING) {
- feature.tag = name_to_tag(keys[i]);
+ feature.tag = _name_to_tag(keys[i]);
} else {
feature.tag = keys[i];
}
@@ -4839,10 +5118,177 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
}
}
-void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, Array p_fonts, int64_t p_span, int64_t p_fb_index) {
+void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end) {
+ RID f;
int fs = p_sd->spans[p_span].font_size;
- if (p_fb_index >= p_fonts.size()) {
- // Add fallback glyphs.
+
+ if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) {
+ // Try font from list.
+ f = p_fonts[p_fb_index];
+ } else if (OS::get_singleton()->has_feature("system_fonts") && p_fonts.size() > 0 && ((p_fb_index == p_fonts.size()) || (p_fb_index > p_fonts.size() && p_start != p_prev_start))) {
+ // Try system fallback.
+ RID fdef = p_fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = p_sd->text.substr(p_start, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ char scr_buffer[5] = { 0, 0, 0, 0, 0 };
+ hb_tag_to_string(hb_script_to_iso15924_tag(p_script), scr_buffer);
+ String script_code = String(scr_buffer);
+ String locale = (p_sd->spans[p_span].language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_sd->spans[p_span].language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, script_code, font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ f = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!f.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ f = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+
+ if (!f.is_valid()) {
+ // No valid font, use fallback hex code boxes.
for (int i = p_start; i < p_end; i++) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
Glyph gl;
@@ -4860,6 +5306,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(fs, gl.index).y);
} else {
gl.advance = get_hex_code_box_size(fs, gl.index).y;
+ gl.y_off = get_hex_code_box_size(fs, gl.index).y;
+ gl.x_off = -Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5);
p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5));
p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5));
}
@@ -4871,28 +5319,32 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
return;
}
- RID f = p_fonts[p_fb_index];
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 scale = _font_get_scale(f, fs);
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);
+ bool subpos = (scale != 1.0) || (_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);
hb_buffer_clear_contents(p_sd->hb_buffer);
hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
+ int flags = (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0);
if (p_sd->preserve_control) {
- hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES;
} else {
- hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ flags |= HB_BUFFER_FLAG_DEFAULT;
}
+#if HB_VERSION_ATLEAST(5, 1, 0)
+ flags |= HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL;
+#endif
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)flags);
hb_buffer_set_script(p_sd->hb_buffer, p_script);
if (p_sd->spans[p_span].language.is_empty()) {
@@ -4906,7 +5358,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start);
Vector<hb_feature_t> ftrs;
- _add_featuers(font_get_opentype_feature_overrides(f), ftrs);
+ _add_featuers(_font_get_opentype_feature_overrides(f), ftrs);
_add_featuers(p_sd->spans[p_span].features, ftrs);
hb_shape(hb_font, p_sd->hb_buffer, ftrs.is_empty() ? nullptr : &ftrs[0], ftrs.size());
@@ -4915,6 +5367,14 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count);
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
// Process glyphs.
if (glyph_count > 0) {
Glyph *w = (Glyph *)memalloc(glyph_count * sizeof(Glyph));
@@ -4953,31 +5413,37 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
gl.end = end;
gl.count = 0;
- gl.font_rid = p_fonts[p_fb_index];
+ gl.font_rid = f;
gl.font_size = fs;
- if (glyph_info[i].mask & HB_GLYPH_FLAG_DEFINED) {
+ if (glyph_info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) {
gl.flags |= GRAPHEME_IS_CONNECTED;
}
+#if HB_VERSION_ATLEAST(5, 1, 0)
+ if (glyph_info[i].mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) {
+ gl.flags |= GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL;
+ }
+#endif
+
gl.index = glyph_info[i].codepoint;
if (gl.index != 0) {
- _ensure_glyph(fd, fss, gl.index);
+ _ensure_glyph(fd, fss, gl.index | mod);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
if (subpos) {
- gl.advance = glyph_pos[i].x_advance / (64.0 / scale) + ea;
+ gl.advance = (double)glyph_pos[i].x_advance / (64.0 / scale) + ea;
} else {
- gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale) + ea);
+ gl.advance = Math::round((double)glyph_pos[i].x_advance / (64.0 / scale) + ea);
}
} else {
- gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale));
+ gl.advance = -Math::round((double)glyph_pos[i].y_advance / (64.0 / scale));
}
if (subpos) {
- gl.x_off = glyph_pos[i].x_offset / (64.0 / scale);
+ gl.x_off = (double)glyph_pos[i].x_offset / (64.0 / scale);
} else {
- gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale));
+ gl.x_off = Math::round((double)glyph_pos[i].x_offset / (64.0 / scale));
}
- gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
+ gl.y_off = -Math::round((double)glyph_pos[i].y_offset / (64.0 / scale));
}
if (!last_run || i < glyph_count - 1) {
// Do not add extra spacing to the last glyph of the string.
@@ -5014,7 +5480,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
for (unsigned int i = 0; i < glyph_count; i++) {
if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
failed_subrun_start = p_end + 1;
failed_subrun_end = p_start;
}
@@ -5023,7 +5489,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off);
p_sd->descent = MAX(p_sd->descent, w[i + j].y_off);
} else {
- double gla = Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5);
+ double gla = Math::round(_font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5);
p_sd->ascent = MAX(p_sd->ascent, gla);
p_sd->descent = MAX(p_sd->descent, gla);
}
@@ -5044,16 +5510,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
memfree(w);
if (failed_subrun_start != p_end + 1) {
- _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end);
}
- p_sd->ascent = MAX(p_sd->ascent, font_get_ascent(f, fs));
- p_sd->descent = MAX(p_sd->descent, font_get_descent(f, fs));
- p_sd->upos = MAX(p_sd->upos, font_get_underline_position(f, fs));
- p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(f, fs));
+ p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs));
+ p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs));
+ p_sd->upos = MAX(p_sd->upos, _font_get_underline_position(f, fs));
+ p_sd->uthk = MAX(p_sd->uthk, _font_get_underline_thickness(f, fs));
}
}
-bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
+bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -5064,7 +5530,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
invalidate(sd, false);
if (sd->parent != RID()) {
- shaped_text_shape(sd->parent);
+ _shaped_text_shape(sd->parent);
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
ERR_FAIL_COND_V(!parent_sd->valid, false);
ERR_FAIL_COND_V(!_shape_substr(sd, parent_sd, sd->start, sd->end - sd->start), false);
@@ -5084,8 +5550,31 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
}
+ int base_para_direction = UBIDI_DEFAULT_LTR;
+ switch (sd->direction) {
+ case DIRECTION_LTR: {
+ sd->para_direction = DIRECTION_LTR;
+ base_para_direction = UBIDI_LTR;
+ } break;
+ case DIRECTION_RTL: {
+ sd->para_direction = DIRECTION_RTL;
+ base_para_direction = UBIDI_RTL;
+ } break;
+ case DIRECTION_INHERITED:
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length());
+ if (direction != UBIDI_NEUTRAL) {
+ sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
+ base_para_direction = direction;
+ } else {
+ sd->para_direction = DIRECTION_LTR;
+ base_para_direction = UBIDI_DEFAULT_LTR;
+ }
+ } break;
+ }
+
if (sd->bidi_override.is_empty()) {
- sd->bidi_override.push_back(Vector2i(sd->start, sd->end));
+ sd->bidi_override.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED));
}
for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
@@ -5101,23 +5590,22 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
- switch (sd->direction) {
+ switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) {
case DIRECTION_LTR: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
- sd->para_direction = DIRECTION_LTR;
} break;
case DIRECTION_RTL: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
- sd->para_direction = DIRECTION_RTL;
+ } break;
+ case DIRECTION_INHERITED: {
+ ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
} break;
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
- sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
} else {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
- sd->para_direction = DIRECTION_LTR;
}
} break;
}
@@ -5164,7 +5652,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
int32_t script_run_end = MIN(sd->script_iter->script_ranges[j].end, bidi_run_end);
char scr_buffer[5] = { 0, 0, 0, 0, 0 };
hb_tag_to_string(hb_script_to_iso15924_tag(sd->script_iter->script_ranges[j].script), scr_buffer);
- String script = String(scr_buffer);
+ String script_code = String(scr_buffer);
int spn_from = (is_rtl) ? 0 : sd->spans.size() - 1;
int spn_to = (is_rtl) ? sd->spans.size() : -1;
@@ -5200,9 +5688,12 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
Array fonts_scr_only;
Array fonts_no_match;
int font_count = span.fonts.size();
- for (int l = 0; l < font_count; l++) {
- if (font_is_script_supported(span.fonts[l], script)) {
- if (font_is_language_supported(span.fonts[l], span.language)) {
+ if (font_count > 0) {
+ fonts.push_back(sd->spans[k].fonts[0]);
+ }
+ for (int l = 1; l < font_count; l++) {
+ if (_font_is_script_supported(span.fonts[l], script_code)) {
+ if (_font_is_language_supported(span.fonts[l], span.language)) {
fonts.push_back(sd->spans[k].fonts[l]);
} else {
fonts_scr_only.push_back(sd->spans[k].fonts[l]);
@@ -5213,7 +5704,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
}
fonts.append_array(fonts_scr_only);
fonts.append_array(fonts_no_match);
- _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
+ _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0);
}
}
}
@@ -5226,7 +5717,7 @@ bool TextServerAdvanced::shaped_text_shape(const RID &p_shaped) {
return sd->valid;
}
-bool TextServerAdvanced::shaped_text_is_ready(const RID &p_shaped) const {
+bool TextServerAdvanced::_shaped_text_is_ready(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -5234,35 +5725,35 @@ bool TextServerAdvanced::shaped_text_is_ready(const RID &p_shaped) const {
return sd->valid;
}
-const Glyph *TextServerAdvanced::shaped_text_get_glyphs(const RID &p_shaped) const {
+const Glyph *TextServerAdvanced::_shaped_text_get_glyphs(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->glyphs.ptr();
}
-int64_t TextServerAdvanced::shaped_text_get_glyph_count(const RID &p_shaped) const {
+int64_t TextServerAdvanced::_shaped_text_get_glyph_count(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->glyphs.size();
}
-const Glyph *TextServerAdvanced::shaped_text_sort_logical(const RID &p_shaped) {
+const Glyph *TextServerAdvanced::_shaped_text_sort_logical(const RID &p_shaped) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
if (!sd->sort_valid) {
@@ -5274,7 +5765,7 @@ const Glyph *TextServerAdvanced::shaped_text_sort_logical(const RID &p_shaped) {
return sd->glyphs_logical.ptr();
}
-Vector2i TextServerAdvanced::shaped_text_get_range(const RID &p_shaped) const {
+Vector2i TextServerAdvanced::_shaped_text_get_range(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
@@ -5282,7 +5773,7 @@ Vector2i TextServerAdvanced::shaped_text_get_range(const RID &p_shaped) const {
return Vector2(sd->start, sd->end);
}
-Array TextServerAdvanced::shaped_text_get_objects(const RID &p_shaped) const {
+Array TextServerAdvanced::_shaped_text_get_objects(const RID &p_shaped) const {
Array ret;
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
@@ -5295,25 +5786,25 @@ Array TextServerAdvanced::shaped_text_get_objects(const RID &p_shaped) const {
return ret;
}
-Rect2 TextServerAdvanced::shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
+Rect2 TextServerAdvanced::_shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->objects[p_key].rect;
}
-Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const {
+Size2 TextServerAdvanced::_shaped_text_get_size(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ 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 + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
@@ -5322,58 +5813,58 @@ Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const {
}
}
-double TextServerAdvanced::shaped_text_get_ascent(const RID &p_shaped) const {
+double TextServerAdvanced::_shaped_text_get_ascent(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
-double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const {
+double TextServerAdvanced::_shaped_text_get_descent(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
-double TextServerAdvanced::shaped_text_get_width(const RID &p_shaped) const {
+double TextServerAdvanced::_shaped_text_get_width(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return Math::ceil(sd->text_trimmed ? sd->width_trimmed : sd->width);
}
-double TextServerAdvanced::shaped_text_get_underline_position(const RID &p_shaped) const {
+double TextServerAdvanced::_shaped_text_get_underline_position(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->upos;
}
-double TextServerAdvanced::shaped_text_get_underline_thickness(const RID &p_shaped) const {
+double TextServerAdvanced::_shaped_text_get_underline_thickness(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
}
return sd->uthk;
@@ -5554,7 +6045,7 @@ void TextServerAdvanced::_insert_num_systems_lang() {
}
}
-String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const {
+String TextServerAdvanced::_format_number(const String &p_string, const String &p_language) const {
const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
@@ -5579,7 +6070,7 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p
return res;
}
-String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
+String TextServerAdvanced::_parse_number(const String &p_string, const String &p_language) const {
const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
@@ -5607,7 +6098,7 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_
return res;
}
-String TextServerAdvanced::percent_sign(const String &p_language) const {
+String TextServerAdvanced::_percent_sign(const String &p_language) const {
const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
for (int i = 0; i < num_systems.size(); i++) {
@@ -5621,7 +6112,69 @@ String TextServerAdvanced::percent_sign(const String &p_language) const {
return "%";
}
-String TextServerAdvanced::strip_diacritics(const String &p_string) const {
+int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedStringArray &p_dict) const {
+ UErrorCode status = U_ZERO_ERROR;
+ int64_t match_index = -1;
+
+ Char16String utf16 = p_string.utf16();
+ Vector<UChar *> skeletons;
+ skeletons.resize(p_dict.size());
+
+ USpoofChecker *sc = uspoof_open(&status);
+ uspoof_setChecks(sc, USPOOF_CONFUSABLE, &status);
+ for (int i = 0; i < p_dict.size(); i++) {
+ Char16String word = p_dict[i].utf16();
+ int32_t len = uspoof_getSkeleton(sc, 0, word.get_data(), -1, NULL, 0, &status);
+ skeletons.write[i] = (UChar *)memalloc(++len * sizeof(UChar));
+ status = U_ZERO_ERROR;
+ uspoof_getSkeleton(sc, 0, word.get_data(), -1, skeletons.write[i], len, &status);
+ }
+
+ int32_t len = uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, NULL, 0, &status);
+ UChar *skel = (UChar *)memalloc(++len * sizeof(UChar));
+ status = U_ZERO_ERROR;
+ uspoof_getSkeleton(sc, 0, utf16.get_data(), -1, skel, len, &status);
+ for (int i = 0; i < skeletons.size(); i++) {
+ if (u_strcmp(skel, skeletons[i]) == 0) {
+ match_index = i;
+ break;
+ }
+ }
+ memfree(skel);
+
+ for (int i = 0; i < skeletons.size(); i++) {
+ memfree(skeletons.write[i]);
+ }
+ uspoof_close(sc);
+
+ ERR_FAIL_COND_V_MSG(U_FAILURE(status), -1, u_errorName(status));
+
+ return match_index;
+}
+
+bool TextServerAdvanced::_spoof_check(const String &p_string) const {
+ UErrorCode status = U_ZERO_ERROR;
+ Char16String utf16 = p_string.utf16();
+
+ USet *allowed = uset_openEmpty();
+ uset_addAll(allowed, uspoof_getRecommendedSet(&status));
+ uset_addAll(allowed, uspoof_getInclusionSet(&status));
+
+ USpoofChecker *sc = uspoof_open(&status);
+ uspoof_setAllowedChars(sc, allowed, &status);
+ uspoof_setRestrictionLevel(sc, USPOOF_MODERATELY_RESTRICTIVE);
+
+ int32_t bitmask = uspoof_check(sc, utf16.get_data(), -1, NULL, &status);
+
+ uspoof_close(sc);
+ uset_close(allowed);
+
+ ERR_FAIL_COND_V_MSG(U_FAILURE(status), false, u_errorName(status));
+
+ return (bitmask != 0);
+}
+
+String TextServerAdvanced::_strip_diacritics(const String &p_string) const {
UErrorCode err = U_ZERO_ERROR;
// Get NFKD normalizer singleton.
@@ -5648,13 +6201,20 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const {
String result;
for (int i = 0; i < normalized_string.length(); i++) {
if (u_getCombiningClass(normalized_string[i]) == 0) {
+#ifdef GDEXTENSION
+ result = result + String::chr(normalized_string[i]);
+#else
result = result + normalized_string[i];
+#endif
}
}
return result;
}
-String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const {
+String TextServerAdvanced::_string_to_upper(const String &p_string, const String &p_language) const {
+ if (p_string.is_empty()) {
+ return p_string;
+ }
const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
@@ -5673,7 +6233,10 @@ String TextServerAdvanced::string_to_upper(const String &p_string, const String
return String::utf16(upper.ptr(), len);
}
-String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const {
+String TextServerAdvanced::_string_to_lower(const String &p_string, const String &p_language) const {
+ if (p_string.is_empty()) {
+ return p_string;
+ }
const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
@@ -5691,7 +6254,7 @@ String TextServerAdvanced::string_to_lower(const String &p_string, const String
return String::utf16(lower.ptr(), len);
}
-PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_string, const String &p_language) const {
+PackedInt32Array TextServerAdvanced::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) 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();
@@ -5699,15 +6262,7 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri
HashSet<int> breaks;
UErrorCode err = U_ZERO_ERROR;
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++) {
- char32_t c = p_string[i];
- if (is_whitespace(c) || is_linebreak(c)) {
- breaks.insert(i);
- }
- }
- } else {
+ if (U_SUCCESS(err)) {
while (ubrk_next(bi) != UBRK_DONE) {
int pos = _convert_pos(p_string, utf16, ubrk_current(bi)) - 1;
if (pos != p_string.length() - 1) {
@@ -5718,25 +6273,266 @@ PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_stri
ubrk_close(bi);
PackedInt32Array ret;
+
+ int line_start = 0;
+ int line_end = 0; // End of last word on current line.
+ int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
+ int word_length = 0;
+
for (int i = 0; i < p_string.length(); i++) {
- char32_t c = p_string[i];
- if (c == 0xfffc) {
- continue;
- }
- if (u_ispunct(c) && c != 0x005F) {
+ const char32_t c = p_string[i];
+
+ if (is_linebreak(c)) {
+ // Force newline.
+ ret.push_back(line_start);
ret.push_back(i);
+ line_start = i + 1;
+ line_end = line_start;
+ word_start = line_start;
+ word_length = 0;
+ } else if (c == 0xfffc) {
continue;
+ } else if ((u_ispunct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) {
+ // A whitespace ends current word.
+ if (word_length > 0) {
+ line_end = i - 1;
+ word_start = -1;
+ word_length = 0;
+ }
+ } else if (breaks.has(i)) {
+ // End current word, no space.
+ if (word_length > 0) {
+ line_end = i;
+ word_start = i + 1;
+ word_length = 0;
+ }
+ if (p_chars_per_line <= 0) {
+ ret.push_back(line_start);
+ ret.push_back(line_end + 1);
+ line_start = word_start;
+ line_end = line_start;
+ }
+ } else {
+ if (word_start == -1) {
+ word_start = i;
+ if (p_chars_per_line <= 0) {
+ ret.push_back(line_start);
+ ret.push_back(line_end + 1);
+ line_start = word_start;
+ line_end = line_start;
+ }
+ }
+ word_length += 1;
+
+ if (p_chars_per_line > 0) {
+ if (word_length > p_chars_per_line) {
+ // Word too long: wrap before current character.
+ ret.push_back(line_start);
+ ret.push_back(i);
+ line_start = i;
+ line_end = i;
+ word_start = i;
+ word_length = 1;
+ } else if (i - line_start + 1 > p_chars_per_line) {
+ // Line too long: wrap after the last word.
+ ret.push_back(line_start);
+ ret.push_back(line_end + 1);
+ line_start = word_start;
+ line_end = line_start;
+ }
+ }
}
- if (is_underscore(c)) {
- ret.push_back(i);
- continue;
+ }
+ if (line_start < p_string.length()) {
+ ret.push_back(line_start);
+ ret.push_back(p_string.length());
+ }
+ return ret;
+}
+
+bool TextServerAdvanced::_is_valid_identifier(const String &p_string) const {
+ enum UAX31SequenceStatus {
+ SEQ_NOT_STARTED,
+ SEQ_STARTED,
+ SEQ_STARTED_VIR,
+ SEQ_NEAR_END,
+ };
+
+ const char32_t *str = p_string.ptr();
+ int len = p_string.length();
+
+ if (len == 0) {
+ return false; // Empty string.
+ }
+
+ UErrorCode err = U_ZERO_ERROR;
+ Char16String utf16 = p_string.utf16();
+ const UNormalizer2 *norm_c = unorm2_getNFCInstance(&err);
+ if (U_FAILURE(err)) {
+ return false; // Failed to load normalizer.
+ }
+ bool isnurom = unorm2_isNormalized(norm_c, utf16.get_data(), utf16.length(), &err);
+ if (U_FAILURE(err) || !isnurom) {
+ return false; // Do not conform to Normalization Form C.
+ }
+
+ UAX31SequenceStatus A1_sequence_status = SEQ_NOT_STARTED;
+ UScriptCode A1_scr = USCRIPT_INHERITED;
+ UAX31SequenceStatus A2_sequence_status = SEQ_NOT_STARTED;
+ UScriptCode A2_scr = USCRIPT_INHERITED;
+ UAX31SequenceStatus B_sequence_status = SEQ_NOT_STARTED;
+ UScriptCode B_scr = USCRIPT_INHERITED;
+
+ for (int i = 0; i < len; i++) {
+ err = U_ZERO_ERROR;
+ UScriptCode scr = uscript_getScript(str[i], &err);
+ if (U_FAILURE(err)) {
+ return false; // Invalid script.
}
- if (breaks.has(i)) {
- ret.push_back(i);
- continue;
+ if (uscript_getUsage(scr) != USCRIPT_USAGE_RECOMMENDED) {
+ return false; // Not a recommended script.
+ }
+ uint8_t cat = u_charType(str[i]);
+ int32_t jt = u_getIntPropertyValue(str[i], UCHAR_JOINING_TYPE);
+
+ // UAX #31 section 2.3 subsections A1, A2 and B, check ZWNJ and ZWJ usage.
+ switch (A1_sequence_status) {
+ case SEQ_NEAR_END: {
+ if ((A1_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != A1_scr)) {
+ return false; // Mixed script.
+ }
+ if (jt == U_JT_RIGHT_JOINING || jt == U_JT_DUAL_JOINING) {
+ A1_sequence_status = SEQ_NOT_STARTED; // Valid end of sequence, reset.
+ } else if (jt != U_JT_TRANSPARENT) {
+ return false; // Invalid end of sequence.
+ }
+ } break;
+ case SEQ_STARTED: {
+ if ((A1_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != A1_scr)) {
+ A1_sequence_status = SEQ_NOT_STARTED; // Reset.
+ } else {
+ if (jt != U_JT_TRANSPARENT) {
+ if (str[i] == 0x200C /*ZWNJ*/) {
+ A1_sequence_status = SEQ_NEAR_END;
+ continue;
+ } else {
+ A1_sequence_status = SEQ_NOT_STARTED; // Reset.
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ if (A1_sequence_status == SEQ_NOT_STARTED) {
+ if (jt == U_JT_LEFT_JOINING || jt == U_JT_DUAL_JOINING) {
+ A1_sequence_status = SEQ_STARTED;
+ A1_scr = scr;
+ }
+ };
+
+ switch (A2_sequence_status) {
+ case SEQ_NEAR_END: {
+ if ((A2_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != A2_scr)) {
+ return false; // Mixed script.
+ }
+ if (cat == U_UPPERCASE_LETTER || cat == U_LOWERCASE_LETTER || cat == U_TITLECASE_LETTER || cat == U_MODIFIER_LETTER || cat == U_OTHER_LETTER) {
+ A2_sequence_status = SEQ_NOT_STARTED; // Valid end of sequence, reset.
+ } else if (cat != U_MODIFIER_LETTER || u_getCombiningClass(str[i]) == 0) {
+ return false; // Invalid end of sequence.
+ }
+ } break;
+ case SEQ_STARTED_VIR: {
+ if ((A2_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != A2_scr)) {
+ A2_sequence_status = SEQ_NOT_STARTED; // Reset.
+ } else {
+ if (str[i] == 0x200C /*ZWNJ*/) {
+ A2_sequence_status = SEQ_NEAR_END;
+ continue;
+ } else if (cat != U_MODIFIER_LETTER || u_getCombiningClass(str[i]) == 0) {
+ A2_sequence_status = SEQ_NOT_STARTED; // Reset.
+ }
+ }
+ } break;
+ case SEQ_STARTED: {
+ if ((A2_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != A2_scr)) {
+ A2_sequence_status = SEQ_NOT_STARTED; // Reset.
+ } else {
+ if (u_getCombiningClass(str[i]) == 9 /*Virama Combining Class*/) {
+ A2_sequence_status = SEQ_STARTED_VIR;
+ } else if (cat != U_MODIFIER_LETTER) {
+ A2_sequence_status = SEQ_NOT_STARTED; // Reset.
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ if (A2_sequence_status == SEQ_NOT_STARTED) {
+ if (cat == U_UPPERCASE_LETTER || cat == U_LOWERCASE_LETTER || cat == U_TITLECASE_LETTER || cat == U_MODIFIER_LETTER || cat == U_OTHER_LETTER) {
+ A2_sequence_status = SEQ_STARTED;
+ A2_scr = scr;
+ }
+ }
+
+ switch (B_sequence_status) {
+ case SEQ_NEAR_END: {
+ if ((B_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != B_scr)) {
+ return false; // Mixed script.
+ }
+ if (u_getIntPropertyValue(str[i], UCHAR_INDIC_SYLLABIC_CATEGORY) != U_INSC_VOWEL_DEPENDENT) {
+ B_sequence_status = SEQ_NOT_STARTED; // Valid end of sequence, reset.
+ } else {
+ return false; // Invalid end of sequence.
+ }
+ } break;
+ case SEQ_STARTED_VIR: {
+ if ((B_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != B_scr)) {
+ B_sequence_status = SEQ_NOT_STARTED; // Reset.
+ } else {
+ if (str[i] == 0x200D /*ZWJ*/) {
+ B_sequence_status = SEQ_NEAR_END;
+ continue;
+ } else if (cat != U_MODIFIER_LETTER || u_getCombiningClass(str[i]) == 0) {
+ B_sequence_status = SEQ_NOT_STARTED; // Reset.
+ }
+ }
+ } break;
+ case SEQ_STARTED: {
+ if ((B_scr > USCRIPT_INHERITED) && (scr > USCRIPT_INHERITED) && (scr != B_scr)) {
+ B_sequence_status = SEQ_NOT_STARTED; // Reset.
+ } else {
+ if (u_getCombiningClass(str[i]) == 9 /*Virama Combining Class*/) {
+ B_sequence_status = SEQ_STARTED_VIR;
+ } else if (cat != U_MODIFIER_LETTER) {
+ B_sequence_status = SEQ_NOT_STARTED; // Reset.
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ if (B_sequence_status == SEQ_NOT_STARTED) {
+ if (cat == U_UPPERCASE_LETTER || cat == U_LOWERCASE_LETTER || cat == U_TITLECASE_LETTER || cat == U_MODIFIER_LETTER || cat == U_OTHER_LETTER) {
+ B_sequence_status = SEQ_STARTED;
+ B_scr = scr;
+ }
+ }
+
+ if (u_hasBinaryProperty(str[i], UCHAR_PATTERN_SYNTAX) || u_hasBinaryProperty(str[i], UCHAR_PATTERN_WHITE_SPACE) || u_hasBinaryProperty(str[i], UCHAR_NONCHARACTER_CODE_POINT)) {
+ return false; // Not a XID_Start or XID_Continue character.
+ }
+ if (i == 0) {
+ if (!(cat == U_LOWERCASE_LETTER || cat == U_UPPERCASE_LETTER || cat == U_TITLECASE_LETTER || cat == U_OTHER_LETTER || cat == U_MODIFIER_LETTER || cat == U_LETTER_NUMBER || str[0] == 0x2118 || str[0] == 0x212E || str[0] == 0x309B || str[0] == 0x309C || str[0] == 0x005F)) {
+ return false; // Not a XID_Start character.
+ }
+ } else {
+ if (!(cat == U_LOWERCASE_LETTER || cat == U_UPPERCASE_LETTER || cat == U_TITLECASE_LETTER || cat == U_OTHER_LETTER || cat == U_MODIFIER_LETTER || cat == U_LETTER_NUMBER || cat == U_NON_SPACING_MARK || cat == U_COMBINING_SPACING_MARK || cat == U_DECIMAL_DIGIT_NUMBER || cat == U_CONNECTOR_PUNCTUATION || str[i] == 0x2118 || str[i] == 0x212E || str[i] == 0x309B || str[i] == 0x309C || str[i] == 0x1369 || str[i] == 0x1371 || str[i] == 0x00B7 || str[i] == 0x0387 || str[i] == 0x19DA || str[i] == 0x0E33 || str[i] == 0x0EB3 || str[i] == 0xFF9E || str[i] == 0xFF9F)) {
+ return false; // Not a XID_Continue character.
+ }
}
}
- return ret;
+ return true;
}
TextServerAdvanced::TextServerAdvanced() {
@@ -5745,6 +6541,17 @@ TextServerAdvanced::TextServerAdvanced() {
_bmp_create_font_funcs();
}
+void TextServerAdvanced::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
#ifdef MODULE_FREETYPE_ENABLED
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 8cd0e753ba..c7fe46d554 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* text_server_adv.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* text_server_adv.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TEXT_SERVER_ADV_H
#define TEXT_SERVER_ADV_H
@@ -42,6 +42,7 @@
#include <godot_cpp/godot.hpp>
#include <godot_cpp/core/class_db.hpp>
+#include <godot_cpp/core/ext_wrappers.gen.inc>
#include <godot_cpp/core/mutex_lock.hpp>
#include <godot_cpp/variant/array.hpp>
@@ -52,6 +53,7 @@
#include <godot_cpp/variant/rect2.hpp>
#include <godot_cpp/variant/rid.hpp>
#include <godot_cpp/variant/string.hpp>
+#include <godot_cpp/variant/typed_array.hpp>
#include <godot_cpp/variant/vector2.hpp>
#include <godot_cpp/variant/vector2i.hpp>
@@ -70,7 +72,6 @@
#include <godot_cpp/templates/hash_map.hpp>
#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@@ -78,13 +79,15 @@ using namespace godot;
#else
// Headers for building as built-in module.
+#include "servers/text/text_server_extension.h"
+
+#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/worker_thread_pool.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
#include "scene/resources/texture.h"
-#include "servers/text/text_server_extension.h"
-#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
#endif
@@ -101,6 +104,7 @@ using namespace godot;
#include <unicode/uloc.h>
#include <unicode/unorm2.h>
#include <unicode/uscript.h>
+#include <unicode/uspoof.h>
#include <unicode/ustring.h>
#include <unicode/utypes.h>
@@ -112,7 +116,11 @@ using namespace godot;
#include FT_ADVANCES_H
#include FT_MULTIPLE_MASTERS_H
#include FT_BBOX_H
-
+#include FT_MODULE_H
+#include FT_CONFIG_OPTIONS_H
+#if !defined(FT_CONFIG_OPTION_USE_BROTLI) && !defined(_MSC_VER)
+#warning FreeType is configured without Brotli support, built-in fonts will not be available.
+#endif
#include <hb-ft.h>
#include <hb-ot.h>
#endif
@@ -160,20 +168,86 @@ class TextServerAdvanced : public TextServerExtension {
const int rect_range = 1;
- struct FontTexture {
+ struct FontTexturePosition {
+ int32_t index = -1;
+ int32_t x = 0;
+ int32_t y = 0;
+
+ FontTexturePosition() {}
+ FontTexturePosition(int32_t p_id, int32_t p_x, int32_t p_y) :
+ index(p_id), x(p_x), y(p_y) {}
+ };
+
+ struct Shelf {
+ int32_t x = 0;
+ int32_t y = 0;
+ int32_t w = 0;
+ int32_t h = 0;
+
+ FontTexturePosition alloc_shelf(int32_t p_id, int32_t p_w, int32_t p_h) {
+ if (p_w > w || p_h > h) {
+ return FontTexturePosition(-1, 0, 0);
+ }
+ int32_t xx = x;
+ x += p_w;
+ w -= p_w;
+ return FontTexturePosition(p_id, xx, y);
+ }
+
+ Shelf() {}
+ Shelf(int32_t p_x, int32_t p_y, int32_t p_w, int32_t p_h) :
+ x(p_x), y(p_y), w(p_w), h(p_h) {}
+ };
+
+ struct ShelfPackTexture {
+ int32_t texture_w = 1024;
+ int32_t texture_h = 1024;
+
Image::Format format;
PackedByteArray imgdata;
- int texture_w = 0;
- int texture_h = 0;
- PackedInt32Array offsets;
Ref<ImageTexture> texture;
bool dirty = true;
- };
- struct FontTexturePosition {
- int index = 0;
- int x = 0;
- int y = 0;
+ List<Shelf> shelves;
+
+ FontTexturePosition pack_rect(int32_t p_id, int32_t p_h, int32_t p_w) {
+ int32_t y = 0;
+ int32_t waste = 0;
+ Shelf *best_shelf = nullptr;
+ int32_t best_waste = std::numeric_limits<std::int32_t>::max();
+
+ for (Shelf &E : shelves) {
+ y += E.h;
+ if (p_w > E.w) {
+ continue;
+ }
+ if (p_h == E.h) {
+ return E.alloc_shelf(p_id, p_w, p_h);
+ }
+ if (p_h > E.h) {
+ continue;
+ }
+ if (p_h < E.h) {
+ waste = (E.h - p_h) * p_w;
+ if (waste < best_waste) {
+ best_waste = waste;
+ best_shelf = &E;
+ }
+ }
+ }
+ if (best_shelf) {
+ return best_shelf->alloc_shelf(p_id, p_w, p_h);
+ }
+ if (p_h <= (texture_h - y) && p_w <= texture_w) {
+ List<Shelf>::Element *E = shelves.push_back(Shelf(0, y, texture_w, p_h));
+ return E->get().alloc_shelf(p_id, p_w, p_h);
+ }
+ return FontTexturePosition(-1, 0, 0);
+ }
+
+ ShelfPackTexture() {}
+ ShelfPackTexture(int32_t p_w, int32_t p_h) :
+ texture_w(p_w), texture_h(p_h) {}
};
struct FontGlyph {
@@ -194,7 +268,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector2i size;
- Vector<FontTexture> textures;
+ Vector<ShelfPackTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr;
@@ -219,12 +293,13 @@ class TextServerAdvanced : public TextServerExtension {
struct FontAdvanced {
Mutex mutex;
- bool antialiased = true;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
bool mipmaps = false;
bool msdf = false;
int msdf_range = 14;
int msdf_source_size = 48;
int fixed_size = 0;
+ bool allow_system_fallback = true;
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
@@ -236,6 +311,8 @@ class TextServerAdvanced : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
@@ -267,12 +344,12 @@ class TextServerAdvanced : public TextServerExtension {
_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(FontForSizeAdvanced *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, bool p_bgra) const;
#endif
_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;
+ static void _generateMTSDF_threaded(void *p_td, uint32_t p_y);
_FORCE_INLINE_ Vector2i _get_size(const FontAdvanced *p_font_data, int p_size) const {
if (p_font_data->msdf) {
@@ -297,6 +374,57 @@ 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;
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
// Shaped text cache data.
struct TrimData {
@@ -338,6 +466,7 @@ class TextServerAdvanced : public TextServerExtension {
int pos = 0;
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
+ float baseline = 0;
};
HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
@@ -370,12 +499,13 @@ class TextServerAdvanced : public TextServerExtension {
/* Intermediate data */
Char16String utf16;
Vector<UBiDi *> bidi_iter;
- Vector<Vector2i> bidi_override;
+ Vector<Vector3i> bidi_override;
ScriptIterator *script_iter = nullptr;
hb_buffer_t *hb_buffer = nullptr;
HashMap<int, bool> jstops;
HashMap<int, bool> breaks;
+ int break_inserts = 0;
bool break_ops_valid = false;
bool js_ops_valid = false;
@@ -398,12 +528,87 @@ class TextServerAdvanced : public TextServerExtension {
mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerAdvanced *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataAdvanced *p_sd) const;
int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const;
int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const;
- void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, Array p_fonts, int64_t p_span, int64_t p_fb_index);
+ void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end);
Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size);
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
@@ -457,254 +662,270 @@ protected:
void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false);
public:
- virtual bool has_feature(Feature p_feature) const override;
- virtual String get_name() const override;
- virtual int64_t get_features() const override;
+ MODBIND1RC(bool, has_feature, Feature);
+ MODBIND0RC(String, get_name);
+ MODBIND0RC(int64_t, get_features);
- virtual void free_rid(const RID &p_rid) override;
- virtual bool has(const RID &p_rid) override;
- virtual bool load_support_data(const String &p_filename) override;
+ MODBIND1(free_rid, const RID &);
+ MODBIND1R(bool, has, const RID &);
+ MODBIND1R(bool, load_support_data, const String &);
- virtual String get_support_data_filename() const override;
- virtual String get_support_data_info() const override;
- virtual bool save_support_data(const String &p_filename) const override;
+ MODBIND0RC(String, get_support_data_filename);
+ MODBIND0RC(String, get_support_data_info);
+ MODBIND1RC(bool, save_support_data, const String &);
- virtual bool is_locale_right_to_left(const String &p_locale) const override;
+ MODBIND1RC(bool, is_locale_right_to_left, const String &);
- virtual int64_t name_to_tag(const String &p_name) const override;
- virtual String tag_to_name(int64_t p_tag) const override;
+ MODBIND1RC(int64_t, name_to_tag, const String &);
+ MODBIND1RC(String, tag_to_name, int64_t);
/* Font interface */
- virtual RID create_font() override;
- 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;
+ MODBIND0R(RID, create_font);
+
+ MODBIND2(font_set_data, const RID &, const PackedByteArray &);
+ MODBIND3(font_set_data_ptr, const RID &, const uint8_t *, int64_t);
+
+ MODBIND2(font_set_face_index, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_face_index, const RID &);
- 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;
+ MODBIND1RC(int64_t, font_get_face_count, const RID &);
- virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+ MODBIND2(font_set_style, const RID &, BitField<FontStyle>);
+ MODBIND1RC(BitField<FontStyle>, font_get_style, const RID &);
- 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;
+ MODBIND2(font_set_style_name, const RID &, const String &);
+ MODBIND1RC(String, font_get_style_name, const RID &);
- 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;
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
- virtual void font_set_name(const RID &p_font_rid, const String &p_name) override;
- virtual String font_get_name(const RID &p_font_rid) const override;
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
- virtual void font_set_antialiased(const RID &p_font_rid, bool p_antialiased) override;
- virtual bool font_is_antialiased(const RID &p_font_rid) const override;
+ MODBIND2(font_set_name, const RID &, const String &);
+ MODBIND1RC(String, font_get_name, const RID &);
- virtual void font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) override;
- virtual bool font_get_generate_mipmaps(const RID &p_font_rid) const override;
+ MODBIND2(font_set_antialiasing, const RID &, TextServer::FontAntialiasing);
+ MODBIND1RC(TextServer::FontAntialiasing, font_get_antialiasing, const RID &);
- virtual void font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) override;
- virtual bool font_is_multichannel_signed_distance_field(const RID &p_font_rid) const override;
+ MODBIND2(font_set_generate_mipmaps, const RID &, bool);
+ MODBIND1RC(bool, font_get_generate_mipmaps, const RID &);
- virtual void font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) override;
- virtual int64_t font_get_msdf_pixel_range(const RID &p_font_rid) const override;
+ MODBIND2(font_set_multichannel_signed_distance_field, const RID &, bool);
+ MODBIND1RC(bool, font_is_multichannel_signed_distance_field, const RID &);
- virtual void font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) override;
- virtual int64_t font_get_msdf_size(const RID &p_font_rid) const override;
+ MODBIND2(font_set_msdf_pixel_range, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_msdf_pixel_range, const RID &);
- virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) override;
- virtual int64_t font_get_fixed_size(const RID &p_font_rid) const override;
+ MODBIND2(font_set_msdf_size, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_msdf_size, const RID &);
- virtual void font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) override;
- virtual bool font_is_force_autohinter(const RID &p_font_rid) const override;
+ MODBIND2(font_set_fixed_size, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
- virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override;
- virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override;
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
- virtual void font_set_embolden(const RID &p_font_rid, double p_strength) override;
- virtual double font_get_embolden(const RID &p_font_rid) const override;
+ MODBIND2(font_set_force_autohinter, const RID &, bool);
+ MODBIND1RC(bool, font_is_force_autohinter, const RID &);
- virtual void font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) override;
- virtual Transform2D font_get_transform(const RID &p_font_rid) const override;
+ MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
+ MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
- virtual void font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) override;
- virtual Dictionary font_get_variation_coordinates(const RID &p_font_rid) const override;
+ MODBIND2(font_set_embolden, const RID &, double);
+ MODBIND1RC(double, font_get_embolden, const RID &);
- virtual void font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) override;
- virtual TextServer::Hinting font_get_hinting(const RID &p_font_rid) const override;
+ MODBIND2(font_set_transform, const RID &, const Transform2D &);
+ MODBIND1RC(Transform2D, font_get_transform, const RID &);
- virtual void font_set_oversampling(const RID &p_font_rid, double p_oversampling) override;
- virtual double font_get_oversampling(const RID &p_font_rid) const override;
+ MODBIND2(font_set_variation_coordinates, const RID &, const Dictionary &);
+ MODBIND1RC(Dictionary, font_get_variation_coordinates, const RID &);
- virtual Array font_get_size_cache_list(const RID &p_font_rid) const override;
- virtual void font_clear_size_cache(const RID &p_font_rid) override;
- virtual void font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) override;
+ MODBIND2(font_set_hinting, const RID &, TextServer::Hinting);
+ MODBIND1RC(TextServer::Hinting, font_get_hinting, const RID &);
- virtual void font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) override;
- virtual double font_get_ascent(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND2(font_set_oversampling, const RID &, double);
+ MODBIND1RC(double, font_get_oversampling, const RID &);
- virtual void font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) override;
- virtual double font_get_descent(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND1RC(TypedArray<Vector2i>, font_get_size_cache_list, const RID &);
+ MODBIND1(font_clear_size_cache, const RID &);
+ MODBIND2(font_remove_size_cache, const RID &, const Vector2i &);
- virtual void font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) override;
- virtual double font_get_underline_position(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND3(font_set_ascent, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_ascent, const RID &, int64_t);
- virtual void font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) override;
- virtual double font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND3(font_set_descent, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_descent, const RID &, int64_t);
- 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;
+ MODBIND3(font_set_underline_position, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_underline_position, const RID &, int64_t);
- 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;
+ MODBIND3(font_set_underline_thickness, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_underline_thickness, const RID &, int64_t);
- virtual void font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) override;
- virtual Ref<Image> font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
+ MODBIND3(font_set_scale, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_scale, const RID &, int64_t);
- virtual void font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) override;
- virtual PackedInt32Array font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
+ MODBIND2RC(int64_t, font_get_texture_count, const RID &, const Vector2i &);
+ MODBIND2(font_clear_textures, const RID &, const Vector2i &);
+ MODBIND3(font_remove_texture, const RID &, const Vector2i &, int64_t);
- virtual Array font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const override;
- virtual void font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) override;
- virtual void font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) override;
+ MODBIND4(font_set_texture_image, const RID &, const Vector2i &, int64_t, const Ref<Image> &);
+ MODBIND3RC(Ref<Image>, font_get_texture_image, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) override;
+ MODBIND4(font_set_texture_offsets, const RID &, const Vector2i &, int64_t, const PackedInt32Array &);
+ MODBIND3RC(PackedInt32Array, font_get_texture_offsets, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) override;
+ MODBIND2RC(PackedInt32Array, font_get_glyph_list, const RID &, const Vector2i &);
+ MODBIND2(font_clear_glyphs, const RID &, const Vector2i &);
+ MODBIND3(font_remove_glyph, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) override;
+ MODBIND3RC(Vector2, font_get_glyph_advance, const RID &, int64_t, int64_t);
+ MODBIND4(font_set_glyph_advance, const RID &, int64_t, int64_t, const Vector2 &);
- virtual Rect2 font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) override;
+ MODBIND3RC(Vector2, font_get_glyph_offset, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_offset, const RID &, const Vector2i &, int64_t, const Vector2 &);
- virtual int64_t font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) override;
+ MODBIND3RC(Vector2, font_get_glyph_size, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_size, const RID &, const Vector2i &, int64_t, const Vector2 &);
- virtual RID font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual Size2 font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
+ MODBIND3RC(Rect2, font_get_glyph_uv_rect, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_uv_rect, const RID &, const Vector2i &, int64_t, const Rect2 &);
- virtual Dictionary font_get_glyph_contours(const RID &p_font, int64_t p_size, int64_t p_index) const override;
+ MODBIND3RC(int64_t, font_get_glyph_texture_idx, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_texture_idx, const RID &, const Vector2i &, int64_t, int64_t);
- virtual Array font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) override;
- virtual void font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) override;
+ MODBIND3RC(RID, font_get_glyph_texture_rid, const RID &, const Vector2i &, int64_t);
+ MODBIND3RC(Size2, font_get_glyph_texture_size, const RID &, const Vector2i &, int64_t);
- virtual void font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
- virtual Vector2 font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const override;
+ MODBIND3RC(Dictionary, font_get_glyph_contours, const RID &, int64_t, int64_t);
- virtual int64_t font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector = 0) const override;
+ MODBIND2RC(TypedArray<Vector2i>, font_get_kerning_list, const RID &, int64_t);
+ MODBIND2(font_clear_kerning_map, const RID &, int64_t);
+ MODBIND3(font_remove_kerning, const RID &, int64_t, const Vector2i &);
- virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override;
- virtual String font_get_supported_chars(const RID &p_font_rid) const override;
+ MODBIND4(font_set_kerning, const RID &, int64_t, const Vector2i &, const Vector2 &);
+ MODBIND3RC(Vector2, font_get_kerning, const RID &, int64_t, const Vector2i &);
- virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override;
- virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override;
+ MODBIND4RC(int64_t, font_get_glyph_index, const RID &, int64_t, int64_t, int64_t);
- virtual void font_draw_glyph(const RID &p_font, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual void font_draw_glyph_outline(const RID &p_font, 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 = Color(1, 1, 1)) const override;
+ MODBIND2RC(bool, font_has_char, const RID &, int64_t);
+ MODBIND1RC(String, font_get_supported_chars, const RID &);
- virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const override;
- virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(const RID &p_font_rid, const String &p_language) override;
- virtual void font_remove_language_support_override(const RID &p_font_rid, const String &p_language) override;
- virtual PackedStringArray font_get_language_support_overrides(const RID &p_font_rid) override;
+ MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
+ MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
- virtual bool font_is_script_supported(const RID &p_font_rid, const String &p_script) const override;
- virtual void font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(const RID &p_font_rid, const String &p_script) override;
- virtual void font_remove_script_support_override(const RID &p_font_rid, const String &p_script) override;
- virtual PackedStringArray font_get_script_support_overrides(const RID &p_font_rid) override;
+ MODBIND6C(font_draw_glyph, const RID &, const RID &, int64_t, const Vector2 &, int64_t, const Color &);
+ MODBIND7C(font_draw_glyph_outline, const RID &, const RID &, int64_t, int64_t, const Vector2 &, int64_t, const Color &);
- virtual void font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) override;
- virtual Dictionary font_get_opentype_feature_overrides(const RID &p_font_rid) const override;
+ MODBIND2RC(bool, font_is_language_supported, const RID &, const String &);
+ MODBIND3(font_set_language_support_override, const RID &, const String &, bool);
+ MODBIND2R(bool, font_get_language_support_override, const RID &, const String &);
+ MODBIND2(font_remove_language_support_override, const RID &, const String &);
+ MODBIND1R(PackedStringArray, font_get_language_support_overrides, const RID &);
- virtual Dictionary font_supported_feature_list(const RID &p_font_rid) const override;
- virtual Dictionary font_supported_variation_list(const RID &p_font_rid) const override;
+ MODBIND2RC(bool, font_is_script_supported, const RID &, const String &);
+ MODBIND3(font_set_script_support_override, const RID &, const String &, bool);
+ MODBIND2R(bool, font_get_script_support_override, const RID &, const String &);
+ MODBIND2(font_remove_script_support_override, const RID &, const String &);
+ MODBIND1R(PackedStringArray, font_get_script_support_overrides, const RID &);
- virtual double font_get_global_oversampling() const override;
- virtual void font_set_global_oversampling(double p_oversampling) override;
+ MODBIND2(font_set_opentype_feature_overrides, const RID &, const Dictionary &);
+ MODBIND1RC(Dictionary, font_get_opentype_feature_overrides, const RID &);
+
+ MODBIND1RC(Dictionary, font_supported_feature_list, const RID &);
+ MODBIND1RC(Dictionary, font_supported_variation_list, const RID &);
+
+ MODBIND0RC(double, font_get_global_oversampling);
+ MODBIND1(font_set_global_oversampling, double);
/* Shaped text buffer interface */
- virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+ MODBIND2R(RID, create_shaped_text, Direction, Orientation);
+
+ MODBIND1(shaped_text_clear, const RID &);
+
+ MODBIND2(shaped_text_set_direction, const RID &, Direction);
+ MODBIND1RC(Direction, shaped_text_get_direction, const RID &);
+ MODBIND1RC(Direction, shaped_text_get_inferred_direction, const RID &);
- virtual void shaped_text_clear(const RID &p_shaped) override;
+ MODBIND2(shaped_text_set_bidi_override, const RID &, const Array &);
- virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) override;
- virtual Direction shaped_text_get_direction(const RID &p_shaped) const override;
- virtual Direction shaped_text_get_inferred_direction(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_custom_punctuation, const RID &, const String &);
+ MODBIND1RC(String, shaped_text_get_custom_punctuation, const RID &);
- virtual void shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) override;
+ MODBIND2(shaped_text_set_orientation, const RID &, Orientation);
+ MODBIND1RC(Orientation, shaped_text_get_orientation, const RID &);
- virtual void shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) override;
- virtual String shaped_text_get_custom_punctuation(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_preserve_invalid, const RID &, bool);
+ MODBIND1RC(bool, shaped_text_get_preserve_invalid, const RID &);
- virtual void shaped_text_set_orientation(const RID &p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
- virtual Orientation shaped_text_get_orientation(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_preserve_control, const RID &, bool);
+ MODBIND1RC(bool, shaped_text_get_preserve_control, const RID &);
- virtual void shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) override;
- virtual bool shaped_text_get_preserve_invalid(const RID &p_shaped) const override;
+ MODBIND3(shaped_text_set_spacing, const RID &, SpacingType, int64_t);
+ MODBIND2RC(int64_t, shaped_text_get_spacing, const RID &, SpacingType);
- 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;
+ MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
+ MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, float);
+ MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, float);
- 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;
+ MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
+ MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
+ MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
- 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;
+ MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);
+ MODBIND1RC(RID, shaped_text_get_parent, const RID &);
- virtual int64_t shaped_get_span_count(const RID &p_shaped) const override;
- virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override;
- virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+ MODBIND3R(double, shaped_text_fit_to_width, const RID &, double, BitField<TextServer::JustificationFlag>);
+ MODBIND2R(double, shaped_text_tab_align, const RID &, const PackedFloat32Array &);
- 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;
+ MODBIND1R(bool, shaped_text_shape, const RID &);
+ MODBIND1R(bool, shaped_text_update_breaks, const RID &);
+ MODBIND1R(bool, shaped_text_update_justification_ops, const RID &);
- 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;
+ MODBIND1RC(int64_t, shaped_text_get_trim_pos, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_ellipsis_pos, const RID &);
+ MODBIND1RC(const Glyph *, shaped_text_get_ellipsis_glyphs, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_ellipsis_glyph_count, const RID &);
- virtual bool shaped_text_shape(const RID &p_shaped) override;
- virtual bool shaped_text_update_breaks(const RID &p_shaped) override;
- virtual bool shaped_text_update_justification_ops(const RID &p_shaped) override;
+ MODBIND3(shaped_text_overrun_trim_to_width, const RID &, double, BitField<TextServer::TextOverrunFlag>);
- virtual int64_t shaped_text_get_trim_pos(const RID &p_shaped) const override;
- virtual int64_t shaped_text_get_ellipsis_pos(const RID &p_shaped) const override;
- 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;
+ MODBIND1RC(bool, shaped_text_is_ready, const RID &);
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
+ MODBIND1RC(const Glyph *, shaped_text_get_glyphs, const RID &);
+ MODBIND1R(const Glyph *, shaped_text_sort_logical, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_glyph_count, const RID &);
- virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
+ MODBIND1RC(Vector2i, shaped_text_get_range, const RID &);
- virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const override;
- virtual const Glyph *shaped_text_sort_logical(const RID &p_shaped) override;
- virtual int64_t shaped_text_get_glyph_count(const RID &p_shaped) const override;
+ MODBIND1RC(Array, shaped_text_get_objects, const RID &);
+ MODBIND2RC(Rect2, shaped_text_get_object_rect, const RID &, const Variant &);
- virtual Vector2i shaped_text_get_range(const RID &p_shaped) const override;
+ MODBIND1RC(Size2, shaped_text_get_size, const RID &);
+ MODBIND1RC(double, shaped_text_get_ascent, const RID &);
+ MODBIND1RC(double, shaped_text_get_descent, const RID &);
+ MODBIND1RC(double, shaped_text_get_width, const RID &);
+ MODBIND1RC(double, shaped_text_get_underline_position, const RID &);
+ MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &);
- virtual Array shaped_text_get_objects(const RID &p_shaped) const override;
- virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override;
+ MODBIND2RC(String, format_number, const String &, const String &);
+ MODBIND2RC(String, parse_number, const String &, const String &);
+ MODBIND1RC(String, percent_sign, const String &);
- virtual Size2 shaped_text_get_size(const RID &p_shaped) const override;
- virtual double shaped_text_get_ascent(const RID &p_shaped) const override;
- virtual double shaped_text_get_descent(const RID &p_shaped) const override;
- virtual double shaped_text_get_width(const RID &p_shaped) const override;
- virtual double shaped_text_get_underline_position(const RID &p_shaped) const override;
- virtual double shaped_text_get_underline_thickness(const RID &p_shaped) const override;
+ MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int);
- virtual String format_number(const String &p_string, const String &p_language = "") const override;
- virtual String parse_number(const String &p_string, const String &p_language = "") const override;
- virtual String percent_sign(const String &p_language = "") const override;
+ MODBIND2RC(int64_t, is_confusable, const String &, const PackedStringArray &);
+ MODBIND1RC(bool, spoof_check, const String &);
- virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const override;
+ MODBIND1RC(String, strip_diacritics, const String &);
+ MODBIND1RC(bool, is_valid_identifier, const String &);
- virtual String strip_diacritics(const String &p_string) const override;
+ MODBIND2RC(String, string_to_upper, const String &, const String &);
+ MODBIND2RC(String, string_to_lower, const String &, const String &);
- virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
- virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+ MODBIND0(cleanup);
TextServerAdvanced();
~TextServerAdvanced();
diff --git a/modules/text_server_adv/thorvg_bounds_iterator.cpp b/modules/text_server_adv/thorvg_bounds_iterator.cpp
new file mode 100644
index 0000000000..807f356b83
--- /dev/null
+++ b/modules/text_server_adv/thorvg_bounds_iterator.cpp
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* thorvg_bounds_iterator.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/godot.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include "thorvg_bounds_iterator.h"
+
+#include <tvgIteratorAccessor.h>
+#include <tvgPaint.h>
+
+// This function uses private ThorVG API to get bounding box of top level children elements.
+
+void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) {
+ tvg::IteratorAccessor itrAccessor;
+ if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) {
+ while (const tvg::Paint *child = it->next()) {
+ float x = 0, y = 0, w = 0, h = 0;
+ child->bounds(&x, &y, &w, &h, true);
+ r_min_x = MIN(x, r_min_x);
+ r_min_y = MIN(y, r_min_y);
+ r_max_x = MAX(x + w, r_max_x);
+ r_max_y = MAX(y + h, r_max_y);
+ }
+ delete (it);
+ }
+}
+
+#endif // MODULE_SVG_ENABLED
diff --git a/modules/mono/mono_gd/managed_type.h b/modules/text_server_adv/thorvg_bounds_iterator.h
index 603ff3aca1..a44cbb99a7 100644
--- a/modules/mono/mono_gd/managed_type.h
+++ b/modules/text_server_adv/thorvg_bounds_iterator.h
@@ -1,55 +1,58 @@
-/*************************************************************************/
-/* managed_type.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 MANAGED_TYPE_H
-#define MANAGED_TYPE_H
-
-#include <mono/metadata/object.h>
-
-#include "gd_mono_header.h"
-
-struct ManagedType {
- int type_encoding = 0;
- GDMonoClass *type_class = nullptr;
-
- static ManagedType from_class(GDMonoClass *p_class);
- static ManagedType from_class(MonoClass *p_mono_class);
- static ManagedType from_type(MonoType *p_mono_type);
- static ManagedType from_reftype(MonoReflectionType *p_mono_reftype);
-
- ManagedType() {}
-
- ManagedType(int p_type_encoding, GDMonoClass *p_type_class) :
- type_encoding(p_type_encoding),
- type_class(p_type_class) {
- }
-};
-
-#endif // MANAGED_TYPE_H
+/**************************************************************************/
+/* thorvg_bounds_iterator.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 THORVG_BOUNDS_ITERATOR_H
+#define THORVG_BOUNDS_ITERATOR_H
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include <thorvg.h>
+
+void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y);
+
+#endif // MODULE_SVG_ENABLED
+
+#endif // THORVG_BOUNDS_ITERATOR_H
diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp
new file mode 100644
index 0000000000..1406e3aaa0
--- /dev/null
+++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp
@@ -0,0 +1,287 @@
+/**************************************************************************/
+/* thorvg_svg_in_ot.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/classes/xml_parser.hpp>
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+#include <godot_cpp/templates/vector.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/error/error_macros.h"
+#include "core/io/xml_parser.h"
+#include "core/os/memory.h"
+#include "core/os/os.h"
+#include "core/string/ustring.h"
+#include "core/typedefs.h"
+#include "core/variant/variant.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include "thorvg_bounds_iterator.h"
+#include "thorvg_svg_in_ot.h"
+
+#include <freetype/otsvg.h>
+#include <ft2build.h>
+
+#include <math.h>
+#include <stdlib.h>
+
+FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state) {
+ *p_state = memnew(TVG_State);
+
+ return FT_Err_Ok;
+}
+
+void tvg_svg_in_ot_free(FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ memdelete(state);
+}
+
+FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ if (!state) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
+ }
+ MutexLock lock(state->mutex);
+
+ FT_SVG_Document document = (FT_SVG_Document)p_slot->other;
+ FT_Size_Metrics metrics = document->metrics;
+
+ GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
+ if (!gl_state.ready) {
+ Ref<XMLParser> parser;
+ parser.instantiate();
+ parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
+
+ float aspect = 1.0f;
+ String xml_body;
+ while (parser->read() == OK) {
+ if (parser->has_attribute("id")) {
+ const String &gl_name = parser->get_named_attribute_value("id");
+ if (gl_name.begins_with("glyph")) {
+ int dot_pos = gl_name.find(".");
+ int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
+ if (p_slot->glyph_index != gl_idx) {
+ parser->skip_section();
+ continue;
+ }
+ }
+ }
+ if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
+ if (parser->has_attribute("viewBox")) {
+ PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
+
+ if (vb.size() == 4) {
+ aspect = vb[2].to_float() / vb[3].to_float();
+ }
+ }
+ continue;
+ }
+ if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
+ xml_body += vformat("<%s", parser->get_node_name());
+ for (int i = 0; i < parser->get_attribute_count(); i++) {
+ xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i));
+ }
+ xml_body += ">";
+ } else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
+ xml_body += parser->get_node_data();
+ } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ xml_body += vformat("</%s>", parser->get_node_name());
+ }
+ }
+ String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
+
+ std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
+ tvg::Result result = picture->load(temp_xml.utf8().get_data(), temp_xml.utf8().length(), "svg+xml", false);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection).");
+ }
+
+ float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY;
+ tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y);
+
+ float new_h = (max_y - min_y);
+ float new_w = (max_x - min_x);
+
+ if (new_h * aspect >= new_w) {
+ new_w = (new_h * aspect);
+ } else {
+ new_h = (new_w / aspect);
+ }
+
+ gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
+
+ picture = tvg::Picture::gen();
+ result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
+ }
+
+ float x_svg_to_out, y_svg_to_out;
+ x_svg_to_out = (float)metrics.x_ppem / new_w;
+ y_svg_to_out = (float)metrics.y_ppem / new_h;
+
+ gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out;
+ gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out;
+ gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out;
+ gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out;
+ gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem;
+ gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem;
+ gl_state.m.e31 = 0;
+ gl_state.m.e32 = 0;
+ gl_state.m.e33 = 1;
+
+ result = picture->transform(gl_state.m);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
+ }
+
+ result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds.");
+ }
+
+ gl_state.bmp_y = -min_y * gl_state.h / new_h;
+ gl_state.bmp_x = min_x * gl_state.w / new_w;
+
+ gl_state.ready = true;
+ }
+
+ p_slot->bitmap_left = (FT_Int)gl_state.bmp_x;
+ p_slot->bitmap_top = (FT_Int)gl_state.bmp_y;
+
+ float tmp = ceil(gl_state.h);
+ p_slot->bitmap.rows = (unsigned int)tmp;
+ tmp = ceil(gl_state.w);
+ p_slot->bitmap.width = (unsigned int)tmp;
+ p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4;
+ p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+
+ float metrics_width, metrics_height;
+ float horiBearingX, horiBearingY;
+ float vertBearingX, vertBearingY;
+
+ metrics_width = (float)gl_state.w;
+ metrics_height = (float)gl_state.h;
+ horiBearingX = (float)gl_state.x;
+ horiBearingY = (float)-gl_state.y;
+ vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2;
+ vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2;
+
+ tmp = roundf(metrics_width * 64);
+ p_slot->metrics.width = (FT_Pos)tmp;
+ tmp = roundf(metrics_height * 64);
+ p_slot->metrics.height = (FT_Pos)tmp;
+
+ p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64);
+ p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64);
+ p_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64);
+ p_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64);
+
+ if (p_slot->metrics.vertAdvance == 0) {
+ p_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64);
+ }
+
+ return FT_Err_Ok;
+}
+
+FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ if (!state) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
+ }
+ MutexLock lock(state->mutex);
+
+ if (!state->glyph_map.has(p_slot->glyph_index)) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG glyph not loaded.");
+ }
+
+ GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
+ ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready.");
+
+ std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
+ tvg::Result res = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
+ }
+ res = picture->transform(gl_state.m);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
+ }
+
+ std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
+ res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
+ }
+ res = sw_canvas->push(std::move(picture));
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to set SVG canvas source.");
+ }
+ res = sw_canvas->draw();
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to draw to SVG canvas.");
+ }
+ res = sw_canvas->sync();
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to sync SVG canvas.");
+ }
+
+ state->glyph_map.erase(p_slot->glyph_index);
+
+ p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+ p_slot->bitmap.num_grays = 256;
+ p_slot->format = FT_GLYPH_FORMAT_BITMAP;
+
+ return FT_Err_Ok;
+}
+
+SVG_RendererHooks tvg_svg_in_ot_hooks = {
+ (SVG_Lib_Init_Func)tvg_svg_in_ot_init,
+ (SVG_Lib_Free_Func)tvg_svg_in_ot_free,
+ (SVG_Lib_Render_Func)tvg_svg_in_ot_render,
+ (SVG_Lib_Preset_Slot_Func)tvg_svg_in_ot_preset_slot,
+};
+
+SVG_RendererHooks *get_tvg_svg_in_ot_hooks() {
+ return &tvg_svg_in_ot_hooks;
+}
+
+#endif // MODULE_SVG_ENABLED
diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h
new file mode 100644
index 0000000000..4035a984b6
--- /dev/null
+++ b/modules/text_server_adv/thorvg_svg_in_ot.h
@@ -0,0 +1,86 @@
+/**************************************************************************/
+/* thorvg_svg_in_ot.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 THORVG_SVG_IN_OT_H
+#define THORVG_SVG_IN_OT_H
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+#include <godot_cpp/templates/hash_map.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/os/mutex.h"
+#include "core/templates/hash_map.h"
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include <freetype/freetype.h>
+#include <freetype/otsvg.h>
+#include <ft2build.h>
+#include <thorvg.h>
+
+struct GL_State {
+ bool ready = false;
+ float bmp_x = 0;
+ float bmp_y = 0;
+ float x = 0;
+ float y = 0;
+ float w = 0;
+ float h = 0;
+ String xml_code;
+ tvg::Matrix m;
+};
+
+struct TVG_State {
+ Mutex mutex;
+ HashMap<uint32_t, GL_State> glyph_map;
+};
+
+FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state);
+void tvg_svg_in_ot_free(FT_Pointer *p_state);
+FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state);
+FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state);
+
+SVG_RendererHooks *get_tvg_svg_in_ot_hooks();
+
+#endif // MODULE_SVG_ENABLED
+
+#endif // THORVG_SVG_IN_OT_H
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index 121f38fcd5..f1d57ec4d3 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -3,15 +3,19 @@
Import("env")
Import("env_modules")
-freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True)
-msdfgen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True)
+freetype_enabled = "freetype" in env.module_list
+msdfgen_enabled = "msdfgen" in env.module_list
env_text_server_fb = env_modules.Clone()
+if "svg" in env.module_list:
+ env_text_server_fb.Prepend(CPPPATH=["#thirdparty/thorvg/inc", "#thirdparty/thorvg/src/lib"])
+
if env["builtin_msdfgen"] and msdfgen_enabled:
- env_text_server_fb.Append(CPPPATH=["#thirdparty/msdfgen"])
+ env_text_server_fb.Prepend(CPPPATH=["#thirdparty/msdfgen"])
if env["builtin_freetype"] and freetype_enabled:
- env_text_server_fb.Append(CPPPATH=["#thirdparty/freetype/include"])
+ env_text_server_fb.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+ env_text_server_fb.Prepend(CPPPATH=["#thirdparty/freetype/include"])
env_text_server_fb.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index de0a549900..7b4c548a21 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -19,8 +19,10 @@ env = SConscript("./godot-cpp/SConstruct")
env.__class__.disable_warnings = methods.disable_warnings
opts = Variables([], ARGUMENTS)
+opts.Add(BoolVariable("brotli_enabled", "Use Brotli library", True))
opts.Add(BoolVariable("freetype_enabled", "Use FreeType library", True))
opts.Add(BoolVariable("msdfgen_enabled", "Use MSDFgen library (require FreeType)", True))
+opts.Add(BoolVariable("thorvg_enabled", "Use ThorVG library (require FreeType)", True))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
opts.Update(env)
@@ -28,6 +30,79 @@ opts.Update(env)
if not env["verbose"]:
methods.no_verbose(sys, env)
+# ThorVG
+if env["thorvg_enabled"] and env["freetype_enabled"]:
+ env_tvg = env.Clone()
+ env_tvg.disable_warnings()
+
+ thirdparty_tvg_dir = "../../../thirdparty/thorvg/"
+ thirdparty_tvg_sources = [
+ "src/lib/sw_engine/tvgSwFill.cpp",
+ "src/lib/sw_engine/tvgSwImage.cpp",
+ "src/lib/sw_engine/tvgSwMath.cpp",
+ "src/lib/sw_engine/tvgSwMemPool.cpp",
+ "src/lib/sw_engine/tvgSwRaster.cpp",
+ "src/lib/sw_engine/tvgSwRenderer.cpp",
+ "src/lib/sw_engine/tvgSwRle.cpp",
+ "src/lib/sw_engine/tvgSwShape.cpp",
+ "src/lib/sw_engine/tvgSwStroke.cpp",
+ "src/lib/tvgAccessor.cpp",
+ "src/lib/tvgBezier.cpp",
+ "src/lib/tvgCanvas.cpp",
+ "src/lib/tvgFill.cpp",
+ "src/lib/tvgGlCanvas.cpp",
+ "src/lib/tvgInitializer.cpp",
+ "src/lib/tvgLinearGradient.cpp",
+ "src/lib/tvgLoader.cpp",
+ "src/lib/tvgLzw.cpp",
+ "src/lib/tvgPaint.cpp",
+ "src/lib/tvgPicture.cpp",
+ "src/lib/tvgRadialGradient.cpp",
+ "src/lib/tvgRender.cpp",
+ "src/lib/tvgSaver.cpp",
+ "src/lib/tvgScene.cpp",
+ "src/lib/tvgShape.cpp",
+ "src/lib/tvgSwCanvas.cpp",
+ "src/lib/tvgTaskScheduler.cpp",
+ "src/loaders/external_png/tvgPngLoader.cpp",
+ "src/loaders/jpg/tvgJpgd.cpp",
+ "src/loaders/jpg/tvgJpgLoader.cpp",
+ "src/loaders/raw/tvgRawLoader.cpp",
+ "src/loaders/svg/tvgSvgCssStyle.cpp",
+ "src/loaders/svg/tvgSvgLoader.cpp",
+ "src/loaders/svg/tvgSvgPath.cpp",
+ "src/loaders/svg/tvgSvgSceneBuilder.cpp",
+ "src/loaders/svg/tvgSvgUtil.cpp",
+ "src/loaders/svg/tvgXmlParser.cpp",
+ "src/loaders/tvg/tvgTvgBinInterpreter.cpp",
+ "src/loaders/tvg/tvgTvgLoader.cpp",
+ "src/savers/tvg/tvgTvgSaver.cpp",
+ ]
+ thirdparty_tvg_sources = [thirdparty_tvg_dir + file for file in thirdparty_tvg_sources]
+
+ env_tvg.Append(
+ CPPPATH=[
+ "../../../thirdparty/thorvg/inc",
+ "../../../thirdparty/thorvg/src/lib",
+ "../../../thirdparty/thorvg/src/lib/sw_engine",
+ "../../../thirdparty/thorvg/src/loaders/external_png",
+ "../../../thirdparty/thorvg/src/loaders/jpg",
+ "../../../thirdparty/thorvg/src/loaders/raw",
+ "../../../thirdparty/thorvg/src/loaders/svg",
+ "../../../thirdparty/thorvg/src/loaders/tvg",
+ "../../../thirdparty/thorvg/src/savers/tvg",
+ "../../../thirdparty/libpng",
+ ]
+ )
+ env.Append(CPPPATH=["../../../thirdparty/thorvg/inc", "../../../thirdparty/thorvg/src/lib"])
+ env.Append(CPPDEFINES=["MODULE_SVG_ENABLED"])
+
+ lib = env_tvg.Library(
+ f'tvg_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
+ thirdparty_tvg_sources,
+ )
+ env.Append(LIBS=[lib])
+
# MSDFGEN
if env["msdfgen_enabled"] and env["freetype_enabled"]:
env_msdfgen = env.Clone()
@@ -62,7 +137,7 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
env.Append(CPPDEFINES=["MODULE_MSDFGEN_ENABLED"])
lib = env_msdfgen.Library(
- f'msdfgen_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'msdfgen_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_msdfgen_sources,
)
env.Append(LIBS=[lib])
@@ -157,6 +232,25 @@ if env["freetype_enabled"]:
]
thirdparty_freetype_sources += [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources]
+ if env["brotli_enabled"]:
+ thirdparty_brotli_dir = "../../../thirdparty/brotli/"
+ thirdparty_brotli_sources = [
+ "common/constants.c",
+ "common/context.c",
+ "common/dictionary.c",
+ "common/platform.c",
+ "common/shared_dictionary.c",
+ "common/transform.c",
+ "dec/bit_reader.c",
+ "dec/decode.c",
+ "dec/huffman.c",
+ "dec/state.c",
+ ]
+ thirdparty_freetype_sources += [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources]
+ env_freetype.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+ env_freetype.Prepend(CPPPATH=[thirdparty_brotli_dir + "include"])
+ env.Append(CPPDEFINES=["FT_CONFIG_OPTION_USE_BROTLI"])
+
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
@@ -168,13 +262,13 @@ if env["freetype_enabled"]:
"FT_CONFIG_OPTION_SYSTEM_ZLIB",
]
)
- if env["target"] == "debug":
+ if env.dev_build:
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
env.Append(CPPDEFINES=["MODULE_FREETYPE_ENABLED"])
lib = env_freetype.Library(
- f'freetype_builtin.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["LIBSUFFIX"]}',
+ f'freetype_builtin{env["suffix"]}{env["LIBSUFFIX"]}',
thirdparty_freetype_sources,
)
env.Append(LIBS=[lib])
@@ -197,7 +291,7 @@ if env["platform"] == "macos":
)
else:
library = env.SharedLibrary(
- f'./bin/libtextserver_fallback.{env["platform"]}.{env["target"]}.{env["arch_suffix"]}{env["SHLIBSUFFIX"]}',
+ f'./bin/libtextserver_fallback{env["suffix"]}{env["SHLIBSUFFIX"]}',
source=sources,
)
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 9236555d63..58a92e403b 100644
--- a/modules/text_server_fb/gdextension_build/text_server_fb.gdextension
+++ b/modules/text_server_fb/gdextension_build/text_server_fb.gdextension
@@ -4,9 +4,21 @@ entry_symbol = "textserver_fallback_init"
[libraries]
-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.macos.debug.framework"
-macos.release = "bin/libtextserver_fallback.macos.release.framework"
+linux.x86_64.debug = "bin/libtextserver_fallback.linux.template_debug.x86_64.so"
+linux.x86_64.release = "bin/libtextserver_fallback.linux.template_release.x86_64.so"
+linux.x86_32.debug = "bin/libtextserver_fallback.linux.template_debug.x86_32.so"
+linux.x86_32.release = "bin/libtextserver_fallback.linux.template_release.x86_32.so"
+linux.arm64.debug = "bin/libtextserver_fallback.linux.template_debug.arm64.so"
+linux.arm64.release = "bin/libtextserver_fallback.linux.template_release.arm64.so"
+linux.rv64.debug = "bin/libtextserver_fallback.linux.template_debug.rv64.so"
+linux.rv64.release = "bin/libtextserver_fallback.linux.template_release.rv64.so"
+
+windows.x86_64.debug = "bin/libtextserver_fallback.windows.template_debug.x86_64.dll"
+windows.x86_64.release = "bin/libtextserver_fallback.windows.template_release.x86_64.dll"
+windows.x86_32.debug = "bin/libtextserver_fallback.windows.template_debug.x86_32.dll"
+windows.x86_32.release = "bin/libtextserver_fallback.windows.template_release.x86_32.dll"
+windows.arm64.debug = "bin/libtextserver_fallback.windows.template_debug.arm64.dll"
+windows.arm64.release = "bin/libtextserver_fallback.windows.template_release.arm64.dll"
+
+macos.debug = "bin/libtextserver_fallback.macos.template_debug.framework"
+macos.release = "bin/libtextserver_fallback.macos.template_release.framework"
diff --git a/modules/text_server_fb/register_types.cpp b/modules/text_server_fb/register_types.cpp
index fa7b87fc17..4888fa9849 100644
--- a/modules/text_server_fb/register_types.cpp
+++ b/modules/text_server_fb/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -62,7 +62,7 @@ using namespace godot;
extern "C" {
-GDNativeBool GDN_EXPORT textserver_fallback_init(const GDNativeInterface *p_interface, const GDNativeExtensionClassLibraryPtr p_library, GDNativeInitialization *r_initialization) {
+GDExtensionBool GDN_EXPORT textserver_fallback_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
init_obj.register_initializer(&initialize_text_server_fb_module);
diff --git a/modules/text_server_fb/register_types.h b/modules/text_server_fb/register_types.h
index 229aec2266..97bc06a8f7 100644
--- a/modules/text_server_fb/register_types.h
+++ b/modules/text_server_fb/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TEXT_SERVER_FB_REGISTER_TYPES_H
#define 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 50ea4677b1..b5d7d3a3cf 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1,53 +1,58 @@
-/*************************************************************************/
-/* text_server_fb.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* text_server_fb.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "text_server_fb.h"
#ifdef GDEXTENSION
// Headers for building as GDExtension plug-in.
-#include <godot_cpp/classes/file.hpp>
+#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/os.hpp>
+#include <godot_cpp/classes/project_settings.hpp>
#include <godot_cpp/classes/rendering_server.hpp>
#include <godot_cpp/classes/translation_server.hpp>
#include <godot_cpp/core/error_macros.hpp>
using namespace godot;
+#define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var)
+
#else
// Headers for building as built-in module.
+#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
-#include "core/string/ucaps.h"
+#include "core/string/translation.h"
-#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
#endif
@@ -60,11 +65,15 @@ using namespace godot;
#include "msdfgen.h"
#endif
+#ifdef MODULE_SVG_ENABLED
+#include "thorvg_svg_in_ot.h"
+#endif
+
/*************************************************************************/
#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xff) << 24) | (((uint32_t)(c2)&0xff) << 16) | (((uint32_t)(c3)&0xff) << 8) | ((uint32_t)(c4)&0xff)))
-bool TextServerFallback::has_feature(Feature p_feature) const {
+bool TextServerFallback::_has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SIMPLE_LAYOUT:
case FEATURE_FONT_BITMAP:
@@ -81,7 +90,7 @@ bool TextServerFallback::has_feature(Feature p_feature) const {
return false;
}
-String TextServerFallback::get_name() const {
+String TextServerFallback::_get_name() const {
#ifdef GDEXTENSION
return "Fallback (GDExtension)";
#else
@@ -89,7 +98,7 @@ String TextServerFallback::get_name() const {
#endif
}
-int64_t TextServerFallback::get_features() const {
+int64_t TextServerFallback::_get_features() const {
int64_t interface_features = FEATURE_SIMPLE_LAYOUT | FEATURE_FONT_BITMAP;
#ifdef MODULE_FREETYPE_ENABLED
interface_features |= FEATURE_FONT_DYNAMIC;
@@ -101,33 +110,47 @@ int64_t TextServerFallback::get_features() const {
return interface_features;
}
-void TextServerFallback::free_rid(const RID &p_rid) {
+void TextServerFallback::_free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
FontFallback *fd = font_owner.get_or_null(p_rid);
- font_owner.free(p_rid);
+ {
+ MutexLock lock(fd->mutex);
+ font_owner.free(p_rid);
+ }
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);
- shaped_owner.free(p_rid);
+ {
+ MutexLock lock(sd->mutex);
+ shaped_owner.free(p_rid);
+ }
memdelete(sd);
}
}
-bool TextServerFallback::has(const RID &p_rid) {
+bool TextServerFallback::_has(const RID &p_rid) {
_THREAD_SAFE_METHOD_
return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
}
-bool TextServerFallback::load_support_data(const String &p_filename) {
+String TextServerFallback::_get_support_data_filename() const {
+ return "";
+};
+
+String TextServerFallback::_get_support_data_info() const {
+ return "Not supported";
+};
+
+bool TextServerFallback::_load_support_data(const String &p_filename) {
return false; // No extra data used.
}
-bool TextServerFallback::save_support_data(const String &p_filename) const {
+bool TextServerFallback::_save_support_data(const String &p_filename) const {
return false; // No extra data used.
}
-bool TextServerFallback::is_locale_right_to_left(const String &p_locale) const {
+bool TextServerFallback::_is_locale_right_to_left(const String &p_locale) const {
return false; // No RTL support.
}
@@ -167,7 +190,7 @@ _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
return OT_TAG(tag[0], tag[1], tag[2], tag[3]);
}
-int64_t TextServerFallback::name_to_tag(const String &p_name) const {
+int64_t TextServerFallback::_name_to_tag(const String &p_name) const {
if (feature_sets.has(p_name)) {
return feature_sets[p_name];
}
@@ -183,7 +206,7 @@ _FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {
p_buf[3] = (char)(uint8_t)(p_tag >> 0);
}
-String TextServerFallback::tag_to_name(int64_t p_tag) const {
+String TextServerFallback::_tag_to_name(int64_t p_tag) const {
if (feature_sets_inv.has(p_tag)) {
return feature_sets_inv[p_tag];
}
@@ -201,61 +224,30 @@ String TextServerFallback::tag_to_name(int64_t p_tag) 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;
int mw = p_width;
int mh = p_height;
- for (int i = 0; i < p_data->textures.size(); i++) {
- const FontTexture &ct = p_data->textures[i];
-
- if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture.
+ ShelfPackTexture *ct = p_data->textures.ptrw();
+ for (int32_t i = 0; i < p_data->textures.size(); i++) {
+ if (p_image_format != ct[i].format) {
continue;
}
-
- if (ct.offsets.size() < ct.texture_w) {
+ if (mw > ct[i].texture_w || mh > ct[i].texture_h) { // Too big for this texture.
continue;
}
- ret.y = 0x7fffffff;
- ret.x = 0;
-
- for (int j = 0; j < ct.texture_w - mw; j++) {
- int max_y = 0;
-
- for (int k = j; k < j + mw; k++) {
- int y = ct.offsets[k];
- if (y > max_y) {
- max_y = y;
- }
- }
-
- if (max_y < ret.y) {
- ret.y = max_y;
- ret.x = j;
- }
+ ret = ct[i].pack_rect(i, mh, mw);
+ if (ret.index != -1) {
+ break;
}
-
- if (ret.y == 0x7fffffff || ret.y + mh > ct.texture_h) {
- continue; // Fail, could not fit it here.
- }
-
- ret.index = i;
- break;
}
if (ret.index == -1) {
// Could not find texture to fit, create one.
- ret.x = 0;
- ret.y = 0;
-
int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(texsize);
-#else
texsize = next_power_of_2(texsize);
-#endif
if (p_msdf) {
texsize = MIN(texsize, 2048);
@@ -263,26 +255,15 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
texsize = MIN(texsize, 1024);
}
if (mw > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mw);
-#else
texsize = next_power_of_2(mw);
-#endif
}
if (mh > texsize) { // Special case, adapt to it?
-#ifdef GDEXTENSION
- texsize = Math::next_power_of_2(mh);
-#else
texsize = next_power_of_2(mh);
-#endif
}
- FontTexture tex;
- tex.texture_w = texsize;
- tex.texture_h = texsize;
+ ShelfPackTexture tex = ShelfPackTexture(texsize, texsize);
tex.format = p_image_format;
tex.imgdata.resize(texsize * texsize * p_color_size);
-
{
// Zero texture.
uint8_t *w = tex.imgdata.ptrw();
@@ -305,14 +286,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
ERR_FAIL_V(ret);
}
}
- tex.offsets.resize(texsize);
- int32_t *offw = tex.offsets.ptrw();
- for (int i = 0; i < texsize; i++) { // Zero offsets.
- offw[i] = 0;
- }
-
p_data->textures.push_back(tex);
- ret.index = p_data->textures.size() - 1;
+
+ int32_t idx = p_data->textures.size() - 1;
+ ret = p_data->textures.write[idx].pack_rect(idx, mh, mw);
}
return ret;
@@ -384,14 +361,14 @@ static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, con
return 0;
}
-void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+void TextServerFallback::_generateMTSDF_threaded(void *p_td, uint32_t p_y) {
MSDFThreadData *td = static_cast<MSDFThreadData *>(p_td);
msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
- int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ int row = td->shape->inverseYAxis ? td->output->height() - p_y - 1 : p_y;
for (int col = 0; col < td->output->width(); ++col) {
- int x = (y % 2) ? td->output->width() - col - 1 : col;
- msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ int x = (p_y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, p_y + .5));
msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
}
@@ -446,7 +423,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh, true);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
- FontTexture &tex = p_data->textures.write[tex_pos.index];
+ ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
edgeColoringSimple(shape, 3.0); // Max. angle.
msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
@@ -461,8 +438,8 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
td.projection = &projection;
td.distancePixelConversion = &distancePixelConversion;
- 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);
+ WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_native_group_task(&TextServerFallback::_generateMTSDF_threaded, &td, h, -1, true, String("TextServerFBRenderMSDF"));
+ WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
@@ -483,12 +460,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
tex.dirty = true;
- // Update height array.
- int32_t *offw = tex.offsets.ptrw();
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- offw[k] = tex_pos.y + mh;
- }
-
chr.texture_idx = tex_pos.index;
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2);
@@ -500,9 +471,28 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_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 {
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance, bool p_bgra) const {
int w = bitmap.width;
int h = bitmap.rows;
+ int color_size = 2;
+
+ switch (bitmap.pixel_mode) {
+ case FT_PIXEL_MODE_MONO:
+ case FT_PIXEL_MODE_GRAY: {
+ color_size = 2;
+ } break;
+ case FT_PIXEL_MODE_BGRA: {
+ color_size = 4;
+ } break;
+ case FT_PIXEL_MODE_LCD: {
+ color_size = 4;
+ w /= 3;
+ } break;
+ case FT_PIXEL_MODE_LCD_V: {
+ color_size = 4;
+ h /= 3;
+ } break;
+ }
int mw = w + p_rect_margin * 4;
int mh = h + p_rect_margin * 4;
@@ -510,15 +500,13 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
ERR_FAIL_COND_V(mw > 4096, FontGlyph());
ERR_FAIL_COND_V(mh > 4096, FontGlyph());
- int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false);
ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
// Fit character in char texture.
-
- FontTexture &tex = p_data->textures.write[tex_pos.index];
+ ShelfPackTexture &tex = p_data->textures.write[tex_pos.index];
{
uint8_t *wr = tex.imgdata.ptrw();
@@ -545,6 +533,34 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
} break;
+ case FT_PIXEL_MODE_LCD: {
+ int ofs_color = i * bitmap.pitch + (j * 3);
+ if (p_bgra) {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = 255;
+ } else {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 3] = 255;
+ }
+ } break;
+ case FT_PIXEL_MODE_LCD_V: {
+ int ofs_color = i * bitmap.pitch * 3 + j;
+ if (p_bgra) {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 3] = 255;
+ } else {
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + bitmap.pitch];
+ wr[ofs + 2] = bitmap.buffer[ofs_color + bitmap.pitch * 2];
+ wr[ofs + 3] = 255;
+ }
+ } break;
default:
ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + String::num_int64(bitmap.pixel_mode) + ".");
break;
@@ -555,12 +571,6 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
tex.dirty = true;
- // Update height array.
- int32_t *offw = tex.offsets.ptrw();
- for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
- offw[k] = tex_pos.y + mh;
- }
-
FontGlyph chr;
chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
@@ -650,9 +660,44 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data,
FT_Outline_Transform(&fd->face->glyph->outline, &mat);
}
+ FT_Render_Mode aa_mode = FT_RENDER_MODE_NORMAL;
+ bool bgra = false;
+ switch (p_font_data->antialiasing) {
+ case FONT_ANTIALIASING_NONE: {
+ aa_mode = FT_RENDER_MODE_MONO;
+ } break;
+ case FONT_ANTIALIASING_GRAY: {
+ aa_mode = FT_RENDER_MODE_NORMAL;
+ } break;
+ case FONT_ANTIALIASING_LCD: {
+ int aa_layout = (int)((p_glyph >> 24) & 7);
+ switch (aa_layout) {
+ case FONT_LCD_SUBPIXEL_LAYOUT_HRGB: {
+ aa_mode = FT_RENDER_MODE_LCD;
+ bgra = false;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_HBGR: {
+ aa_mode = FT_RENDER_MODE_LCD;
+ bgra = true;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_VRGB: {
+ aa_mode = FT_RENDER_MODE_LCD_V;
+ bgra = false;
+ } break;
+ case FONT_LCD_SUBPIXEL_LAYOUT_VBGR: {
+ aa_mode = FT_RENDER_MODE_LCD_V;
+ bgra = true;
+ } break;
+ default: {
+ aa_mode = FT_RENDER_MODE_NORMAL;
+ } break;
+ }
+ } break;
+ }
+
if (!outline) {
if (!p_font_data->msdf) {
- error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ error = FT_Render_Glyph(fd->face->glyph, aa_mode);
}
FT_GlyphSlot slot = fd->face->glyph;
if (!error) {
@@ -664,7 +709,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data,
ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
#endif
} else {
- gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra);
}
}
} else {
@@ -684,11 +729,11 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data,
if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
goto cleanup_glyph;
}
- if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ if (FT_Glyph_To_Bitmap(&glyph, aa_mode, nullptr, 1) != 0) {
goto cleanup_glyph;
}
glyph_bitmap = (FT_BitmapGlyph)glyph;
- gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2(), bgra);
cleanup_glyph:
FT_Done_Glyph(glyph);
@@ -717,7 +762,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
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)) + "'.");
+ if (error != 0) {
+ memdelete(fd);
+ ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+#ifdef MODULE_SVG_ENABLED
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+#endif
}
memset(&fd->stream, 0, sizeof(FT_StreamRec));
@@ -733,17 +784,20 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
fargs.stream = &fd->stream;
int max_index = 0;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
- if (error == 0) {
+ if (tmp_face && error == 0) {
max_index = tmp_face->num_faces - 1;
}
- FT_Done_Face(tmp_face);
+ if (tmp_face) {
+ 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;
+ memdelete(fd);
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
}
@@ -751,7 +805,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
fd->oversampling = 1.0;
fd->size.x = p_font_data->msdf_source_size;
} else if (p_font_data->oversampling <= 0.0) {
- fd->oversampling = font_get_global_oversampling();
+ fd->oversampling = _font_get_global_oversampling();
} else {
fd->oversampling = p_font_data->oversampling;
}
@@ -787,11 +841,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
if (fd->face->style_name != nullptr) {
p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
}
+ p_font_data->weight = _font_get_weight_by_name(p_font_data->style_name.to_lower());
+ p_font_data->stretch = _font_get_stretch_by_name(p_font_data->style_name.to_lower());
p_font_data->style_flags = 0;
- if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_BOLD) || p_font_data->weight >= 700) {
p_font_data->style_flags.set_flag(FONT_BOLD);
}
- if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ if ((fd->face->style_flags & FT_STYLE_FLAG_ITALIC) || _is_ital_style(p_font_data->style_name.to_lower())) {
p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
@@ -832,8 +888,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
- if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) {
- var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)];
+ if (p_font_data->variation_coordinates.has(_tag_to_name(var_tag))) {
+ var_value = p_font_data->variation_coordinates[_tag_to_name(var_tag)];
coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
}
@@ -842,6 +898,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
FT_Done_MM_Var(ft_library, amaster);
}
#else
+ memdelete(fd);
ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
}
@@ -859,7 +916,7 @@ _FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_d
p_font_data->supported_varaitions.clear();
}
-RID TextServerFallback::create_font() {
+RID TextServerFallback::_create_font() {
_THREAD_SAFE_METHOD_
FontFallback *fd = memnew(FontFallback);
@@ -867,7 +924,7 @@ RID TextServerFallback::create_font() {
return font_owner.make_rid(fd);
}
-void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
+void TextServerFallback::_font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -878,7 +935,7 @@ void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteAr
fd->data_size = fd->data.size();
}
-void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
+void TextServerFallback::_font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -889,7 +946,7 @@ 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, BitField<FontStyle> p_style) {
+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);
@@ -899,7 +956,7 @@ void TextServerFallback::font_set_style(const RID &p_font_rid, BitField<FontStyl
fd->style_flags = p_style;
}
-void TextServerFallback::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+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);
@@ -913,7 +970,7 @@ void TextServerFallback::font_set_face_index(const RID &p_font_rid, int64_t p_fa
}
}
-int64_t TextServerFallback::font_get_face_index(const RID &p_font_rid) const {
+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);
@@ -921,7 +978,7 @@ int64_t TextServerFallback::font_get_face_index(const RID &p_font_rid) const {
return fd->face_index;
}
-int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
+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);
@@ -935,6 +992,9 @@ int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
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)) + "'.");
+#ifdef MODULE_SVG_ENABLED
+ FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
+#endif
}
FT_StreamRec stream;
@@ -950,19 +1010,19 @@ int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &stream;
- FT_Face tmp_face;
+ FT_Face tmp_face = nullptr;
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
if (error == 0) {
face_count = tmp_face->num_faces;
+ FT_Done_Face(tmp_face);
}
- FT_Done_Face(tmp_face);
#endif
}
return face_count;
}
-BitField<TextServer::FontStyle> TextServerFallback::font_get_style(const RID &p_font_rid) const {
+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);
@@ -972,7 +1032,7 @@ BitField<TextServer::FontStyle> TextServerFallback::font_get_style(const RID &p_
return fd->style_flags;
}
-void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String &p_name) {
+void TextServerFallback::_font_set_style_name(const RID &p_font_rid, const String &p_name) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -982,7 +1042,7 @@ void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String
fd->style_name = p_name;
}
-String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
+String TextServerFallback::_font_get_style_name(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -992,7 +1052,47 @@ String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
return fd->style_name;
}
-void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_name) {
+void TextServerFallback::_font_set_weight(const RID &p_font_rid, int64_t p_weight) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->weight = CLAMP(p_weight, 100, 999);
+}
+
+int64_t TextServerFallback::_font_get_weight(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 400);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 400);
+ return fd->weight;
+}
+
+void TextServerFallback::_font_set_stretch(const RID &p_font_rid, int64_t p_stretch) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->stretch = CLAMP(p_stretch, 50, 200);
+}
+
+int64_t TextServerFallback::_font_get_stretch(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 100);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 100);
+ return fd->stretch;
+}
+
+void TextServerFallback::_font_set_name(const RID &p_font_rid, const String &p_name) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1002,7 +1102,7 @@ void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_na
fd->font_name = p_name;
}
-String TextServerFallback::font_get_name(const RID &p_font_rid) const {
+String TextServerFallback::_font_get_name(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -1012,26 +1112,26 @@ String TextServerFallback::font_get_name(const RID &p_font_rid) const {
return fd->font_name;
}
-void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
+void TextServerFallback::_font_set_antialiasing(const RID &p_font_rid, TextServer::FontAntialiasing p_antialiasing) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- if (fd->antialiased != p_antialiased) {
+ if (fd->antialiasing != p_antialiasing) {
_font_clear_cache(fd);
- fd->antialiased = p_antialiased;
+ fd->antialiasing = p_antialiasing;
}
}
-bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const {
+TextServer::FontAntialiasing TextServerFallback::_font_get_antialiasing(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, false);
+ ERR_FAIL_COND_V(!fd, TextServer::FONT_ANTIALIASING_NONE);
MutexLock lock(fd->mutex);
- return fd->antialiased;
+ return fd->antialiasing;
}
-void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
+void TextServerFallback::_font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1040,13 +1140,14 @@ void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p
for (KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
+ E.value->textures.write[i].texture = Ref<ImageTexture>();
}
}
fd->mipmaps = p_generate_mipmaps;
}
}
-bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const {
+bool TextServerFallback::_font_get_generate_mipmaps(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1054,7 +1155,7 @@ bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const
return fd->mipmaps;
}
-void TextServerFallback::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
+void TextServerFallback::_font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1065,7 +1166,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 {
+bool TextServerFallback::_font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1073,7 +1174,7 @@ bool TextServerFallback::font_is_multichannel_signed_distance_field(const RID &p
return fd->msdf;
}
-void TextServerFallback::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
+void TextServerFallback::_font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1084,7 +1185,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 {
+int64_t TextServerFallback::_font_get_msdf_pixel_range(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1092,7 +1193,7 @@ int64_t TextServerFallback::font_get_msdf_pixel_range(const RID &p_font_rid) con
return fd->msdf_range;
}
-void TextServerFallback::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
+void TextServerFallback::_font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1103,7 +1204,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 {
+int64_t TextServerFallback::_font_get_msdf_size(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1111,7 +1212,7 @@ int64_t TextServerFallback::font_get_msdf_size(const RID &p_font_rid) const {
return fd->msdf_source_size;
}
-void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
+void TextServerFallback::_font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1119,7 +1220,7 @@ void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
fd->fixed_size = p_fixed_size;
}
-int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
+int64_t TextServerFallback::_font_get_fixed_size(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1127,7 +1228,23 @@ int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
return fd->fixed_size;
}
-void TextServerFallback::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
+void TextServerFallback::_font_set_allow_system_fallback(const RID &p_font_rid, bool p_allow_system_fallback) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->allow_system_fallback = p_allow_system_fallback;
+}
+
+bool TextServerFallback::_font_is_allow_system_fallback(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->allow_system_fallback;
+}
+
+void TextServerFallback::_font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1138,7 +1255,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 {
+bool TextServerFallback::_font_is_force_autohinter(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -1146,7 +1263,7 @@ bool TextServerFallback::font_is_force_autohinter(const RID &p_font_rid) const {
return fd->force_autohinter;
}
-void TextServerFallback::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
+void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1157,7 +1274,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 {
+TextServer::Hinting TextServerFallback::_font_get_hinting(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
@@ -1165,7 +1282,7 @@ TextServer::Hinting TextServerFallback::font_get_hinting(const RID &p_font_rid)
return fd->hinting;
}
-void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
+void TextServerFallback::_font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1173,7 +1290,7 @@ void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, Te
fd->subpixel_positioning = p_subpixel;
}
-TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positioning(const RID &p_font_rid) const {
+TextServer::SubpixelPositioning TextServerFallback::_font_get_subpixel_positioning(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
@@ -1181,7 +1298,7 @@ TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positionin
return fd->subpixel_positioning;
}
-void TextServerFallback::font_set_embolden(const RID &p_font_rid, double p_strength) {
+void TextServerFallback::_font_set_embolden(const RID &p_font_rid, double p_strength) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1192,7 +1309,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 {
+double TextServerFallback::_font_get_embolden(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1200,7 +1317,7 @@ double TextServerFallback::font_get_embolden(const RID &p_font_rid) const {
return fd->embolden;
}
-void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
+void TextServerFallback::_font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1211,7 +1328,7 @@ void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transfo
}
}
-Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const {
+Transform2D TextServerFallback::_font_get_transform(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
@@ -1219,7 +1336,7 @@ Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const
return fd->transform;
}
-void TextServerFallback::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
+void TextServerFallback::_font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1230,7 +1347,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 {
+Dictionary TextServerFallback::_font_get_variation_coordinates(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -1238,7 +1355,7 @@ Dictionary TextServerFallback::font_get_variation_coordinates(const RID &p_font_
return fd->variation_coordinates;
}
-void TextServerFallback::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
+void TextServerFallback::_font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1249,7 +1366,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 {
+double TextServerFallback::_font_get_oversampling(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1257,19 +1374,19 @@ double TextServerFallback::font_get_oversampling(const RID &p_font_rid) const {
return fd->oversampling;
}
-Array TextServerFallback::font_get_size_cache_list(const RID &p_font_rid) const {
+TypedArray<Vector2i> TextServerFallback::_font_get_size_cache_list(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>());
MutexLock lock(fd->mutex);
- Array ret;
+ TypedArray<Vector2i> ret;
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) {
+void TextServerFallback::_font_clear_size_cache(const RID &p_font_rid) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1280,7 +1397,7 @@ void TextServerFallback::font_clear_size_cache(const RID &p_font_rid) {
fd->cache.clear();
}
-void TextServerFallback::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerFallback::_font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1291,7 +1408,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) {
+void TextServerFallback::_font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1302,7 +1419,7 @@ void TextServerFallback::font_set_ascent(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->ascent = p_ascent;
}
-double TextServerFallback::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
+double TextServerFallback::_font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1318,7 +1435,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) {
+void TextServerFallback::_font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1328,7 +1445,7 @@ void TextServerFallback::font_set_descent(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->descent = p_descent;
}
-double TextServerFallback::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
+double TextServerFallback::_font_get_descent(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1344,7 +1461,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) {
+void TextServerFallback::_font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1355,7 +1472,7 @@ void TextServerFallback::font_set_underline_position(const RID &p_font_rid, int6
fd->cache[size]->underline_position = p_underline_position;
}
-double TextServerFallback::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
+double TextServerFallback::_font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1371,7 +1488,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) {
+void TextServerFallback::_font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1382,7 +1499,7 @@ void TextServerFallback::font_set_underline_thickness(const RID &p_font_rid, int
fd->cache[size]->underline_thickness = p_underline_thickness;
}
-double TextServerFallback::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
+double TextServerFallback::_font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1398,7 +1515,7 @@ 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) {
+void TextServerFallback::_font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1414,7 +1531,7 @@ void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, d
fd->cache[size]->scale = p_scale;
}
-double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
+double TextServerFallback::_font_get_scale(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
@@ -1430,7 +1547,7 @@ double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
+int64_t TextServerFallback::_font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
@@ -1442,7 +1559,7 @@ int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const
return fd->cache[size]->textures.size();
}
-void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerFallback::_font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1452,7 +1569,7 @@ void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector
fd->cache[size]->textures.clear();
}
-void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
+void TextServerFallback::_font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1464,7 +1581,7 @@ void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector
fd->cache[size]->textures.remove_at(p_texture_index);
}
-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) {
+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) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -1477,16 +1594,14 @@ void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vec
fd->cache[size]->textures.resize(p_texture_index + 1);
}
- FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
tex.imgdata = p_image->get_data();
tex.texture_w = p_image->get_width();
tex.texture_h = p_image->get_height();
tex.format = p_image->get_format();
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -1495,7 +1610,7 @@ void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vec
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 {
+Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
@@ -1504,15 +1619,12 @@ Ref<Image> TextServerFallback::font_get_texture_image(const RID &p_font_rid, con
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
- const FontTexture &tex = fd->cache[size]->textures[p_texture_index];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
-
- return img;
+ const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
+ return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
}
-void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
+void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offsets) {
+ ERR_FAIL_COND(p_offsets.size() % 4 != 0);
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1524,11 +1636,14 @@ void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const V
fd->cache[size]->textures.resize(p_texture_index + 1);
}
- FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
- tex.offsets = p_offset;
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.shelves.clear();
+ for (int32_t i = 0; i < p_offsets.size(); i += 4) {
+ tex.shelves.push_back(Shelf(p_offsets[i], p_offsets[i + 1], p_offsets[i + 2], p_offsets[i + 3]));
+ }
}
-PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
+PackedInt32Array TextServerFallback::_font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
@@ -1537,19 +1652,31 @@ PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
- const FontTexture &tex = fd->cache[size]->textures[p_texture_index];
- return tex.offsets;
+ const ShelfPackTexture &tex = fd->cache[size]->textures[p_texture_index];
+ PackedInt32Array ret;
+ ret.resize(tex.shelves.size() * 4);
+
+ int32_t *wr = ret.ptrw();
+ int32_t i = 0;
+ for (const Shelf &E : tex.shelves) {
+ wr[i * 4] = E.x;
+ wr[i * 4 + 1] = E.y;
+ wr[i * 4 + 2] = E.w;
+ wr[i * 4 + 3] = E.h;
+ i++;
+ }
+ return ret;
}
-Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
+PackedInt32Array TextServerFallback::_font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
- Array ret;
+ PackedInt32Array ret;
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
for (const KeyValue<int32_t, FontGlyph> &E : gl) {
ret.push_back(E.key);
@@ -1557,7 +1684,7 @@ Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vecto
return ret;
}
-void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
+void TextServerFallback::_font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1568,7 +1695,7 @@ void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i
fd->cache[size]->glyph_map.clear();
}
-void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
+void TextServerFallback::_font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1579,7 +1706,7 @@ void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i
fd->cache[size]->glyph_map.erase(p_glyph);
}
-Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
+Vector2 TextServerFallback::_font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -1587,7 +1714,16 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_
Vector2i size = _get_size(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
@@ -1598,16 +1734,17 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_
ea.x = fd->embolden * double(size.x) / 64.0;
}
+ double scale = _font_get_scale(p_font_rid, p_size);
if (fd->msdf) {
- return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- return (gl[p_glyph].advance + ea).round();
+ return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size;
+ } else if ((scale == 1.0) && ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE))) {
+ return (gl[p_glyph | mod].advance + ea).round();
} else {
- return gl[p_glyph].advance + ea;
+ return gl[p_glyph | mod].advance + ea;
}
}
-void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
+void TextServerFallback::_font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1622,7 +1759,7 @@ void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p
gl[p_glyph].found = true;
}
-Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Vector2 TextServerFallback::_font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -1630,20 +1767,29 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
+ return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size;
} else {
- return gl[p_glyph].rect.position;
+ return gl[p_glyph | mod].rect.position;
}
}
-void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
+void TextServerFallback::_font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1658,7 +1804,7 @@ void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vect
gl[p_glyph].found = true;
}
-Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Vector2 TextServerFallback::_font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -1666,20 +1812,29 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Vector2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
+ return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size;
} else {
- return gl[p_glyph].rect.size;
+ return gl[p_glyph | mod].rect.size;
}
}
-void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
+void TextServerFallback::_font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1694,7 +1849,7 @@ void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector
gl[p_glyph].found = true;
}
-Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Rect2 TextServerFallback::_font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
@@ -1702,15 +1857,24 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Rect2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- return gl[p_glyph].uv_rect;
+ return gl[p_glyph | mod].uv_rect;
}
-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) {
+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) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1725,7 +1889,7 @@ void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
gl[p_glyph].found = true;
}
-int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+int64_t TextServerFallback::_font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
@@ -1733,15 +1897,24 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1);
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return -1; // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- return gl[p_glyph].texture_idx;
+ return gl[p_glyph | mod].texture_idx;
}
-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) {
+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) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1756,7 +1929,7 @@ void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const
gl[p_glyph].found = true;
}
-RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
@@ -1764,20 +1937,27 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), RID());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return RID(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID());
+ ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID());
if (RenderingServer::get_singleton() != nullptr) {
- if (gl[p_glyph].texture_idx != -1) {
- if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ if (gl[p_glyph | mod].texture_idx != -1) {
+ if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -1788,14 +1968,14 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
tex.dirty = false;
}
- return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid();
+ return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid();
}
}
return RID();
}
-Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
+Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
@@ -1803,20 +1983,27 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Size2());
- if (!_ensure_glyph(fd, size, p_glyph)) {
+
+ int mod = 0;
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ mod = (layout << 24);
+ }
+ }
+
+ if (!_ensure_glyph(fd, size, p_glyph | mod)) {
return Size2(); // Invalid or non graphicl glyph, do not display errors.
}
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
- ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2());
+ ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2());
if (RenderingServer::get_singleton() != nullptr) {
- if (gl[p_glyph].texture_idx != -1) {
- if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ if (gl[p_glyph | mod].texture_idx != -1) {
+ if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) {
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -1827,14 +2014,14 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
}
tex.dirty = false;
}
- return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size();
+ return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size();
}
}
return Size2();
}
-Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
+Dictionary TextServerFallback::_font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -1884,23 +2071,23 @@ Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, in
#endif
}
-Array TextServerFallback::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
+TypedArray<Vector2i> TextServerFallback::_font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, Array());
+ ERR_FAIL_COND_V(!fd, TypedArray<Vector2i>());
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), TypedArray<Vector2i>());
- Array ret;
+ TypedArray<Vector2i> ret;
for (const KeyValue<Vector2i, Vector2> &E : fd->cache[size]->kerning_map) {
ret.push_back(E.key);
}
return ret;
}
-void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
+void TextServerFallback::_font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1911,7 +2098,7 @@ void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p
fd->cache[size]->kerning_map.clear();
}
-void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
+void TextServerFallback::_font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1922,7 +2109,7 @@ void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_si
fd->cache[size]->kerning_map.erase(p_glyph_pair);
}
-void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
+void TextServerFallback::_font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -1933,7 +2120,7 @@ void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size,
fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
}
-Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
+Vector2 TextServerFallback::_font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
@@ -1968,15 +2155,17 @@ Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_si
return Vector2();
}
-int64_t TextServerFallback::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
+int64_t TextServerFallback::_font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
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) + ".");
return (int64_t)p_char;
}
-bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) const {
+bool TextServerFallback::_font_has_char(const RID &p_font_rid, int64_t p_char) const {
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) + ".");
+ if (!fd) {
+ return false;
+ }
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
@@ -1992,7 +2181,7 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
}
-String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const {
+String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
@@ -2025,7 +2214,7 @@ String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const
return chars;
}
-void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
+void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
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) + ".");
@@ -2041,16 +2230,18 @@ void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i
if (fd->msdf) {
_ensure_glyph(fd, size, (int32_t)idx);
} else {
- if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- } else {
- _ensure_glyph(fd, size, (int32_t)idx);
+ for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {
+ if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24));
+ } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ } else {
+ _ensure_glyph(fd, size, (int32_t)idx | (aa << 24));
+ }
}
}
}
@@ -2058,7 +2249,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) {
+void TextServerFallback::_font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2071,23 +2262,25 @@ void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i
if (fd->msdf) {
_ensure_glyph(fd, size, (int32_t)idx);
} else {
- if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (2 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (3 << 27));
- } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- _ensure_glyph(fd, size, (int32_t)idx | (1 << 27));
- _ensure_glyph(fd, size, (int32_t)idx | (0 << 27));
- } else {
- _ensure_glyph(fd, size, (int32_t)idx);
+ for (int aa = 0; aa < ((fd->antialiasing == FONT_ANTIALIASING_LCD) ? FONT_LCD_SUBPIXEL_LAYOUT_MAX : 1); aa++) {
+ if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (2 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (3 << 27) | (aa << 24));
+ } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
+ _ensure_glyph(fd, size, (int32_t)idx | (1 << 27) | (aa << 24));
+ _ensure_glyph(fd, size, (int32_t)idx | (0 << 27) | (aa << 24));
+ } else {
+ _ensure_glyph(fd, size, (int32_t)idx | (aa << 24));
+ }
}
}
}
#endif
}
-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 {
+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 {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2096,9 +2289,19 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
int32_t index = p_index & 0xffffff; // Remove subpixel shifts.
+ bool lcd_aa = false;
#ifdef MODULE_FREETYPE_ENABLED
if (!fd->msdf && fd->cache[size]->face) {
+ // LCD layout, bits 24, 25, 26
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ lcd_aa = true;
+ index = index | (layout << 24);
+ }
+ }
+ // Subpixel X-shift, bits 27, 28
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
index = index | (xshift << 27);
@@ -2120,16 +2323,14 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
if (gl.texture_idx != -1) {
Color modulate = p_color;
#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
modulate.r = modulate.g = modulate.b = 1.0;
}
#endif
if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -2145,27 +2346,33 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
Point2 cpos = p_pos;
cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size;
Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size;
- RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);
} else {
Point2 cpos = p_pos;
- cpos.y = Math::floor(cpos.y);
+ double scale = _font_get_scale(p_font_rid, p_size);
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.125));
+ cpos.x = cpos.x + 0.125;
} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.25));
- } else {
+ cpos.x = cpos.x + 0.25;
+ }
+ if (scale == 1.0) {
+ cpos.y = Math::floor(cpos.y);
cpos.x = Math::floor(cpos.x);
}
cpos += gl.rect.position;
Size2 csize = gl.rect.size;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ if (lcd_aa) {
+ RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ }
}
}
}
}
}
-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 {
+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 {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2174,9 +2381,19 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
int32_t index = p_index & 0xffffff; // Remove subpixel shifts.
+ bool lcd_aa = false;
#ifdef MODULE_FREETYPE_ENABLED
if (!fd->msdf && fd->cache[size]->face) {
+ // LCD layout, bits 24, 25, 26
+ if (fd->antialiasing == FONT_ANTIALIASING_LCD) {
+ TextServer::FontLCDSubpixelLayout layout = (TextServer::FontLCDSubpixelLayout)(int)GLOBAL_GET("gui/theme/lcd_subpixel_layout");
+ if (layout != FONT_LCD_SUBPIXEL_LAYOUT_NONE) {
+ lcd_aa = true;
+ index = index | (layout << 24);
+ }
+ }
+ // Subpixel X-shift, bits 27, 28
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
int xshift = (int)(Math::floor(4 * (p_pos.x + 0.125)) - 4 * Math::floor(p_pos.x + 0.125));
index = index | (xshift << 27);
@@ -2198,16 +2415,14 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
if (gl.texture_idx != -1) {
Color modulate = p_color;
#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ if (fd->cache[size]->face && (fd->cache[size]->textures[gl.texture_idx].format == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
modulate.r = modulate.g = modulate.b = 1.0;
}
#endif
if (RenderingServer::get_singleton() != nullptr) {
if (fd->cache[size]->textures[gl.texture_idx].dirty) {
- FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
- Ref<Image> img;
- img.instantiate();
- img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
+ ShelfPackTexture &tex = fd->cache[size]->textures.write[gl.texture_idx];
+ Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata);
if (fd->mipmaps) {
img->generate_mipmaps();
}
@@ -2223,27 +2438,33 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
Point2 cpos = p_pos;
cpos += gl.rect.position * (double)p_size / (double)fd->msdf_source_size;
Size2 csize = gl.rect.size * (double)p_size / (double)fd->msdf_source_size;
- RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
+ RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size, fd->msdf_range, (double)p_size / (double)fd->msdf_source_size);
} else {
Point2 cpos = p_pos;
- cpos.y = Math::floor(cpos.y);
+ double scale = _font_get_scale(p_font_rid, p_size);
if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.125));
+ cpos.x = cpos.x + 0.125;
} else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) {
- cpos.x = ((int)Math::floor(cpos.x + 0.25));
- } else {
+ cpos.x = cpos.x + 0.25;
+ }
+ if (scale == 1.0) {
+ cpos.y = Math::floor(cpos.y);
cpos.x = Math::floor(cpos.x);
}
cpos += gl.rect.position;
Size2 csize = gl.rect.size;
- RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ if (lcd_aa) {
+ RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
+ }
}
}
}
}
}
-bool TextServerFallback::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
+bool TextServerFallback::_font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2255,7 +2476,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) {
+void TextServerFallback::_font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2263,7 +2484,7 @@ void TextServerFallback::font_set_language_support_override(const RID &p_font_ri
fd->language_support_overrides[p_language] = p_supported;
}
-bool TextServerFallback::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
+bool TextServerFallback::_font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2271,7 +2492,7 @@ bool TextServerFallback::font_get_language_support_override(const RID &p_font_ri
return fd->language_support_overrides[p_language];
}
-void TextServerFallback::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
+void TextServerFallback::_font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2279,7 +2500,7 @@ void TextServerFallback::font_remove_language_support_override(const RID &p_font
fd->language_support_overrides.erase(p_language);
}
-PackedStringArray TextServerFallback::font_get_language_support_overrides(const RID &p_font_rid) {
+PackedStringArray TextServerFallback::_font_get_language_support_overrides(const RID &p_font_rid) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
@@ -2291,7 +2512,7 @@ PackedStringArray TextServerFallback::font_get_language_support_overrides(const
return out;
}
-bool TextServerFallback::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
+bool TextServerFallback::_font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2303,7 +2524,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) {
+void TextServerFallback::_font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2311,7 +2532,7 @@ void TextServerFallback::font_set_script_support_override(const RID &p_font_rid,
fd->script_support_overrides[p_script] = p_supported;
}
-bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
+bool TextServerFallback::_font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
@@ -2319,7 +2540,7 @@ bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid,
return fd->script_support_overrides[p_script];
}
-void TextServerFallback::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
+void TextServerFallback::_font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2329,7 +2550,7 @@ void TextServerFallback::font_remove_script_support_override(const RID &p_font_r
fd->script_support_overrides.erase(p_script);
}
-PackedStringArray TextServerFallback::font_get_script_support_overrides(const RID &p_font_rid) {
+PackedStringArray TextServerFallback::_font_get_script_support_overrides(const RID &p_font_rid) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
@@ -2341,7 +2562,7 @@ PackedStringArray TextServerFallback::font_get_script_support_overrides(const RI
return out;
}
-void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
+void TextServerFallback::_font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2351,7 +2572,7 @@ void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_r
fd->feature_overrides = p_overrides;
}
-Dictionary TextServerFallback::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
+Dictionary TextServerFallback::_font_get_opentype_feature_overrides(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -2359,11 +2580,11 @@ Dictionary TextServerFallback::font_get_opentype_feature_overrides(const RID &p_
return fd->feature_overrides;
}
-Dictionary TextServerFallback::font_supported_feature_list(const RID &p_font_rid) const {
+Dictionary TextServerFallback::_font_supported_feature_list(const RID &p_font_rid) const {
return Dictionary();
}
-Dictionary TextServerFallback::font_supported_variation_list(const RID &p_font_rid) const {
+Dictionary TextServerFallback::_font_supported_variation_list(const RID &p_font_rid) const {
FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@@ -2373,11 +2594,11 @@ Dictionary TextServerFallback::font_supported_variation_list(const RID &p_font_r
return fd->supported_varaitions;
}
-double TextServerFallback::font_get_global_oversampling() const {
+double TextServerFallback::_font_get_global_oversampling() const {
return oversampling;
}
-void TextServerFallback::font_set_global_oversampling(double p_oversampling) {
+void TextServerFallback::_font_set_global_oversampling(double p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@@ -2385,8 +2606,8 @@ void TextServerFallback::font_set_global_oversampling(double p_oversampling) {
font_owner.get_owned_list(&fonts);
bool font_cleared = false;
for (const RID &E : fonts) {
- if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
- font_clear_size_cache(E);
+ if (!_font_is_multichannel_signed_distance_field(E) && _font_get_oversampling(E) <= 0) {
+ _font_clear_size_cache(E);
font_cleared = true;
}
}
@@ -2441,8 +2662,9 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
p_shaped->parent = RID();
}
-RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
@@ -2451,7 +2673,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
return shaped_owner.make_rid(sd);
}
-void TextServerFallback::shaped_text_clear(const RID &p_shaped) {
+void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -2465,21 +2687,22 @@ void TextServerFallback::shaped_text_clear(const RID &p_shaped) {
invalidate(sd);
}
-void TextServerFallback::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
+void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
+ ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
if (p_direction == DIRECTION_RTL) {
ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
}
}
-TextServer::Direction TextServerFallback::shaped_text_get_direction(const RID &p_shaped) const {
+TextServer::Direction TextServerFallback::_shaped_text_get_direction(const RID &p_shaped) const {
return TextServer::DIRECTION_LTR;
}
-TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(const RID &p_shaped) const {
+TextServer::Direction TextServerFallback::_shaped_text_get_inferred_direction(const RID &p_shaped) const {
return TextServer::DIRECTION_LTR;
}
-void TextServerFallback::shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {
+void TextServerFallback::_shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) {
_THREAD_SAFE_METHOD_
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -2493,14 +2716,14 @@ void TextServerFallback::shaped_text_set_custom_punctuation(const RID &p_shaped,
}
}
-String TextServerFallback::shaped_text_get_custom_punctuation(const RID &p_shaped) const {
+String TextServerFallback::_shaped_text_get_custom_punctuation(const RID &p_shaped) const {
_THREAD_SAFE_METHOD_
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, String());
return sd->custom_punct;
}
-void TextServerFallback::shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {
+void TextServerFallback::_shaped_text_set_orientation(const RID &p_shaped, TextServer::Orientation p_orientation) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -2514,11 +2737,11 @@ void TextServerFallback::shaped_text_set_orientation(const RID &p_shaped, TextSe
}
}
-void TextServerFallback::shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {
+void TextServerFallback::_shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) {
// No BiDi support, ignore.
}
-TextServer::Orientation TextServerFallback::shaped_text_get_orientation(const RID &p_shaped) const {
+TextServer::Orientation TextServerFallback::_shaped_text_get_orientation(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
@@ -2526,7 +2749,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(const RI
return sd->orientation;
}
-void TextServerFallback::shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {
+void TextServerFallback::_shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
MutexLock lock(sd->mutex);
@@ -2540,7 +2763,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(const RID &p_shaped, b
}
}
-bool TextServerFallback::shaped_text_get_preserve_invalid(const RID &p_shaped) const {
+bool TextServerFallback::_shaped_text_get_preserve_invalid(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -2548,7 +2771,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(const RID &p_shaped) c
return sd->preserve_invalid;
}
-void TextServerFallback::shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {
+void TextServerFallback::_shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -2562,7 +2785,7 @@ void TextServerFallback::shaped_text_set_preserve_control(const RID &p_shaped, b
}
}
-bool TextServerFallback::shaped_text_get_preserve_control(const RID &p_shaped) const {
+bool TextServerFallback::_shaped_text_get_preserve_control(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -2570,7 +2793,7 @@ 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) {
+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);
@@ -2585,7 +2808,7 @@ void TextServerFallback::shaped_text_set_spacing(const RID &p_shaped, SpacingTyp
}
}
-int64_t TextServerFallback::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+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);
@@ -2595,20 +2818,20 @@ int64_t TextServerFallback::shaped_text_get_spacing(const RID &p_shaped, Spacing
return sd->extra_spacing[p_spacing];
}
-int64_t TextServerFallback::shaped_get_span_count(const RID &p_shaped) const {
+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);
return sd->spans.size();
}
-Variant TextServerFallback::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
+Variant TextServerFallback::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Variant());
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
return sd->spans[p_index].meta;
}
-void TextServerFallback::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
+void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
ERR_FAIL_INDEX(p_index, sd->spans.size());
@@ -2619,7 +2842,7 @@ void TextServerFallback::shaped_set_span_update_font(const RID &p_shaped, int64_
Array fonts_no_match;
int font_count = p_fonts.size();
for (int i = 0; i < font_count; i++) {
- if (font_is_language_supported(p_fonts[i], span.language)) {
+ if (_font_is_language_supported(p_fonts[i], span.language)) {
span.fonts.push_back(p_fonts[i]);
} else {
fonts_no_match.push_back(p_fonts[i]);
@@ -2632,7 +2855,7 @@ void TextServerFallback::shaped_set_span_update_font(const RID &p_shaped, int64_
sd->valid = false;
}
-bool TextServerFallback::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, const String &p_language, const Variant &p_meta) {
+bool TextServerFallback::_shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -2658,8 +2881,11 @@ bool TextServerFallback::shaped_text_add_string(const RID &p_shaped, const Strin
// Pre-sort fonts, push fonts with the language support first.
Array fonts_no_match;
int font_count = p_fonts.size();
- for (int i = 0; i < font_count; i++) {
- if (font_is_language_supported(p_fonts[i], p_language)) {
+ if (font_count > 0) {
+ span.fonts.push_back(p_fonts[0]);
+ }
+ for (int i = 1; i < font_count; i++) {
+ if (_font_is_language_supported(p_fonts[i], p_language)) {
span.fonts.push_back(p_fonts[i]);
} else {
fonts_no_match.push_back(p_fonts[i]);
@@ -2680,7 +2906,7 @@ bool TextServerFallback::shaped_text_add_string(const RID &p_shaped, const Strin
return true;
}
-bool TextServerFallback::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length) {
+bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, float p_baseline) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -2701,6 +2927,7 @@ bool TextServerFallback::shaped_text_add_object(const RID &p_shaped, const Varia
obj.inline_align = p_inline_align;
obj.rect.size = p_size;
obj.pos = span.start;
+ obj.baseline = p_baseline;
sd->spans.push_back(span);
sd->text = sd->text + String::chr(0xfffc).repeat(p_length);
@@ -2711,7 +2938,7 @@ bool TextServerFallback::shaped_text_add_object(const RID &p_shaped, const Varia
return true;
}
-bool TextServerFallback::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
+bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -2719,6 +2946,7 @@ bool TextServerFallback::shaped_text_resize_object(const RID &p_shaped, const Va
ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
sd->objects[p_key].rect.size = p_size;
sd->objects[p_key].inline_align = p_inline_align;
+ sd->objects[p_key].baseline = p_baseline;
if (sd->valid) {
// Recalc string metrics.
sd->ascent = 0;
@@ -2752,14 +2980,14 @@ bool TextServerFallback::shaped_text_resize_object(const RID &p_shaped, const Va
} else {
if (gl.font_rid.is_valid()) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
- sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
} else {
- sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
- 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));
+ 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));
} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
@@ -2805,6 +3033,9 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
+ case INLINE_ALIGNMENT_BASELINE_TO: {
+ E.value.rect.position.y -= E.value.baseline;
+ } break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
@@ -2833,6 +3064,9 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
case INLINE_ALIGNMENT_CENTER_TO: {
E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
+ case INLINE_ALIGNMENT_BASELINE_TO: {
+ E.value.rect.position.x -= E.value.baseline;
+ } break;
case INLINE_ALIGNMENT_TOP_TO: {
// NOP
} break;
@@ -2846,7 +3080,7 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
p_sd->descent = full_descent;
}
-RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) 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);
@@ -2854,10 +3088,10 @@ RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start,
MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
- return shaped_text_substr(sd->parent, p_start, p_length);
+ return _shaped_text_substr(sd->parent, p_start, p_length);
}
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
@@ -2912,11 +3146,11 @@ RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start,
} else {
if (gl.font_rid.is_valid()) {
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
- new_sd->descent = MAX(new_sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ new_sd->ascent = MAX(new_sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
+ new_sd->descent = MAX(new_sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
} else {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ new_sd->ascent = MAX(new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ new_sd->descent = MAX(new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
@@ -2940,7 +3174,7 @@ RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start,
return shaped_owner.make_rid(new_sd);
}
-RID TextServerFallback::shaped_text_get_parent(const RID &p_shaped) const {
+RID TextServerFallback::_shaped_text_get_parent(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -2948,16 +3182,16 @@ 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, BitField<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);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
if (!sd->justification_ops_valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_update_justification_ops(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_update_justification_ops(p_shaped);
}
int start_pos = 0;
@@ -3057,16 +3291,16 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
return Math::ceil(justification_width);
}
-double TextServerFallback::shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {
+double TextServerFallback::_shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
if (!sd->line_breaks_valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_update_breaks(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_update_breaks(p_shaped);
}
for (int i = 0; i < p_tab_stops.size(); i++) {
@@ -3113,13 +3347,13 @@ double TextServerFallback::shaped_text_tab_align(const RID &p_shaped, const Pack
return 0.0;
}
-bool TextServerFallback::shaped_text_update_breaks(const RID &p_shaped) {
+bool TextServerFallback::_shaped_text_update_breaks(const RID &p_shaped) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped);
+ _shaped_text_shape(p_shaped);
}
if (sd->line_breaks_valid) {
@@ -3169,29 +3403,29 @@ bool TextServerFallback::shaped_text_update_breaks(const RID &p_shaped) {
return sd->line_breaks_valid;
}
-bool TextServerFallback::shaped_text_update_justification_ops(const RID &p_shaped) {
+bool TextServerFallback::_shaped_text_update_justification_ops(const RID &p_shaped) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped);
+ _shaped_text_shape(p_shaped);
}
if (!sd->line_breaks_valid) {
- shaped_text_update_breaks(p_shaped);
+ _shaped_text_update_breaks(p_shaped);
}
sd->justification_ops_valid = true; // Not supported by fallback server.
return true;
}
-void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> 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.");
MutexLock lock(sd->mutex);
if (!sd->valid) {
- shaped_text_shape(p_shaped_line);
+ _shaped_text_shape(p_shaped_line);
}
sd->text_trimmed = false;
@@ -3204,7 +3438,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_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;
@@ -3230,30 +3464,30 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!font_has_char(dot_gl_font_rid, '.')) {
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], '.')) {
dot_gl_font_rid = fonts[i];
break;
}
}
}
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!font_has_char(whitespace_gl_font_rid, '.')) {
+ if (!_font_has_char(whitespace_gl_font_rid, '.')) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (font_has_char(fonts[i], ' ')) {
+ if (_font_has_char(fonts[i], ' ')) {
whitespace_gl_font_rid = fonts[i];
break;
}
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.') : -10;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
- int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ') : -10;
- Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
+ int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.', 0) : -10;
+ Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -10;
+ Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
@@ -3269,29 +3503,34 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int last_valid_cut = 0;
bool found = false;
- for (int i = sd_size - 1; i != -1; i--) {
- width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ if (enforce_ellipsis && (width + ellipsis_width <= p_width)) {
+ trim_pos = -1;
+ ellipsis_pos = sd_size;
+ } else {
+ for (int i = sd_size - 1; i != -1; i--) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
- if (sd_glyphs[i].count > 0) {
- bool above_min_char_threshold = (i >= ell_min_characters);
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_threshold = (i >= ell_min_characters);
- if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
- if (cut_per_word && above_min_char_threshold) {
- if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+ if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
+ if (cut_per_word && above_min_char_threshold) {
+ if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
+ last_valid_cut = i;
+ found = true;
+ }
+ } else {
last_valid_cut = i;
found = true;
}
- } else {
- last_valid_cut = i;
- found = true;
- }
- if (found) {
- trim_pos = last_valid_cut;
+ if (found) {
+ trim_pos = last_valid_cut;
- if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
- ellipsis_pos = trim_pos;
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
}
- break;
}
}
}
@@ -3337,7 +3576,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
}
}
-int64_t TextServerFallback::shaped_text_get_trim_pos(const RID &p_shaped) const {
+int64_t TextServerFallback::_shaped_text_get_trim_pos(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
@@ -3345,7 +3584,7 @@ int64_t TextServerFallback::shaped_text_get_trim_pos(const RID &p_shaped) const
return sd->overrun_trim_data.trim_pos;
}
-int64_t TextServerFallback::shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
+int64_t TextServerFallback::_shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
@@ -3353,7 +3592,7 @@ int64_t TextServerFallback::shaped_text_get_ellipsis_pos(const RID &p_shaped) co
return sd->overrun_trim_data.ellipsis_pos;
}
-const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
+const Glyph *TextServerFallback::_shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataFallback invalid.");
@@ -3361,7 +3600,7 @@ const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(const RID &p_sh
return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
}
-int64_t TextServerFallback::shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
+int64_t TextServerFallback::_shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataFallback invalid.");
@@ -3369,7 +3608,7 @@ int64_t TextServerFallback::shaped_text_get_ellipsis_glyph_count(const RID &p_sh
return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
-bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
+bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3421,6 +3660,7 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
sd->glyphs.push_back(gl);
} else {
// Text span.
+ RID prev_font;
for (int j = span.start; j < span.end; j++) {
Glyph gl;
gl.start = j;
@@ -3436,27 +3676,192 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
}
// Select first font which has character (font are already sorted by span language).
for (int k = 0; k < span.fonts.size(); k++) {
- if (font_has_char(span.fonts[k], gl.index)) {
+ if (_font_has_char(span.fonts[k], gl.index)) {
gl.font_rid = span.fonts[k];
break;
}
}
+ if (!gl.font_rid.is_valid() && prev_font.is_valid()) {
+ if (_font_has_char(prev_font, gl.index)) {
+ gl.font_rid = prev_font;
+ }
+ }
+ if (!gl.font_rid.is_valid() && OS::get_singleton()->has_feature("system_fonts") && span.fonts.size() > 0) {
+ // Try system fallback.
+ RID fdef = span.fonts[0];
+ if (_font_is_allow_system_fallback(fdef)) {
+ String text = sd->text.substr(j, 1);
+ String font_name = _font_get_name(fdef);
+ BitField<FontStyle> font_style = _font_get_style(fdef);
+ int font_weight = _font_get_weight(fdef);
+ int font_stretch = _font_get_stretch(fdef);
+ Dictionary dvar = _font_get_variation_coordinates(fdef);
+ static int64_t wgth_tag = _name_to_tag("weight");
+ static int64_t wdth_tag = _name_to_tag("width");
+ static int64_t ital_tag = _name_to_tag("italic");
+ if (dvar.has(wgth_tag)) {
+ font_weight = dvar[wgth_tag].operator int();
+ }
+ if (dvar.has(wdth_tag)) {
+ font_stretch = dvar[wdth_tag].operator int();
+ }
+ if (dvar.has(ital_tag) && dvar[ital_tag].operator int() == 1) {
+ font_style.set_flag(TextServer::FONT_ITALIC);
+ }
+
+ String locale = (span.language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : span.language;
+
+ PackedStringArray fallback_font_name = OS::get_singleton()->get_system_font_path_for_text(font_name, text, locale, String(), font_weight, font_stretch, font_style & TextServer::FONT_ITALIC);
+#ifdef GDEXTENSION
+ for (int fb = 0; fb < fallback_font_name.size(); fb++) {
+ const String &E = fallback_font_name[fb];
+#else
+ for (const String &E : fallback_font_name) {
+#endif
+ SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, fdef, this);
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < sysf_cache.var.size(); face_idx++) {
+ const SystemFontCacheRec &F = sysf_cache.var[face_idx];
+ if (unlikely(!_font_has_char(F.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(F.rid);
+ int weight = _font_get_weight(F.rid);
+ int stretch = _font_get_stretch(F.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match != -1) {
+ gl.font_rid = sysf_cache.var[best_match].rid;
+ }
+ }
+ if (!gl.font_rid.is_valid()) {
+ if (system_fonts.has(key)) {
+ const SystemFontCache &sysf_cache = system_fonts[key];
+ if (sysf_cache.max_var == sysf_cache.var.size()) {
+ // All subfonts already tested, skip.
+ continue;
+ }
+ }
+
+ if (!system_font_data.has(E)) {
+ system_font_data[E] = FileAccess::get_file_as_bytes(E);
+ }
+
+ const PackedByteArray &font_data = system_font_data[E];
+
+ SystemFontCacheRec sysf;
+ sysf.rid = _create_font();
+ _font_set_data_ptr(sysf.rid, font_data.ptr(), font_data.size());
+
+ Dictionary var = dvar;
+ // Select matching style from collection.
+ int best_score = 0;
+ int best_match = -1;
+ for (int face_idx = 0; face_idx < _font_get_face_count(sysf.rid); face_idx++) {
+ _font_set_face_index(sysf.rid, face_idx);
+ if (unlikely(!_font_has_char(sysf.rid, text[0]))) {
+ continue;
+ }
+ BitField<FontStyle> style = _font_get_style(sysf.rid);
+ int weight = _font_get_weight(sysf.rid);
+ int stretch = _font_get_stretch(sysf.rid);
+ int score = (20 - Math::abs(weight - font_weight) / 50);
+ score += (20 - Math::abs(stretch - font_stretch) / 10);
+ if (bool(style & TextServer::FONT_ITALIC) == bool(font_style & TextServer::FONT_ITALIC)) {
+ score += 30;
+ }
+ if (score >= best_score) {
+ best_score = score;
+ best_match = face_idx;
+ }
+ if (best_score == 70) {
+ break;
+ }
+ }
+ if (best_match == -1) {
+ _free_rid(sysf.rid);
+ continue;
+ } else {
+ _font_set_face_index(sysf.rid, best_match);
+ }
+ sysf.index = best_match;
+
+ // If it's a variable font, apply weight, stretch and italic coordinates to match requested style.
+ if (best_score != 70) {
+ Dictionary ftr = _font_supported_variation_list(sysf.rid);
+ if (ftr.has(wdth_tag)) {
+ var[wdth_tag] = font_stretch;
+ _font_set_stretch(sysf.rid, font_stretch);
+ }
+ if (ftr.has(wgth_tag)) {
+ var[wgth_tag] = font_weight;
+ _font_set_weight(sysf.rid, font_weight);
+ }
+ if ((font_style & TextServer::FONT_ITALIC) && ftr.has(ital_tag)) {
+ var[ital_tag] = 1;
+ _font_set_style(sysf.rid, _font_get_style(sysf.rid) | TextServer::FONT_ITALIC);
+ }
+ }
+
+ _font_set_antialiasing(sysf.rid, key.antialiasing);
+ _font_set_generate_mipmaps(sysf.rid, key.mipmaps);
+ _font_set_multichannel_signed_distance_field(sysf.rid, key.msdf);
+ _font_set_msdf_pixel_range(sysf.rid, key.msdf_range);
+ _font_set_msdf_size(sysf.rid, key.msdf_source_size);
+ _font_set_fixed_size(sysf.rid, key.fixed_size);
+ _font_set_force_autohinter(sysf.rid, key.force_autohinter);
+ _font_set_hinting(sysf.rid, key.hinting);
+ _font_set_subpixel_positioning(sysf.rid, key.subpixel_positioning);
+ _font_set_variation_coordinates(sysf.rid, var);
+ _font_set_oversampling(sysf.rid, key.oversampling);
+ _font_set_embolden(sysf.rid, key.embolden);
+ _font_set_transform(sysf.rid, key.transform);
+
+ if (system_fonts.has(key)) {
+ system_fonts[key].var.push_back(sysf);
+ } else {
+ SystemFontCache &sysf_cache = system_fonts[key];
+ sysf_cache.max_var = _font_get_face_count(sysf.rid);
+ sysf_cache.var.push_back(sysf);
+ }
+ gl.font_rid = sysf.rid;
+ }
+ break;
+ }
+ }
+ }
+ prev_font = gl.font_rid;
+ double scale = _font_get_scale(gl.font_rid, gl.font_size);
if (gl.font_rid.is_valid()) {
- bool subpos = (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_AUTO && gl.font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
+ bool subpos = (scale != 1.0) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_HALF) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (_font_get_subpixel_positioning(gl.font_rid) == SUBPIXEL_POSITIONING_AUTO && gl.font_size <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x);
+ gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
gl.x_off = 0;
gl.y_off = 0;
- sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
- sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ sd->ascent = MAX(sd->ascent, _font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, _font_get_descent(gl.font_rid, gl.font_size));
} else {
- gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y);
- gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
- gl.y_off = font_get_ascent(gl.font_rid, gl.font_size);
- sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ gl.advance = _font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;
+ gl.x_off = -Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
+ gl.y_off = _font_get_ascent(gl.font_rid, gl.font_size);
+ sd->ascent = MAX(sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
}
if (j < sd->end - 1) {
@@ -3467,17 +3872,17 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
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));
+ 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));
// Add kerning to previous glyph.
if (sd->glyphs.size() > 0) {
Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];
if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;
+ prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;
} else {
- prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;
+ prev_gl.advance += _font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;
}
}
}
@@ -3508,7 +3913,7 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
return sd->valid;
}
-bool TextServerFallback::shaped_text_is_ready(const RID &p_shaped) const {
+bool TextServerFallback::_shaped_text_is_ready(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
@@ -3516,41 +3921,41 @@ bool TextServerFallback::shaped_text_is_ready(const RID &p_shaped) const {
return sd->valid;
}
-const Glyph *TextServerFallback::shaped_text_get_glyphs(const RID &p_shaped) const {
+const Glyph *TextServerFallback::_shaped_text_get_glyphs(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->glyphs.ptr();
}
-int64_t TextServerFallback::shaped_text_get_glyph_count(const RID &p_shaped) const {
+int64_t TextServerFallback::_shaped_text_get_glyph_count(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->glyphs.size();
}
-const Glyph *TextServerFallback::shaped_text_sort_logical(const RID &p_shaped) {
+const Glyph *TextServerFallback::_shaped_text_sort_logical(const RID &p_shaped) {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->glyphs.ptr(); // Already in the logical order, return as is.
}
-Vector2i TextServerFallback::shaped_text_get_range(const RID &p_shaped) const {
+Vector2i TextServerFallback::_shaped_text_get_range(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Vector2i());
@@ -3558,7 +3963,7 @@ Vector2i TextServerFallback::shaped_text_get_range(const RID &p_shaped) const {
return Vector2(sd->start, sd->end);
}
-Array TextServerFallback::shaped_text_get_objects(const RID &p_shaped) const {
+Array TextServerFallback::_shaped_text_get_objects(const RID &p_shaped) const {
Array ret;
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
@@ -3571,25 +3976,25 @@ Array TextServerFallback::shaped_text_get_objects(const RID &p_shaped) const {
return ret;
}
-Rect2 TextServerFallback::shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
+Rect2 TextServerFallback::_shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->objects[p_key].rect;
}
-Size2 TextServerFallback::shaped_text_get_size(const RID &p_shaped) const {
+Size2 TextServerFallback::_shaped_text_get_size(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
return Size2(sd->width, sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
@@ -3598,111 +4003,134 @@ Size2 TextServerFallback::shaped_text_get_size(const RID &p_shaped) const {
}
}
-double TextServerFallback::shaped_text_get_ascent(const RID &p_shaped) const {
+double TextServerFallback::_shaped_text_get_ascent(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
-double TextServerFallback::shaped_text_get_descent(const RID &p_shaped) const {
+double TextServerFallback::_shaped_text_get_descent(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
-double TextServerFallback::shaped_text_get_width(const RID &p_shaped) const {
+double TextServerFallback::_shaped_text_get_width(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return Math::ceil(sd->width);
}
-double TextServerFallback::shaped_text_get_underline_position(const RID &p_shaped) const {
+double TextServerFallback::_shaped_text_get_underline_position(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->upos;
}
-double TextServerFallback::shaped_text_get_underline_thickness(const RID &p_shaped) const {
+double TextServerFallback::_shaped_text_get_underline_thickness(const RID &p_shaped) const {
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
MutexLock lock(sd->mutex);
if (!sd->valid) {
- const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
}
return sd->uthk;
}
-String TextServerFallback::string_to_upper(const String &p_string, const String &p_language) const {
- String upper = p_string;
-
- for (int i = 0; i <= upper.length(); i++) {
- const char32_t s = upper[i];
- const char32_t t = _find_upper(s);
- if (s != t) { // avoid copy on write
- upper[i] = t;
- }
- }
-
- return upper;
+String TextServerFallback::_string_to_upper(const String &p_string, const String &p_language) const {
+ return p_string.to_upper();
}
-String TextServerFallback::string_to_lower(const String &p_string, const String &p_language) const {
- String lower = p_string;
-
- for (int i = 0; i <= lower.length(); i++) {
- const char32_t s = lower[i];
- const char32_t t = _find_lower(s);
- if (s != t) { // avoid copy on write
- lower[i] = t;
- }
- }
-
- return lower;
+String TextServerFallback::_string_to_lower(const String &p_string, const String &p_language) const {
+ return p_string.to_lower();
}
-PackedInt32Array TextServerFallback::string_get_word_breaks(const String &p_string, const String &p_language) const {
+PackedInt32Array TextServerFallback::_string_get_word_breaks(const String &p_string, const String &p_language, int p_chars_per_line) const {
PackedInt32Array ret;
+
+ int line_start = 0;
+ int line_end = 0; // End of last word on current line.
+ int word_start = 0; // -1 if no word encountered. Leading spaces are part of a word.
+ int word_length = 0;
+
for (int i = 0; i < p_string.length(); i++) {
- char32_t c = p_string[i];
- if (c == 0xfffc) {
- continue;
- }
- if (is_punct(c) && c != 0x005F) {
- ret.push_back(i);
- continue;
- }
- if (is_underscore(c)) {
- ret.push_back(i);
- continue;
- }
- if (is_whitespace(c) || is_linebreak(c)) {
+ const char32_t c = p_string[i];
+
+ if (is_linebreak(c)) {
+ // Force newline.
+ ret.push_back(line_start);
ret.push_back(i);
+ line_start = i + 1;
+ line_end = line_start;
+ word_start = line_start;
+ word_length = 0;
+ } else if (c == 0xfffc) {
continue;
+ } else if ((is_punct(c) && c != 0x005F) || is_underscore(c) || c == '\t' || is_whitespace(c)) {
+ // A whitespace ends current word.
+ if (word_length > 0) {
+ line_end = i - 1;
+ word_start = -1;
+ word_length = 0;
+ }
+ } else {
+ if (word_start == -1) {
+ word_start = i;
+ if (p_chars_per_line <= 0) {
+ ret.push_back(line_start);
+ ret.push_back(line_end + 1);
+ line_start = word_start;
+ line_end = line_start;
+ }
+ }
+ word_length += 1;
+
+ if (p_chars_per_line > 0) {
+ if (word_length > p_chars_per_line) {
+ // Word too long: wrap before current character.
+ ret.push_back(line_start);
+ ret.push_back(i);
+ line_start = i;
+ line_end = i;
+ word_start = i;
+ word_length = 1;
+ } else if (i - line_start + 1 > p_chars_per_line) {
+ // Line too long: wrap after the last word.
+ ret.push_back(line_start);
+ ret.push_back(line_end + 1);
+ line_start = word_start;
+ line_end = line_start;
+ }
+ }
}
}
+ if (line_start < p_string.length()) {
+ ret.push_back(line_start);
+ ret.push_back(p_string.length());
+ }
return ret;
}
@@ -3710,6 +4138,17 @@ TextServerFallback::TextServerFallback() {
_insert_feature_sets();
};
+void TextServerFallback::_cleanup() {
+ for (const KeyValue<SystemFontKey, SystemFontCache> &E : system_fonts) {
+ const Vector<SystemFontCacheRec> &sysf_cache = E.value.var;
+ for (const SystemFontCacheRec &F : sysf_cache) {
+ _free_rid(F.rid);
+ }
+ }
+ system_fonts.clear();
+ system_font_data.clear();
+}
+
TextServerFallback::~TextServerFallback() {
#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index adb5cbb817..9fb048a581 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* text_server_fb.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* text_server_fb.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TEXT_SERVER_FB_H
#define TEXT_SERVER_FB_H
@@ -42,6 +42,7 @@
#include <godot_cpp/godot.hpp>
#include <godot_cpp/core/class_db.hpp>
+#include <godot_cpp/core/ext_wrappers.gen.inc>
#include <godot_cpp/core/mutex_lock.hpp>
#include <godot_cpp/variant/array.hpp>
@@ -52,6 +53,7 @@
#include <godot_cpp/variant/rect2.hpp>
#include <godot_cpp/variant/rid.hpp>
#include <godot_cpp/variant/string.hpp>
+#include <godot_cpp/variant/typed_array.hpp>
#include <godot_cpp/variant/vector2.hpp>
#include <godot_cpp/variant/vector2i.hpp>
@@ -65,11 +67,11 @@
#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/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@@ -79,12 +81,13 @@ using namespace godot;
#include "servers/text/text_server_extension.h"
+#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/worker_thread_pool.h"
#include "core/templates/hash_map.h"
#include "core/templates/rid_owner.h"
#include "scene/resources/texture.h"
-#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
#endif
@@ -98,6 +101,11 @@ using namespace godot;
#include FT_ADVANCES_H
#include FT_MULTIPLE_MASTERS_H
#include FT_BBOX_H
+#include FT_MODULE_H
+#include FT_CONFIG_OPTIONS_H
+#if !defined(FT_CONFIG_OPTION_USE_BROTLI) && !defined(_MSC_VER)
+#warning FreeType is configured without Brotli support, built-in fonts will not be available.
+#endif
#endif
/*************************************************************************/
@@ -120,20 +128,86 @@ class TextServerFallback : public TextServerExtension {
const int rect_range = 1;
- struct FontTexture {
+ struct FontTexturePosition {
+ int32_t index = -1;
+ int32_t x = 0;
+ int32_t y = 0;
+
+ FontTexturePosition() {}
+ FontTexturePosition(int32_t p_id, int32_t p_x, int32_t p_y) :
+ index(p_id), x(p_x), y(p_y) {}
+ };
+
+ struct Shelf {
+ int32_t x = 0;
+ int32_t y = 0;
+ int32_t w = 0;
+ int32_t h = 0;
+
+ FontTexturePosition alloc_shelf(int32_t p_id, int32_t p_w, int32_t p_h) {
+ if (p_w > w || p_h > h) {
+ return FontTexturePosition(-1, 0, 0);
+ }
+ int32_t xx = x;
+ x += p_w;
+ w -= p_w;
+ return FontTexturePosition(p_id, xx, y);
+ }
+
+ Shelf() {}
+ Shelf(int32_t p_x, int32_t p_y, int32_t p_w, int32_t p_h) :
+ x(p_x), y(p_y), w(p_w), h(p_h) {}
+ };
+
+ struct ShelfPackTexture {
+ int32_t texture_w = 1024;
+ int32_t texture_h = 1024;
+
Image::Format format;
PackedByteArray imgdata;
- int texture_w = 0;
- int texture_h = 0;
- PackedInt32Array offsets;
Ref<ImageTexture> texture;
bool dirty = true;
- };
- struct FontTexturePosition {
- int index = 0;
- int x = 0;
- int y = 0;
+ List<Shelf> shelves;
+
+ FontTexturePosition pack_rect(int32_t p_id, int32_t p_h, int32_t p_w) {
+ int32_t y = 0;
+ int32_t waste = 0;
+ Shelf *best_shelf = nullptr;
+ int32_t best_waste = std::numeric_limits<std::int32_t>::max();
+
+ for (Shelf &E : shelves) {
+ y += E.h;
+ if (p_w > E.w) {
+ continue;
+ }
+ if (p_h == E.h) {
+ return E.alloc_shelf(p_id, p_w, p_h);
+ }
+ if (p_h > E.h) {
+ continue;
+ }
+ if (p_h < E.h) {
+ waste = (E.h - p_h) * p_w;
+ if (waste < best_waste) {
+ best_waste = waste;
+ best_shelf = &E;
+ }
+ }
+ }
+ if (best_shelf) {
+ return best_shelf->alloc_shelf(p_id, p_w, p_h);
+ }
+ if (p_h <= (texture_h - y) && p_w <= texture_w) {
+ List<Shelf>::Element *E = shelves.push_back(Shelf(0, y, texture_w, p_h));
+ return E->get().alloc_shelf(p_id, p_w, p_h);
+ }
+ return FontTexturePosition(-1, 0, 0);
+ }
+
+ ShelfPackTexture() {}
+ ShelfPackTexture(int32_t p_w, int32_t p_h) :
+ texture_w(p_w), texture_h(p_h) {}
};
struct FontGlyph {
@@ -154,7 +228,7 @@ class TextServerFallback : public TextServerExtension {
Vector2i size;
- Vector<FontTexture> textures;
+ Vector<ShelfPackTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
HashMap<Vector2i, Vector2> kerning_map;
@@ -175,13 +249,14 @@ class TextServerFallback : public TextServerExtension {
struct FontFallback {
Mutex mutex;
- bool antialiased = true;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
bool mipmaps = false;
bool msdf = false;
int msdf_range = 14;
int msdf_source_size = 48;
int fixed_size = 0;
bool force_autohinter = false;
+ bool allow_system_fallback = true;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
Dictionary variation_coordinates;
@@ -192,6 +267,8 @@ class TextServerFallback : public TextServerExtension {
BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
+ int weight = 400;
+ int stretch = 100;
HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
@@ -221,12 +298,12 @@ class TextServerFallback : public TextServerExtension {
_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(FontForSizeFallback *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, bool p_bgra) const;
#endif
_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;
+ static void _generateMTSDF_threaded(void *p_td, uint32_t p_y);
_FORCE_INLINE_ Vector2i _get_size(const FontFallback *p_font_data, int p_size) const {
if (p_font_data->msdf) {
@@ -248,6 +325,58 @@ class TextServerFallback : public TextServerExtension {
}
}
+ _FORCE_INLINE_ int _font_get_weight_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("thin") >= 0 || sty_name.find("hairline") >= 0) {
+ return 100;
+ } else if (sty_name.find("extralight") >= 0 || sty_name.find("ultralight") >= 0) {
+ return 200;
+ } else if (sty_name.find("light") >= 0) {
+ return 300;
+ } else if (sty_name.find("semilight") >= 0) {
+ return 350;
+ } else if (sty_name.find("regular") >= 0) {
+ return 400;
+ } else if (sty_name.find("medium") >= 0) {
+ return 500;
+ } else if (sty_name.find("semibold") >= 0 || sty_name.find("demibold") >= 0) {
+ return 600;
+ } else if (sty_name.find("bold") >= 0) {
+ return 700;
+ } else if (sty_name.find("extrabold") >= 0 || sty_name.find("ultrabold") >= 0) {
+ return 800;
+ } else if (sty_name.find("black") >= 0 || sty_name.find("heavy") >= 0) {
+ return 900;
+ } else if (sty_name.find("extrablack") >= 0 || sty_name.find("ultrablack") >= 0) {
+ return 950;
+ }
+ return 400;
+ }
+ _FORCE_INLINE_ int _font_get_stretch_by_name(const String &p_sty_name) const {
+ String sty_name = p_sty_name.replace(" ", "").replace("-", "");
+ if (sty_name.find("ultracondensed") >= 0) {
+ return 50;
+ } else if (sty_name.find("extracondensed") >= 0) {
+ return 63;
+ } else if (sty_name.find("condensed") >= 0) {
+ return 75;
+ } else if (sty_name.find("semicondensed") >= 0) {
+ return 87;
+ } else if (sty_name.find("semiexpanded") >= 0) {
+ return 113;
+ } else if (sty_name.find("expanded") >= 0) {
+ return 125;
+ } else if (sty_name.find("extraexpanded") >= 0) {
+ return 150;
+ } else if (sty_name.find("ultraexpanded") >= 0) {
+ return 200;
+ }
+ return 100;
+ }
+ _FORCE_INLINE_ bool _is_ital_style(const String &p_sty_name) const {
+ return (p_sty_name.find("italic") >= 0) || (p_sty_name.find("oblique") >= 0);
+ }
+
// Shaped text cache data.
struct TrimData {
int trim_pos = -1;
@@ -288,6 +417,7 @@ class TextServerFallback : public TextServerExtension {
int pos = 0;
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Rect2 rect;
+ float baseline = 0;
};
HashMap<Variant, EmbeddedObject, VariantHasher, VariantComparator> objects;
@@ -324,6 +454,81 @@ class TextServerFallback : public TextServerExtension {
mutable RID_PtrOwner<FontFallback> font_owner;
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
+ struct SystemFontKey {
+ String font_name;
+ TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY;
+ bool italic = false;
+ bool mipmaps = false;
+ bool msdf = false;
+ bool force_autohinter = false;
+ int weight = 400;
+ int stretch = 100;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ Dictionary variation_coordinates;
+ double oversampling = 0.0;
+ double embolden = 0.0;
+ Transform2D transform;
+
+ bool operator==(const SystemFontKey &p_b) const {
+ return (font_name == p_b.font_name) && (antialiasing == p_b.antialiasing) && (italic == p_b.italic) && (mipmaps == p_b.mipmaps) && (msdf == p_b.msdf) && (force_autohinter == p_b.force_autohinter) && (weight == p_b.weight) && (stretch == p_b.stretch) && (msdf_range == p_b.msdf_range) && (msdf_source_size == p_b.msdf_source_size) && (fixed_size == p_b.fixed_size) && (hinting == p_b.hinting) && (subpixel_positioning == p_b.subpixel_positioning) && (variation_coordinates == p_b.variation_coordinates) && (oversampling == p_b.oversampling) && (embolden == p_b.embolden) && (transform == p_b.transform);
+ }
+
+ SystemFontKey(const String &p_font_name, bool p_italic, int p_weight, int p_stretch, RID p_font, const TextServerFallback *p_fb) {
+ font_name = p_font_name;
+ italic = p_italic;
+ weight = p_weight;
+ stretch = p_stretch;
+ antialiasing = p_fb->_font_get_antialiasing(p_font);
+ mipmaps = p_fb->_font_get_generate_mipmaps(p_font);
+ msdf = p_fb->_font_is_multichannel_signed_distance_field(p_font);
+ msdf_range = p_fb->_font_get_msdf_pixel_range(p_font);
+ msdf_source_size = p_fb->_font_get_msdf_size(p_font);
+ fixed_size = p_fb->_font_get_fixed_size(p_font);
+ force_autohinter = p_fb->_font_is_force_autohinter(p_font);
+ hinting = p_fb->_font_get_hinting(p_font);
+ subpixel_positioning = p_fb->_font_get_subpixel_positioning(p_font);
+ variation_coordinates = p_fb->_font_get_variation_coordinates(p_font);
+ oversampling = p_fb->_font_get_oversampling(p_font);
+ embolden = p_fb->_font_get_embolden(p_font);
+ transform = p_fb->_font_get_transform(p_font);
+ }
+ };
+
+ struct SystemFontCacheRec {
+ RID rid;
+ int index = 0;
+ };
+
+ struct SystemFontCache {
+ Vector<SystemFontCacheRec> var;
+ int max_var = 0;
+ };
+
+ struct SystemFontKeyHasher {
+ _FORCE_INLINE_ static uint32_t hash(const SystemFontKey &p_a) {
+ uint32_t hash = p_a.font_name.hash();
+ hash = hash_murmur3_one_32(p_a.variation_coordinates.hash(), hash);
+ hash = hash_murmur3_one_32(p_a.weight, hash);
+ hash = hash_murmur3_one_32(p_a.stretch, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_range, hash);
+ hash = hash_murmur3_one_32(p_a.msdf_source_size, hash);
+ hash = hash_murmur3_one_32(p_a.fixed_size, hash);
+ hash = hash_murmur3_one_double(p_a.oversampling, hash);
+ hash = hash_murmur3_one_double(p_a.embolden, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[0].y, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].x, hash);
+ hash = hash_murmur3_one_real(p_a.transform[1].y, hash);
+ return hash_fmix32(hash_murmur3_one_32(((int)p_a.mipmaps) | ((int)p_a.msdf << 1) | ((int)p_a.italic << 2) | ((int)p_a.force_autohinter << 3) | ((int)p_a.hinting << 4) | ((int)p_a.subpixel_positioning << 8) | ((int)p_a.antialiasing << 12), hash));
+ }
+ };
+ mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
+ mutable HashMap<String, PackedByteArray> system_font_data;
+
void _realign(ShapedTextDataFallback *p_sd) const;
protected:
@@ -333,251 +538,260 @@ protected:
void invalidate(ShapedTextDataFallback *p_shaped);
public:
- virtual bool has_feature(Feature p_feature) const override;
- virtual String get_name() const override;
- virtual int64_t get_features() const override;
+ MODBIND1RC(bool, has_feature, Feature);
+ MODBIND0RC(String, get_name);
+ MODBIND0RC(int64_t, get_features);
- virtual void free_rid(const RID &p_rid) override;
- virtual bool has(const RID &p_rid) override;
- virtual bool load_support_data(const String &p_filename) override;
+ MODBIND1(free_rid, const RID &);
+ MODBIND1R(bool, has, const RID &);
+ MODBIND1R(bool, load_support_data, const String &);
- virtual String get_support_data_filename() const override {
- return "";
- };
- virtual String get_support_data_info() const override {
- return "Not supported";
- };
- virtual bool save_support_data(const String &p_filename) const override;
+ MODBIND0RC(String, get_support_data_filename);
+ MODBIND0RC(String, get_support_data_info);
+ MODBIND1RC(bool, save_support_data, const String &);
- virtual bool is_locale_right_to_left(const String &p_locale) const override;
+ MODBIND1RC(bool, is_locale_right_to_left, const String &);
- virtual int64_t name_to_tag(const String &p_name) const override;
- virtual String tag_to_name(int64_t p_tag) const override;
+ MODBIND1RC(int64_t, name_to_tag, const String &);
+ MODBIND1RC(String, tag_to_name, int64_t);
/* Font interface */
- virtual RID create_font() override;
- 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;
+ MODBIND0R(RID, create_font);
+
+ MODBIND2(font_set_data, const RID &, const PackedByteArray &);
+ MODBIND3(font_set_data_ptr, const RID &, const uint8_t *, int64_t);
+
+ MODBIND2(font_set_face_index, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_face_index, const RID &);
- 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;
+ MODBIND1RC(int64_t, font_get_face_count, const RID &);
- virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+ MODBIND2(font_set_style, const RID &, BitField<FontStyle>);
+ MODBIND1RC(BitField<FontStyle>, font_get_style, const RID &);
- 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;
+ MODBIND2(font_set_style_name, const RID &, const String &);
+ MODBIND1RC(String, font_get_style_name, const RID &);
- 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;
+ MODBIND2(font_set_weight, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_weight, const RID &);
- virtual void font_set_name(const RID &p_font_rid, const String &p_name) override;
- virtual String font_get_name(const RID &p_font_rid) const override;
+ MODBIND2(font_set_stretch, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_stretch, const RID &);
- virtual void font_set_antialiased(const RID &p_font_rid, bool p_antialiased) override;
- virtual bool font_is_antialiased(const RID &p_font_rid) const override;
+ MODBIND2(font_set_name, const RID &, const String &);
+ MODBIND1RC(String, font_get_name, const RID &);
- virtual void font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) override;
- virtual bool font_get_generate_mipmaps(const RID &p_font_rid) const override;
+ MODBIND2(font_set_antialiasing, const RID &, TextServer::FontAntialiasing);
+ MODBIND1RC(TextServer::FontAntialiasing, font_get_antialiasing, const RID &);
- virtual void font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) override;
- virtual bool font_is_multichannel_signed_distance_field(const RID &p_font_rid) const override;
+ MODBIND2(font_set_generate_mipmaps, const RID &, bool);
+ MODBIND1RC(bool, font_get_generate_mipmaps, const RID &);
- virtual void font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) override;
- virtual int64_t font_get_msdf_pixel_range(const RID &p_font_rid) const override;
+ MODBIND2(font_set_multichannel_signed_distance_field, const RID &, bool);
+ MODBIND1RC(bool, font_is_multichannel_signed_distance_field, const RID &);
- virtual void font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) override;
- virtual int64_t font_get_msdf_size(const RID &p_font_rid) const override;
+ MODBIND2(font_set_msdf_pixel_range, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_msdf_pixel_range, const RID &);
- virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) override;
- virtual int64_t font_get_fixed_size(const RID &p_font_rid) const override;
+ MODBIND2(font_set_msdf_size, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_msdf_size, const RID &);
- virtual void font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) override;
- virtual bool font_is_force_autohinter(const RID &p_font_rid) const override;
+ MODBIND2(font_set_fixed_size, const RID &, int64_t);
+ MODBIND1RC(int64_t, font_get_fixed_size, const RID &);
- virtual void font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) override;
- virtual TextServer::Hinting font_get_hinting(const RID &p_font_rid) const override;
+ MODBIND2(font_set_allow_system_fallback, const RID &, bool);
+ MODBIND1RC(bool, font_is_allow_system_fallback, const RID &);
- virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override;
- virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override;
+ MODBIND2(font_set_force_autohinter, const RID &, bool);
+ MODBIND1RC(bool, font_is_force_autohinter, const RID &);
- virtual void font_set_embolden(const RID &p_font_rid, double p_strength) override;
- virtual double font_get_embolden(const RID &p_font_rid) const override;
+ MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning);
+ MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &);
- virtual void font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) override;
- virtual Transform2D font_get_transform(const RID &p_font_rid) const override;
+ MODBIND2(font_set_embolden, const RID &, double);
+ MODBIND1RC(double, font_get_embolden, const RID &);
- virtual void font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) override;
- virtual Dictionary font_get_variation_coordinates(const RID &p_font_rid) const override;
+ MODBIND2(font_set_transform, const RID &, const Transform2D &);
+ MODBIND1RC(Transform2D, font_get_transform, const RID &);
- virtual void font_set_oversampling(const RID &p_font_rid, double p_oversampling) override;
- virtual double font_get_oversampling(const RID &p_font_rid) const override;
+ MODBIND2(font_set_variation_coordinates, const RID &, const Dictionary &);
+ MODBIND1RC(Dictionary, font_get_variation_coordinates, const RID &);
- virtual Array font_get_size_cache_list(const RID &p_font_rid) const override;
- virtual void font_clear_size_cache(const RID &p_font_rid) override;
- virtual void font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) override;
+ MODBIND2(font_set_hinting, const RID &, TextServer::Hinting);
+ MODBIND1RC(TextServer::Hinting, font_get_hinting, const RID &);
- virtual void font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) override;
- virtual double font_get_ascent(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND2(font_set_oversampling, const RID &, double);
+ MODBIND1RC(double, font_get_oversampling, const RID &);
- virtual void font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) override;
- virtual double font_get_descent(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND1RC(TypedArray<Vector2i>, font_get_size_cache_list, const RID &);
+ MODBIND1(font_clear_size_cache, const RID &);
+ MODBIND2(font_remove_size_cache, const RID &, const Vector2i &);
- virtual void font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) override;
- virtual double font_get_underline_position(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND3(font_set_ascent, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_ascent, const RID &, int64_t);
- virtual void font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) override;
- virtual double font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const override;
+ MODBIND3(font_set_descent, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_descent, const RID &, int64_t);
- 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;
+ MODBIND3(font_set_underline_position, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_underline_position, const RID &, int64_t);
- 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;
+ MODBIND3(font_set_underline_thickness, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_underline_thickness, const RID &, int64_t);
- virtual void font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) override;
- virtual Ref<Image> font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
+ MODBIND3(font_set_scale, const RID &, int64_t, double);
+ MODBIND2RC(double, font_get_scale, const RID &, int64_t);
- virtual void font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) override;
- virtual PackedInt32Array font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
+ MODBIND2RC(int64_t, font_get_texture_count, const RID &, const Vector2i &);
+ MODBIND2(font_clear_textures, const RID &, const Vector2i &);
+ MODBIND3(font_remove_texture, const RID &, const Vector2i &, int64_t);
- virtual Array font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const override;
- virtual void font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) override;
- virtual void font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) override;
+ MODBIND4(font_set_texture_image, const RID &, const Vector2i &, int64_t, const Ref<Image> &);
+ MODBIND3RC(Ref<Image>, font_get_texture_image, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) override;
+ MODBIND4(font_set_texture_offsets, const RID &, const Vector2i &, int64_t, const PackedInt32Array &);
+ MODBIND3RC(PackedInt32Array, font_get_texture_offsets, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) override;
+ MODBIND2RC(PackedInt32Array, font_get_glyph_list, const RID &, const Vector2i &);
+ MODBIND2(font_clear_glyphs, const RID &, const Vector2i &);
+ MODBIND3(font_remove_glyph, const RID &, const Vector2i &, int64_t);
- virtual Vector2 font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) override;
+ MODBIND3RC(Vector2, font_get_glyph_advance, const RID &, int64_t, int64_t);
+ MODBIND4(font_set_glyph_advance, const RID &, int64_t, int64_t, const Vector2 &);
- virtual Rect2 font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) override;
+ MODBIND3RC(Vector2, font_get_glyph_offset, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_offset, const RID &, const Vector2i &, int64_t, const Vector2 &);
- virtual int64_t font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual void font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) override;
- virtual RID font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- virtual Size2 font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
+ MODBIND3RC(Vector2, font_get_glyph_size, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_size, const RID &, const Vector2i &, int64_t, const Vector2 &);
- virtual Dictionary font_get_glyph_contours(const RID &p_font, int64_t p_size, int64_t p_index) const override;
+ MODBIND3RC(Rect2, font_get_glyph_uv_rect, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_uv_rect, const RID &, const Vector2i &, int64_t, const Rect2 &);
- virtual Array font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) override;
- virtual void font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) override;
+ MODBIND3RC(int64_t, font_get_glyph_texture_idx, const RID &, const Vector2i &, int64_t);
+ MODBIND4(font_set_glyph_texture_idx, const RID &, const Vector2i &, int64_t, int64_t);
- virtual void font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
- virtual Vector2 font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const override;
+ MODBIND3RC(RID, font_get_glyph_texture_rid, const RID &, const Vector2i &, int64_t);
+ MODBIND3RC(Size2, font_get_glyph_texture_size, const RID &, const Vector2i &, int64_t);
- virtual int64_t font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector = 0) const override;
+ MODBIND3RC(Dictionary, font_get_glyph_contours, const RID &, int64_t, int64_t);
- virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override;
- virtual String font_get_supported_chars(const RID &p_font_rid) const override;
+ MODBIND2RC(TypedArray<Vector2i>, font_get_kerning_list, const RID &, int64_t);
+ MODBIND2(font_clear_kerning_map, const RID &, int64_t);
+ MODBIND3(font_remove_kerning, const RID &, int64_t, const Vector2i &);
- virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override;
- virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override;
+ MODBIND4(font_set_kerning, const RID &, int64_t, const Vector2i &, const Vector2 &);
+ MODBIND3RC(Vector2, font_get_kerning, const RID &, int64_t, const Vector2i &);
- virtual void font_draw_glyph(const RID &p_font, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual void font_draw_glyph_outline(const RID &p_font, 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 = Color(1, 1, 1)) const override;
+ MODBIND4RC(int64_t, font_get_glyph_index, const RID &, int64_t, int64_t, int64_t);
- virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const override;
- virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(const RID &p_font_rid, const String &p_language) override;
- virtual void font_remove_language_support_override(const RID &p_font_rid, const String &p_language) override;
- virtual PackedStringArray font_get_language_support_overrides(const RID &p_font_rid) override;
+ MODBIND2RC(bool, font_has_char, const RID &, int64_t);
+ MODBIND1RC(String, font_get_supported_chars, const RID &);
- virtual bool font_is_script_supported(const RID &p_font_rid, const String &p_script) const override;
- virtual void font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(const RID &p_font_rid, const String &p_script) override;
- virtual void font_remove_script_support_override(const RID &p_font_rid, const String &p_script) override;
- virtual PackedStringArray font_get_script_support_overrides(const RID &p_font_rid) override;
+ MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
+ MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
- virtual void font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) override;
- virtual Dictionary font_get_opentype_feature_overrides(const RID &p_font_rid) const override;
+ MODBIND6C(font_draw_glyph, const RID &, const RID &, int64_t, const Vector2 &, int64_t, const Color &);
+ MODBIND7C(font_draw_glyph_outline, const RID &, const RID &, int64_t, int64_t, const Vector2 &, int64_t, const Color &);
- virtual Dictionary font_supported_feature_list(const RID &p_font_rid) const override;
- virtual Dictionary font_supported_variation_list(const RID &p_font_rid) const override;
+ MODBIND2RC(bool, font_is_language_supported, const RID &, const String &);
+ MODBIND3(font_set_language_support_override, const RID &, const String &, bool);
+ MODBIND2R(bool, font_get_language_support_override, const RID &, const String &);
+ MODBIND2(font_remove_language_support_override, const RID &, const String &);
+ MODBIND1R(PackedStringArray, font_get_language_support_overrides, const RID &);
- virtual double font_get_global_oversampling() const override;
- virtual void font_set_global_oversampling(double p_oversampling) override;
+ MODBIND2RC(bool, font_is_script_supported, const RID &, const String &);
+ MODBIND3(font_set_script_support_override, const RID &, const String &, bool);
+ MODBIND2R(bool, font_get_script_support_override, const RID &, const String &);
+ MODBIND2(font_remove_script_support_override, const RID &, const String &);
+ MODBIND1R(PackedStringArray, font_get_script_support_overrides, const RID &);
+
+ MODBIND2(font_set_opentype_feature_overrides, const RID &, const Dictionary &);
+ MODBIND1RC(Dictionary, font_get_opentype_feature_overrides, const RID &);
+
+ MODBIND1RC(Dictionary, font_supported_feature_list, const RID &);
+ MODBIND1RC(Dictionary, font_supported_variation_list, const RID &);
+
+ MODBIND0RC(double, font_get_global_oversampling);
+ MODBIND1(font_set_global_oversampling, double);
/* Shaped text buffer interface */
- virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
+ MODBIND2R(RID, create_shaped_text, Direction, Orientation);
+
+ MODBIND1(shaped_text_clear, const RID &);
- virtual void shaped_text_clear(const RID &p_shaped) override;
+ MODBIND2(shaped_text_set_direction, const RID &, Direction);
+ MODBIND1RC(Direction, shaped_text_get_direction, const RID &);
+ MODBIND1RC(Direction, shaped_text_get_inferred_direction, const RID &);
- virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) override;
- virtual Direction shaped_text_get_direction(const RID &p_shaped) const override;
- virtual Direction shaped_text_get_inferred_direction(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_bidi_override, const RID &, const Array &);
- virtual void shaped_text_set_bidi_override(const RID &p_shaped, const Array &p_override) override;
+ MODBIND2(shaped_text_set_custom_punctuation, const RID &, const String &);
+ MODBIND1RC(String, shaped_text_get_custom_punctuation, const RID &);
- virtual void shaped_text_set_custom_punctuation(const RID &p_shaped, const String &p_punct) override;
- virtual String shaped_text_get_custom_punctuation(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_orientation, const RID &, Orientation);
+ MODBIND1RC(Orientation, shaped_text_get_orientation, const RID &);
- virtual void shaped_text_set_orientation(const RID &p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
- virtual Orientation shaped_text_get_orientation(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_preserve_invalid, const RID &, bool);
+ MODBIND1RC(bool, shaped_text_get_preserve_invalid, const RID &);
- virtual void shaped_text_set_preserve_invalid(const RID &p_shaped, bool p_enabled) override;
- virtual bool shaped_text_get_preserve_invalid(const RID &p_shaped) const override;
+ MODBIND2(shaped_text_set_preserve_control, const RID &, bool);
+ MODBIND1RC(bool, shaped_text_get_preserve_control, const RID &);
- 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;
+ MODBIND3(shaped_text_set_spacing, const RID &, SpacingType, int64_t);
+ MODBIND2RC(int64_t, shaped_text_get_spacing, const RID &, SpacingType);
- 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;
+ MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
+ MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, float);
+ MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, float);
- 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;
+ MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
+ MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
+ MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
- virtual int64_t shaped_get_span_count(const RID &p_shaped) const override;
- virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override;
- virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override;
+ MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);
+ MODBIND1RC(RID, shaped_text_get_parent, const RID &);
- 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;
+ MODBIND3R(double, shaped_text_fit_to_width, const RID &, double, BitField<TextServer::JustificationFlag>);
+ MODBIND2R(double, shaped_text_tab_align, const RID &, const PackedFloat32Array &);
- 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;
+ MODBIND1R(bool, shaped_text_shape, const RID &);
+ MODBIND1R(bool, shaped_text_update_breaks, const RID &);
+ MODBIND1R(bool, shaped_text_update_justification_ops, const RID &);
- virtual bool shaped_text_shape(const RID &p_shaped) override;
- virtual bool shaped_text_update_breaks(const RID &p_shaped) override;
- virtual bool shaped_text_update_justification_ops(const RID &p_shaped) override;
+ MODBIND1RC(int64_t, shaped_text_get_trim_pos, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_ellipsis_pos, const RID &);
+ MODBIND1RC(const Glyph *, shaped_text_get_ellipsis_glyphs, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_ellipsis_glyph_count, const RID &);
- virtual int64_t shaped_text_get_trim_pos(const RID &p_shaped) const override;
- virtual int64_t shaped_text_get_ellipsis_pos(const RID &p_shaped) const override;
- 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;
+ MODBIND3(shaped_text_overrun_trim_to_width, const RID &, double, BitField<TextServer::TextOverrunFlag>);
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
+ MODBIND1RC(bool, shaped_text_is_ready, const RID &);
- virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
+ MODBIND1RC(const Glyph *, shaped_text_get_glyphs, const RID &);
+ MODBIND1R(const Glyph *, shaped_text_sort_logical, const RID &);
+ MODBIND1RC(int64_t, shaped_text_get_glyph_count, const RID &);
- virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const override;
- virtual const Glyph *shaped_text_sort_logical(const RID &p_shaped) override;
- virtual int64_t shaped_text_get_glyph_count(const RID &p_shaped) const override;
+ MODBIND1RC(Vector2i, shaped_text_get_range, const RID &);
- virtual Vector2i shaped_text_get_range(const RID &p_shaped) const override;
+ MODBIND1RC(Array, shaped_text_get_objects, const RID &);
+ MODBIND2RC(Rect2, shaped_text_get_object_rect, const RID &, const Variant &);
- virtual Array shaped_text_get_objects(const RID &p_shaped) const override;
- virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override;
+ MODBIND1RC(Size2, shaped_text_get_size, const RID &);
+ MODBIND1RC(double, shaped_text_get_ascent, const RID &);
+ MODBIND1RC(double, shaped_text_get_descent, const RID &);
+ MODBIND1RC(double, shaped_text_get_width, const RID &);
+ MODBIND1RC(double, shaped_text_get_underline_position, const RID &);
+ MODBIND1RC(double, shaped_text_get_underline_thickness, const RID &);
- virtual Size2 shaped_text_get_size(const RID &p_shaped) const override;
- virtual double shaped_text_get_ascent(const RID &p_shaped) const override;
- virtual double shaped_text_get_descent(const RID &p_shaped) const override;
- virtual double shaped_text_get_width(const RID &p_shaped) const override;
- virtual double shaped_text_get_underline_position(const RID &p_shaped) const override;
- virtual double shaped_text_get_underline_thickness(const RID &p_shaped) const override;
+ MODBIND3RC(PackedInt32Array, string_get_word_breaks, const String &, const String &, int);
- virtual PackedInt32Array string_get_word_breaks(const String &p_string, const String &p_language = "") const override;
+ MODBIND2RC(String, string_to_upper, const String &, const String &);
+ MODBIND2RC(String, string_to_lower, const String &, const String &);
- virtual String string_to_upper(const String &p_string, const String &p_language = "") const override;
- virtual String string_to_lower(const String &p_string, const String &p_language = "") const override;
+ MODBIND0(cleanup);
TextServerFallback();
~TextServerFallback();
diff --git a/modules/text_server_fb/thorvg_bounds_iterator.cpp b/modules/text_server_fb/thorvg_bounds_iterator.cpp
new file mode 100644
index 0000000000..807f356b83
--- /dev/null
+++ b/modules/text_server_fb/thorvg_bounds_iterator.cpp
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* thorvg_bounds_iterator.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/godot.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include "thorvg_bounds_iterator.h"
+
+#include <tvgIteratorAccessor.h>
+#include <tvgPaint.h>
+
+// This function uses private ThorVG API to get bounding box of top level children elements.
+
+void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) {
+ tvg::IteratorAccessor itrAccessor;
+ if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) {
+ while (const tvg::Paint *child = it->next()) {
+ float x = 0, y = 0, w = 0, h = 0;
+ child->bounds(&x, &y, &w, &h, true);
+ r_min_x = MIN(x, r_min_x);
+ r_min_y = MIN(y, r_min_y);
+ r_max_x = MAX(x + w, r_max_x);
+ r_max_y = MAX(y + h, r_max_y);
+ }
+ delete (it);
+ }
+}
+
+#endif // MODULE_SVG_ENABLED
diff --git a/modules/text_server_fb/thorvg_bounds_iterator.h b/modules/text_server_fb/thorvg_bounds_iterator.h
new file mode 100644
index 0000000000..a44cbb99a7
--- /dev/null
+++ b/modules/text_server_fb/thorvg_bounds_iterator.h
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* thorvg_bounds_iterator.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 THORVG_BOUNDS_ITERATOR_H
+#define THORVG_BOUNDS_ITERATOR_H
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include <thorvg.h>
+
+void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y);
+
+#endif // MODULE_SVG_ENABLED
+
+#endif // THORVG_BOUNDS_ITERATOR_H
diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp
new file mode 100644
index 0000000000..1406e3aaa0
--- /dev/null
+++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp
@@ -0,0 +1,287 @@
+/**************************************************************************/
+/* thorvg_svg_in_ot.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/classes/xml_parser.hpp>
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+#include <godot_cpp/templates/vector.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/error/error_macros.h"
+#include "core/io/xml_parser.h"
+#include "core/os/memory.h"
+#include "core/os/os.h"
+#include "core/string/ustring.h"
+#include "core/typedefs.h"
+#include "core/variant/variant.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include "thorvg_bounds_iterator.h"
+#include "thorvg_svg_in_ot.h"
+
+#include <freetype/otsvg.h>
+#include <ft2build.h>
+
+#include <math.h>
+#include <stdlib.h>
+
+FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state) {
+ *p_state = memnew(TVG_State);
+
+ return FT_Err_Ok;
+}
+
+void tvg_svg_in_ot_free(FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ memdelete(state);
+}
+
+FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ if (!state) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
+ }
+ MutexLock lock(state->mutex);
+
+ FT_SVG_Document document = (FT_SVG_Document)p_slot->other;
+ FT_Size_Metrics metrics = document->metrics;
+
+ GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
+ if (!gl_state.ready) {
+ Ref<XMLParser> parser;
+ parser.instantiate();
+ parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length);
+
+ float aspect = 1.0f;
+ String xml_body;
+ while (parser->read() == OK) {
+ if (parser->has_attribute("id")) {
+ const String &gl_name = parser->get_named_attribute_value("id");
+ if (gl_name.begins_with("glyph")) {
+ int dot_pos = gl_name.find(".");
+ int64_t gl_idx = gl_name.substr(5, (dot_pos > 0) ? dot_pos - 5 : -1).to_int();
+ if (p_slot->glyph_index != gl_idx) {
+ parser->skip_section();
+ continue;
+ }
+ }
+ }
+ if (parser->get_node_type() == XMLParser::NODE_ELEMENT && parser->get_node_name() == "svg") {
+ if (parser->has_attribute("viewBox")) {
+ PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" ");
+
+ if (vb.size() == 4) {
+ aspect = vb[2].to_float() / vb[3].to_float();
+ }
+ }
+ continue;
+ }
+ if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
+ xml_body += vformat("<%s", parser->get_node_name());
+ for (int i = 0; i < parser->get_attribute_count(); i++) {
+ xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i));
+ }
+ xml_body += ">";
+ } else if (parser->get_node_type() == XMLParser::NODE_TEXT) {
+ xml_body += parser->get_node_data();
+ } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) {
+ xml_body += vformat("</%s>", parser->get_node_name());
+ }
+ }
+ String temp_xml = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 0 0\">" + xml_body;
+
+ std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
+ tvg::Result result = picture->load(temp_xml.utf8().get_data(), temp_xml.utf8().length(), "svg+xml", false);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection).");
+ }
+
+ float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY;
+ tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y);
+
+ float new_h = (max_y - min_y);
+ float new_w = (max_x - min_x);
+
+ if (new_h * aspect >= new_w) {
+ new_w = (new_h * aspect);
+ } else {
+ new_h = (new_w / aspect);
+ }
+
+ gl_state.xml_code = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body;
+
+ picture = tvg::Picture::gen();
+ result = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics).");
+ }
+
+ float x_svg_to_out, y_svg_to_out;
+ x_svg_to_out = (float)metrics.x_ppem / new_w;
+ y_svg_to_out = (float)metrics.y_ppem / new_h;
+
+ gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out;
+ gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out;
+ gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out;
+ gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out;
+ gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem;
+ gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem;
+ gl_state.m.e31 = 0;
+ gl_state.m.e32 = 0;
+ gl_state.m.e33 = 1;
+
+ result = picture->transform(gl_state.m);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
+ }
+
+ result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true);
+ if (result != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds.");
+ }
+
+ gl_state.bmp_y = -min_y * gl_state.h / new_h;
+ gl_state.bmp_x = min_x * gl_state.w / new_w;
+
+ gl_state.ready = true;
+ }
+
+ p_slot->bitmap_left = (FT_Int)gl_state.bmp_x;
+ p_slot->bitmap_top = (FT_Int)gl_state.bmp_y;
+
+ float tmp = ceil(gl_state.h);
+ p_slot->bitmap.rows = (unsigned int)tmp;
+ tmp = ceil(gl_state.w);
+ p_slot->bitmap.width = (unsigned int)tmp;
+ p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4;
+ p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+
+ float metrics_width, metrics_height;
+ float horiBearingX, horiBearingY;
+ float vertBearingX, vertBearingY;
+
+ metrics_width = (float)gl_state.w;
+ metrics_height = (float)gl_state.h;
+ horiBearingX = (float)gl_state.x;
+ horiBearingY = (float)-gl_state.y;
+ vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2;
+ vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2;
+
+ tmp = roundf(metrics_width * 64);
+ p_slot->metrics.width = (FT_Pos)tmp;
+ tmp = roundf(metrics_height * 64);
+ p_slot->metrics.height = (FT_Pos)tmp;
+
+ p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64);
+ p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64);
+ p_slot->metrics.vertBearingX = (FT_Pos)(vertBearingX * 64);
+ p_slot->metrics.vertBearingY = (FT_Pos)(vertBearingY * 64);
+
+ if (p_slot->metrics.vertAdvance == 0) {
+ p_slot->metrics.vertAdvance = (FT_Pos)(metrics_height * 1.2f * 64);
+ }
+
+ return FT_Err_Ok;
+}
+
+FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) {
+ TVG_State *state = *reinterpret_cast<TVG_State **>(p_state);
+ if (!state) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG in OT state not initialized.");
+ }
+ MutexLock lock(state->mutex);
+
+ if (!state->glyph_map.has(p_slot->glyph_index)) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "SVG glyph not loaded.");
+ }
+
+ GL_State &gl_state = state->glyph_map[p_slot->glyph_index];
+ ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready.");
+
+ std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
+ tvg::Result res = picture->load(gl_state.xml_code.utf8().get_data(), gl_state.xml_code.utf8().length(), "svg+xml", false);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering).");
+ }
+ res = picture->transform(gl_state.m);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document.");
+ }
+
+ std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
+ res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT);
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas.");
+ }
+ res = sw_canvas->push(std::move(picture));
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to set SVG canvas source.");
+ }
+ res = sw_canvas->draw();
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to draw to SVG canvas.");
+ }
+ res = sw_canvas->sync();
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to sync SVG canvas.");
+ }
+
+ state->glyph_map.erase(p_slot->glyph_index);
+
+ p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
+ p_slot->bitmap.num_grays = 256;
+ p_slot->format = FT_GLYPH_FORMAT_BITMAP;
+
+ return FT_Err_Ok;
+}
+
+SVG_RendererHooks tvg_svg_in_ot_hooks = {
+ (SVG_Lib_Init_Func)tvg_svg_in_ot_init,
+ (SVG_Lib_Free_Func)tvg_svg_in_ot_free,
+ (SVG_Lib_Render_Func)tvg_svg_in_ot_render,
+ (SVG_Lib_Preset_Slot_Func)tvg_svg_in_ot_preset_slot,
+};
+
+SVG_RendererHooks *get_tvg_svg_in_ot_hooks() {
+ return &tvg_svg_in_ot_hooks;
+}
+
+#endif // MODULE_SVG_ENABLED
diff --git a/modules/text_server_fb/thorvg_svg_in_ot.h b/modules/text_server_fb/thorvg_svg_in_ot.h
new file mode 100644
index 0000000000..4035a984b6
--- /dev/null
+++ b/modules/text_server_fb/thorvg_svg_in_ot.h
@@ -0,0 +1,86 @@
+/**************************************************************************/
+/* thorvg_svg_in_ot.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 THORVG_SVG_IN_OT_H
+#define THORVG_SVG_IN_OT_H
+
+#ifdef GDEXTENSION
+// Headers for building as GDExtension plug-in.
+
+#include <godot_cpp/core/mutex_lock.hpp>
+#include <godot_cpp/godot.hpp>
+#include <godot_cpp/templates/hash_map.hpp>
+
+using namespace godot;
+
+#else
+// Headers for building as built-in module.
+
+#include "core/os/mutex.h"
+#include "core/templates/hash_map.h"
+#include "core/typedefs.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#endif
+
+#ifdef MODULE_SVG_ENABLED
+
+#include <freetype/freetype.h>
+#include <freetype/otsvg.h>
+#include <ft2build.h>
+#include <thorvg.h>
+
+struct GL_State {
+ bool ready = false;
+ float bmp_x = 0;
+ float bmp_y = 0;
+ float x = 0;
+ float y = 0;
+ float w = 0;
+ float h = 0;
+ String xml_code;
+ tvg::Matrix m;
+};
+
+struct TVG_State {
+ Mutex mutex;
+ HashMap<uint32_t, GL_State> glyph_map;
+};
+
+FT_Error tvg_svg_in_ot_init(FT_Pointer *p_state);
+void tvg_svg_in_ot_free(FT_Pointer *p_state);
+FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Pointer *p_state);
+FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state);
+
+SVG_RendererHooks *get_tvg_svg_in_ot_hooks();
+
+#endif // MODULE_SVG_ENABLED
+
+#endif // THORVG_SVG_IN_OT_H
diff --git a/modules/tga/SCsub b/modules/tga/SCsub
index 067caa6ea0..ccd7d2ee37 100644
--- a/modules/tga/SCsub
+++ b/modules/tga/SCsub
@@ -5,5 +5,5 @@ Import("env_modules")
env_tga = env_modules.Clone()
-# Godot's own source files
+# Godot source files
env_tga.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 08ad1ef9f8..e2bb89811c 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_tga.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_tga.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_tga.h"
@@ -100,7 +100,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
uint32_t width = p_header.image_width;
uint32_t height = p_header.image_height;
tga_origin_e origin = static_cast<tga_origin_e>((p_header.image_descriptor & TGA_ORIGIN_MASK) >> TGA_ORIGIN_SHIFT);
-
+ uint8_t alpha_bits = p_header.image_descriptor & TGA_IMAGE_DESCRIPTOR_ALPHA_MASK;
uint32_t x_start;
int32_t x_step;
uint32_t x_end;
@@ -184,6 +184,27 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
y += y_step;
}
}
+ } else if (p_header.pixel_depth == 16) {
+ while (y != y_end) {
+ while (x != x_end) {
+ if (i + 1 >= p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
+
+ // Always stored as RGBA5551
+ uint8_t r = (p_buffer[i + 1] & 0x7c) << 1;
+ uint8_t g = ((p_buffer[i + 1] & 0x03) << 6) | ((p_buffer[i + 0] & 0xe0) >> 2);
+ uint8_t b = (p_buffer[i + 0] & 0x1f) << 3;
+ uint8_t a = (p_buffer[i + 1] & 0x80) ? 0xff : 0;
+
+ TGA_PUT_PIXEL(r, g, b, alpha_bits ? a : 0xff);
+
+ x += x_step;
+ i += 2;
+ }
+ x = x_start;
+ y += y_step;
+ }
} else if (p_header.pixel_depth == 24) {
while (y != y_end) {
while (x != x_end) {
@@ -225,12 +246,12 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
}
}
- p_image->create(width, height, false, Image::FORMAT_RGBA8, image_data);
+ p_image->initialize_data(width, height, false, Image::FORMAT_RGBA8, image_data);
return OK;
}
-Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, 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);
@@ -263,21 +284,28 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f
err = FAILED;
}
+ uint64_t color_map_size;
if (has_color_map) {
if (tga_header.color_map_length > 256 || (tga_header.color_map_depth != 24) || tga_header.color_map_type != 1) {
err = FAILED;
}
+ color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
} else {
if (tga_header.color_map_type) {
err = FAILED;
}
+ color_map_size = 0;
+ }
+
+ if ((src_image_len - f->get_position()) < (tga_header.id_length + color_map_size)) {
+ err = FAILED; // TGA data appears to be truncated (fewer bytes than expected).
}
if (tga_header.image_width <= 0 || tga_header.image_height <= 0) {
err = FAILED;
}
- if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
+ if (!(tga_header.pixel_depth == 8 || tga_header.pixel_depth == 16 || tga_header.pixel_depth == 24 || tga_header.pixel_depth == 32)) {
err = FAILED;
}
@@ -287,7 +315,6 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f
Vector<uint8_t> palette;
if (has_color_map) {
- size_t color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
err = palette.resize(color_map_size);
if (err == OK) {
uint8_t *palette_w = palette.ptrw();
diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h
index 9b7cbbac77..cde0c3003b 100644
--- a/modules/tga/image_loader_tga.h
+++ b/modules/tga/image_loader_tga.h
@@ -1,38 +1,40 @@
-/*************************************************************************/
-/* image_loader_tga.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_tga.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_TGA_H
#define IMAGE_LOADER_TGA_H
#include "core/io/image_loader.h"
+#define TGA_IMAGE_DESCRIPTOR_ALPHA_MASK 0xf
+
class ImageLoaderTGA : public ImageFormatLoader {
enum tga_type_e {
TGA_TYPE_NO_DATA = 0,
@@ -73,7 +75,7 @@ class ImageLoaderTGA : public ImageFormatLoader {
static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size);
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderTGA();
};
diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp
index 520ed5f799..4172801357 100644
--- a/modules/tga/register_types.cpp
+++ b/modules/tga/register_types.cpp
@@ -1,45 +1,45 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_tga.h"
-static ImageLoaderTGA *image_loader_tga = nullptr;
+static Ref<ImageLoaderTGA> image_loader_tga;
void initialize_tga_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_tga = memnew(ImageLoaderTGA);
+ image_loader_tga.instantiate();
ImageLoader::add_image_format_loader(image_loader_tga);
}
@@ -48,5 +48,6 @@ void uninitialize_tga_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_tga);
+ ImageLoader::remove_image_format_loader(image_loader_tga);
+ image_loader_tga.unref();
}
diff --git a/modules/tga/register_types.h b/modules/tga/register_types.h
index 37cdab70dd..4aa13320bf 100644
--- a/modules/tga/register_types.h
+++ b/modules/tga/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TGA_REGISTER_TYPES_H
#define TGA_REGISTER_TYPES_H
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index 6038ea086a..ca666050dd 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -15,7 +15,7 @@ if env["builtin_libtheora"]:
# "analyze.c",
# "apiwrapper.c",
"bitpack.c",
- "cpu.c",
+ # "collect.c",
# "decapiwrapper.c",
"decinfo.c",
"decode.c",
@@ -47,8 +47,12 @@ if env["builtin_libtheora"]:
"x86/mmxfrag.c",
"x86/mmxidct.c",
"x86/mmxstate.c",
+ # "x86/sse2encfrag.c",
# "x86/sse2fdct.c",
+ "x86/sse2idct.c",
+ "x86/x86cpu.c",
# "x86/x86enc.c",
+ # "x86/x86enquant.c"
"x86/x86state.c",
]
@@ -58,6 +62,7 @@ if env["builtin_libtheora"]:
"x86_vc/mmxfrag.c",
"x86_vc/mmxidct.c",
"x86_vc/mmxstate.c",
+ "x86_vc/x86cpu.c",
# "x86_vc/x86enc.c",
"x86_vc/x86state.c",
]
diff --git a/modules/theora/config.py b/modules/theora/config.py
index 7f354a8fda..9a27e8e132 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,7 +1,8 @@
def can_build(env, platform):
if env["arch"].startswith("rv"):
return False
- return env.module_check_dependencies("theora", ["ogg", "vorbis"])
+ env.module_add_dependencies("theora", ["ogg", "vorbis"])
+ return True
def configure(env):
diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml
index 0f2dece8e7..e07af8f169 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -18,7 +18,7 @@
</method>
<method name="set_file">
<return type="void" />
- <argument index="0" name="file" type="String" />
+ <param index="0" name="file" type="String" />
<description>
Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension.
</description>
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index 9ed8a86415..0be2982e38 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h
index 2529b09306..93addcbf04 100644
--- a/modules/theora/register_types.h
+++ b/modules/theora/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 THEORA_REGISTER_TYPES_H
#define THEORA_REGISTER_TYPES_H
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index c4462ba687..c600924a3e 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -1,40 +1,49 @@
-/*************************************************************************/
-/* video_stream_theora.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* video_stream_theora.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "video_stream_theora.h"
#include "core/config/project_settings.h"
#include "core/os/os.h"
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4127)
+#endif
+
#include "thirdparty/misc/yuv2rgb.h"
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
int VideoStreamPlaybackTheora::buffer_data() {
char *buffer = ogg_sync_buffer(&oy, 4096);
@@ -91,8 +100,6 @@ void VideoStreamPlaybackTheora::video_write() {
uint8_t *w = frame_data.ptrw();
char *dst = (char *)w;
- //uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y/2);
-
if (px_fmt == TH_PF_444) {
yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2);
@@ -101,7 +108,7 @@ void VideoStreamPlaybackTheora::video_write() {
} else if (px_fmt == TH_PF_420) {
yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2);
- };
+ }
format = Image::FORMAT_RGBA8;
}
@@ -123,7 +130,7 @@ void VideoStreamPlaybackTheora::clear() {
if (vorbis_p >= 3) {
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
- };
+ }
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
vorbis_p = 0;
@@ -154,7 +161,7 @@ void VideoStreamPlaybackTheora::clear() {
file.unref();
playing = false;
-};
+}
void VideoStreamPlaybackTheora::set_file(const String &p_file) {
ERR_FAIL_COND(playing);
@@ -174,7 +181,6 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
ring_buffer.write(read_buffer.ptr(), read);
thread.start(_streaming_thread, this);
-
#endif
ogg_sync_init(&oy);
@@ -245,10 +251,9 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
/* we're expecting more header packets. */
while ((theora_p && theora_p < 3) || (vorbis_p && vorbis_p < 3)) {
- int ret;
-
/* look for further theora headers */
- while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) {
+ int ret = ogg_stream_packetout(&to, &op);
+ while (theora_p && theora_p < 3 && ret) {
if (ret < 0) {
fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n");
clear();
@@ -259,11 +264,13 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
clear();
return;
}
+ ret = ogg_stream_packetout(&to, &op);
theora_p++;
}
/* look for more vorbis header packets */
- while (vorbis_p && (vorbis_p < 3) && (ret = ogg_stream_packetout(&vo, &op))) {
+ ret = ogg_stream_packetout(&vo, &op);
+ while (vorbis_p && vorbis_p < 3 && ret) {
if (ret < 0) {
fprintf(stderr, "Error parsing Vorbis stream headers; corrupt stream?\n");
clear();
@@ -279,6 +286,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
if (vorbis_p == 3) {
break;
}
+ ret = ogg_stream_packetout(&vo, &op);
}
/* The header pages/packets will arrive before anything else we
@@ -328,9 +336,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
size.x = w;
size.y = h;
- Ref<Image> img;
- img.instantiate();
- img->create(w, h, false, Image::FORMAT_RGBA8);
+ Ref<Image> img = Image::create_empty(w, h, false, Image::FORMAT_RGBA8);
texture->set_image(img);
} else {
@@ -355,20 +361,20 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
buffering = true;
time = 0;
audio_frames_wrote = 0;
-};
+}
-float VideoStreamPlaybackTheora::get_time() const {
+double VideoStreamPlaybackTheora::get_time() const {
// FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to
// systematically return 0. Now that it gives a proper latency, it broke this
// code where the delay compensation likely never really worked.
return time - /* AudioServer::get_singleton()->get_output_latency() - */ delay_compensation;
-};
+}
Ref<Texture2D> VideoStreamPlaybackTheora::get_texture() const {
return texture;
}
-void VideoStreamPlaybackTheora::update(float p_delta) {
+void VideoStreamPlaybackTheora::update(double p_delta) {
if (file.is_null()) {
return;
}
@@ -376,7 +382,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
if (!playing || paused) {
//printf("not playing\n");
return;
- };
+ }
#ifdef THEORA_USE_THREAD_STREAMING
thread_sem->post();
@@ -444,7 +450,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
}
} else { /* we need more data; break out to suck in another page */
break;
- };
+ }
}
audio_done = videobuf_time < (audio_frames_wrote / float(vi.rate));
@@ -457,12 +463,6 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
while (theora_p && !frame_done) {
/* theora is one in, one out... */
if (ogg_stream_packetout(&to, &op) > 0) {
- if (false && pp_inc) {
- pp_level += pp_inc;
- th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level,
- sizeof(pp_level));
- pp_inc = 0;
- }
/*HACK: This should be set after a seek or a gap, but we might not have
a granulepos for the first packet (we only have them for the last
packet on a page), so we just set it as often as we get it.
@@ -507,7 +507,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
//printf("video done, stopping\n");
stop();
return;
- };
+ }
if (!frame_done || !audio_done) {
//what's the point of waiting for audio to grab a page?
@@ -529,7 +529,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
//printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
}
- float tdiff = videobuf_time - get_time();
+ double tdiff = videobuf_time - get_time();
/*If we have lots of extra time, increase the post-processing level.*/
if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) {
pp_inc = pp_level < pp_level_max ? 1 : 0;
@@ -539,7 +539,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
}
video_write();
-};
+}
void VideoStreamPlaybackTheora::play() {
if (!playing) {
@@ -549,9 +549,9 @@ void VideoStreamPlaybackTheora::play() {
}
playing = true;
- delay_compensation = ProjectSettings::get_singleton()->get("audio/video/video_delay_compensation_ms");
+ delay_compensation = GLOBAL_GET("audio/video/video_delay_compensation_ms");
delay_compensation /= 1000.0;
-};
+}
void VideoStreamPlaybackTheora::stop() {
if (playing) {
@@ -560,44 +560,44 @@ void VideoStreamPlaybackTheora::stop() {
}
playing = false;
time = 0;
-};
+}
bool VideoStreamPlaybackTheora::is_playing() const {
return playing;
-};
+}
void VideoStreamPlaybackTheora::set_paused(bool p_paused) {
paused = p_paused;
-};
+}
bool VideoStreamPlaybackTheora::is_paused() const {
return paused;
-};
+}
void VideoStreamPlaybackTheora::set_loop(bool p_enable) {
}
bool VideoStreamPlaybackTheora::has_loop() const {
return false;
-};
+}
-float VideoStreamPlaybackTheora::get_length() const {
+double VideoStreamPlaybackTheora::get_length() const {
return 0;
-};
+}
String VideoStreamPlaybackTheora::get_stream_name() const {
return "";
-};
+}
int VideoStreamPlaybackTheora::get_loop_count() const {
return 0;
-};
+}
-float VideoStreamPlaybackTheora::get_playback_position() const {
+double VideoStreamPlaybackTheora::get_playback_position() const {
return get_time();
-};
+}
-void VideoStreamPlaybackTheora::seek(float p_time) {
+void VideoStreamPlaybackTheora::seek(double p_time) {
WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDExtension-provided video streams).");
}
@@ -650,15 +650,14 @@ VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
thread_sem = Semaphore::create();
#endif
-};
+}
VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() {
#ifdef THEORA_USE_THREAD_STREAMING
-
memdelete(thread_sem);
#endif
clear();
-};
+}
void VideoStreamTheora::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file);
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 00d799dc24..f047440df7 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* video_stream_theora.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* video_stream_theora.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 VIDEO_STREAM_THEORA_H
#define VIDEO_STREAM_THEORA_H
@@ -64,7 +64,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
int buffer_data();
int queue_page(ogg_page *page);
void video_write();
- float get_time() const;
+ double get_time() const;
bool theora_eos = false;
bool vorbis_eos = false;
@@ -76,7 +76,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
th_info ti;
th_comment tc;
th_dec_ctx *td = nullptr;
- vorbis_info vi;
+ vorbis_info vi = {};
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_comment vc;
@@ -136,19 +136,19 @@ public:
virtual void set_loop(bool p_enable) override;
virtual bool has_loop() const override;
- virtual float get_length() const override;
+ virtual double get_length() const override;
virtual String get_stream_name() const;
virtual int get_loop_count() const;
- virtual float get_playback_position() const override;
- virtual void seek(float p_time) override;
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
void set_file(const String &p_file);
virtual Ref<Texture2D> get_texture() const override;
- virtual void update(float p_delta) override;
+ virtual void update(double p_delta) override;
virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override;
virtual int get_channels() const override;
diff --git a/modules/tinyexr/config.py b/modules/tinyexr/config.py
index 53b8f2f2e3..eb565b85b9 100644
--- a/modules/tinyexr/config.py
+++ b/modules/tinyexr/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env["tools"]
+ return env.editor_build
def configure(env):
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 864df765ee..f64bb14e4a 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_tinyexr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_tinyexr.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_tinyexr.h"
@@ -37,7 +37,7 @@
#include "thirdparty/tinyexr/tinyexr.h"
-Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, 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);
@@ -229,7 +229,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool
color.a = *a_channel++;
}
- if (p_force_linear) {
+ if (p_flags & FLAG_FORCE_LINEAR) {
color = color.srgb_to_linear();
}
@@ -260,7 +260,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool
color.a = *a_channel++;
}
- if (p_force_linear) {
+ if (p_flags & FLAG_FORCE_LINEAR) {
color = color.srgb_to_linear();
}
@@ -280,7 +280,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool
}
}
- p_image->create(exr_image.width, exr_image.height, false, format, imgdata);
+ p_image->set_data(exr_image.width, exr_image.height, false, format, imgdata);
FreeEXRHeader(&exr_header);
FreeEXRImage(&exr_image);
diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h
index 0d3de93956..0b0f2f7479 100644
--- a/modules/tinyexr/image_loader_tinyexr.h
+++ b/modules/tinyexr/image_loader_tinyexr.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_tinyexr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_tinyexr.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_TINYEXR_H
#define IMAGE_LOADER_TINYEXR_H
@@ -35,7 +35,7 @@
class ImageLoaderTinyEXR : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderTinyEXR();
};
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 661fe343af..315de9b268 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_saver_tinyexr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_saver_tinyexr.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_saver_tinyexr.h"
#include "core/math/math_funcs.h"
diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h
index 8f97f55408..058eeae58e 100644
--- a/modules/tinyexr/image_saver_tinyexr.h
+++ b/modules/tinyexr/image_saver_tinyexr.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_saver_tinyexr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_saver_tinyexr.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_SAVER_TINYEXR_H
#define IMAGE_SAVER_TINYEXR_H
diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp
index c5897f37c3..13a9295f8d 100644
--- a/modules/tinyexr/register_types.cpp
+++ b/modules/tinyexr/register_types.cpp
@@ -1,46 +1,46 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_tinyexr.h"
#include "image_saver_tinyexr.h"
-static ImageLoaderTinyEXR *image_loader_tinyexr = nullptr;
+static Ref<ImageLoaderTinyEXR> image_loader_tinyexr;
void initialize_tinyexr_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_tinyexr = memnew(ImageLoaderTinyEXR);
+ image_loader_tinyexr.instantiate();
ImageLoader::add_image_format_loader(image_loader_tinyexr);
Image::save_exr_func = save_exr;
@@ -52,7 +52,8 @@ void uninitialize_tinyexr_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_tinyexr);
+ ImageLoader::remove_image_format_loader(image_loader_tinyexr);
+ image_loader_tinyexr.unref();
Image::save_exr_func = nullptr;
}
diff --git a/modules/tinyexr/register_types.h b/modules/tinyexr/register_types.h
index bb50e024cb..7873c5b238 100644
--- a/modules/tinyexr/register_types.h
+++ b/modules/tinyexr/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TINYEXR_REGISTER_TYPES_H
#define TINYEXR_REGISTER_TYPES_H
diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
index 066506922c..92e25efbe0 100644
--- a/modules/upnp/doc_classes/UPNP.xml
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -1,16 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="UPNP" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- UPNP network functions.
+ Universal Plug and Play (UPnP) functions for network device discovery, querying and port forwarding.
</brief_description>
<description>
- Provides UPNP functionality to discover [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (port forwarding) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
- To forward a specific port:
+ This class can be used to discover compatible [UPNPDevice]s on the local network and execute commands on them, like managing port mappings (for port forwarding/NAT traversal) and querying the local and remote network IP address. Note that methods on this class are synchronous and block the calling thread.
+ To forward a specific port (here [code]7777[/code], note both [method discover] and [method add_port_mapping] can return errors that should be checked):
[codeblock]
- const PORT = 7777
var upnp = UPNP.new()
- upnp.discover(2000, 2, "InternetGatewayDevice")
- upnp.add_port_mapping(port)
+ upnp.discover()
+ upnp.add_port_mapping(7777)
[/codeblock]
To close a specific port (e.g. after you have finished using it):
[codeblock]
@@ -21,7 +20,7 @@
# Emitted when UPnP port mapping setup is completed (regardless of success or failure).
signal upnp_completed(error)
- # Replace this with your own server port number between 1025 and 65535.
+ # Replace this with your own server port number between 1024 and 65535.
const SERVER_PORT = 3928
var thread = null
@@ -42,34 +41,44 @@
func _ready():
thread = Thread.new()
- thread.start(self, "_upnp_setup", SERVER_PORT)
+ thread.start(_upnp_setup.bind(SERVER_PORT))
func _exit_tree():
# Wait for thread finish here to handle game exit while the thread is running.
thread.wait_to_finish()
[/codeblock]
+ [b]Terminology:[/b] In the context of UPnP networking, "gateway" (or "internet gateway device", short IGD) refers to network devices that allow computers in the local network to access the internet ("wide area network", WAN). These gateways are often also called "routers".
+ [b]Pitfalls:[/b]
+ - As explained above, these calls are blocking and shouldn't be run on the main thread, especially as they can block for multiple seconds at a time. Use threading!
+ - Networking is physical and messy. Packets get lost in transit or get filtered, addresses, free ports and assigned mappings change, and devices may leave or join the network at any time. Be mindful of this, be diligent when checking and handling errors, and handle these gracefully if you can: add clear error UI, timeouts and re-try handling.
+ - Port mappings may change (and be removed) at any time, and the remote/external IP address of the gateway can change likewise. You should consider re-querying the external IP and try to update/refresh the port mapping periodically (for example, every 5 minutes and on networking failures).
+ - Not all devices support UPnP, and some users disable UPnP support. You need to handle this (e.g. documenting and requiring the user to manually forward ports, or adding alternative methods of NAT traversal, like a relay/mirror server, or NAT hole punching, STUN/TURN, etc.).
+ - Consider what happens on mapping conflicts. Maybe multiple users on the same network would like to play your game at the same time, or maybe another application uses the same port. Make the port configurable, and optimally choose a port automatically (re-trying with a different port on failure).
+ [b]Further reading:[/b] If you want to know more about UPnP (and the Internet Gateway Device (IGD) and Port Control Protocol (PCP) specifically), [url=https://en.wikipedia.org/wiki/Universal_Plug_and_Play]Wikipedia[/url] is a good first stop, the specification can be found at the [url=https://openconnectivity.org/developer/specifications/upnp-resources/upnp/]Open Connectivity Foundation[/url] and Godot's implementation is based on the [url=https://github.com/miniupnp/miniupnp]MiniUPnP client[/url].
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_device">
<return type="void" />
- <argument index="0" name="device" type="UPNPDevice" />
+ <param index="0" name="device" type="UPNPDevice" />
<description>
Adds the given [UPNPDevice] to the list of discovered devices.
</description>
</method>
<method name="add_port_mapping" qualifiers="const">
<return type="int" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="port_internal" type="int" default="0" />
- <argument index="2" name="desc" type="String" default="&quot;&quot;" />
- <argument index="3" name="proto" type="String" default="&quot;UDP&quot;" />
- <argument index="4" name="duration" type="int" default="0" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="port_internal" type="int" default="0" />
+ <param index="2" name="desc" type="String" default="&quot;&quot;" />
+ <param index="3" name="proto" type="String" default="&quot;UDP&quot;" />
+ <param index="4" name="duration" type="int" default="0" />
<description>
- Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any.
+ Adds a mapping to forward the external [code]port[/code] (between 1 and 65535, although recommended to use port 1024 or above) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]"TCP"[/code] or [code]"UDP"[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. Note that forwarding a well-known port (below 1024) with UPnP may fail depending on the device.
+ Depending on the gateway device, if a mapping for that port already exists, it will either be updated or it will refuse this command due to that conflict, especially if the existing mapping for that port wasn't created via UPnP or points to a different network address (or device) than this one.
If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
- The description ([code]desc[/code]) is shown in some router UIs and can be used to point out which application added the mapping. The mapping's lease duration can be limited by specifying a [code]duration[/code] (in seconds). However, some routers are incompatible with one or both of these, so use with caution and add fallback logic in case of errors to retry without them if in doubt.
+ The description ([code]desc[/code]) is shown in some routers management UIs and can be used to point out which application added the mapping.
+ The mapping's lease [code]duration[/code] can be limited by specifying a duration in seconds. The default of [code]0[/code] means no duration, i.e. a permanent lease and notably some devices only support these permanent leases. Note that whether permanent or not, this is only a request and the gateway may still decide at any point to remove the mapping (which usually happens on a reboot of the gateway, when its external IP address changes, or on some models when it detects a port mapping has become inactive, i.e. had no traffic for multiple minutes). If not [code]0[/code] (permanent), the allowed range according to spec is between [code]120[/code] (2 minutes) and [code]86400[/code] seconds (24 hours).
See [enum UPNPResult] for possible return values.
</description>
</method>
@@ -81,17 +90,17 @@
</method>
<method name="delete_port_mapping" qualifiers="const">
<return type="int" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="proto" type="String" default="&quot;UDP&quot;" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="proto" type="String" default="&quot;UDP&quot;" />
<description>
- Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values.
+ Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]"TCP"[/code] or [code]"UDP"[/code]. May be refused for mappings pointing to addresses other than this one, for well-known ports (below 1024), or for mappings not added via UPnP. See [enum UPNPResult] for possible return values.
</description>
</method>
<method name="discover">
<return type="int" />
- <argument index="0" name="timeout" type="int" default="2000" />
- <argument index="1" name="ttl" type="int" default="2" />
- <argument index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;" />
+ <param index="0" name="timeout" type="int" default="2000" />
+ <param index="1" name="ttl" type="int" default="2" />
+ <param index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;" />
<description>
Discovers local [UPNPDevice]s. Clears the list of previously discovered devices.
Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in milliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing.
@@ -100,7 +109,7 @@
</method>
<method name="get_device" qualifiers="const">
<return type="UPNPDevice" />
- <argument index="0" name="index" type="int" />
+ <param index="0" name="index" type="int" />
<description>
Returns the [UPNPDevice] at the given [code]index[/code].
</description>
@@ -125,15 +134,15 @@
</method>
<method name="remove_device">
<return type="void" />
- <argument index="0" name="index" type="int" />
+ <param index="0" name="index" type="int" />
<description>
Removes the device at [code]index[/code] from the list of discovered devices.
</description>
</method>
<method name="set_device">
<return type="void" />
- <argument index="0" name="index" type="int" />
- <argument index="1" name="device" type="UPNPDevice" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="device" type="UPNPDevice" />
<description>
Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code].
</description>
diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml
index 7749ac18ab..538ca72391 100644
--- a/modules/upnp/doc_classes/UPNPDevice.xml
+++ b/modules/upnp/doc_classes/UPNPDevice.xml
@@ -1,29 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="UPNPDevice" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- UPNP device.
+ Universal Plug and Play (UPnP) device.
</brief_description>
<description>
- UPNP device. See [UPNP] for UPNP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
+ Universal Plug and Play (UPnP) device. See [UPNP] for UPnP discovery and utility functions. Provides low-level access to UPNP control commands. Allows to manage port mappings (port forwarding) and to query network information of the device (like local and external IP address and status). Note that methods on this class are synchronous and block the calling thread.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_port_mapping" qualifiers="const">
<return type="int" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="port_internal" type="int" default="0" />
- <argument index="2" name="desc" type="String" default="&quot;&quot;" />
- <argument index="3" name="proto" type="String" default="&quot;UDP&quot;" />
- <argument index="4" name="duration" type="int" default="0" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="port_internal" type="int" default="0" />
+ <param index="2" name="desc" type="String" default="&quot;&quot;" />
+ <param index="3" name="proto" type="String" default="&quot;UDP&quot;" />
+ <param index="4" name="duration" type="int" default="0" />
<description>
Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping].
</description>
</method>
<method name="delete_port_mapping" qualifiers="const">
<return type="int" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="proto" type="String" default="&quot;UDP&quot;" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="proto" type="String" default="&quot;UDP&quot;" />
<description>
Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping].
</description>
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
index 9e041a0c6e..e8b92964fb 100644
--- a/modules/upnp/register_types.cpp
+++ b/modules/upnp/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h
index ef559cc4e8..4e64d9392c 100644
--- a/modules/upnp/register_types.h
+++ b/modules/upnp/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 UPNP_REGISTER_TYPES_H
#define UPNP_REGISTER_TYPES_H
diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp
index d762ca4f09..df7672754b 100644
--- a/modules/upnp/upnp.cpp
+++ b/modules/upnp/upnp.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* upnp.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* upnp.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "upnp.h"
@@ -319,8 +319,6 @@ int UPNP::add_port_mapping(int port, int port_internal, String desc, String prot
return UPNP_RESULT_NO_GATEWAY;
}
- dev->delete_port_mapping(port, proto);
-
return dev->add_port_mapping(port, port_internal, desc, proto, duration);
}
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
index be7ede3ee0..2975e1474e 100644
--- a/modules/upnp/upnp.h
+++ b/modules/upnp/upnp.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* upnp.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* upnp.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 UPNP_H
#define UPNP_H
diff --git a/modules/upnp/upnp_device.cpp b/modules/upnp/upnp_device.cpp
index 4009a399f2..11ee3681af 100644
--- a/modules/upnp/upnp_device.cpp
+++ b/modules/upnp/upnp_device.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* upnp_device.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* upnp_device.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "upnp_device.h"
diff --git a/modules/upnp/upnp_device.h b/modules/upnp/upnp_device.h
index 1c9dc82df5..a49e574890 100644
--- a/modules/upnp/upnp_device.h
+++ b/modules/upnp/upnp_device.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* upnp_device.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* upnp_device.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 UPNP_DEVICE_H
#define UPNP_DEVICE_H
diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp
index 8bb7e780de..6310becf1b 100644
--- a/modules/vhacd/register_types.cpp
+++ b/modules/vhacd/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "scene/resources/mesh.h"
diff --git a/modules/vhacd/register_types.h b/modules/vhacd/register_types.h
index 04ec180cd6..786dc1cf53 100644
--- a/modules/vhacd/register_types.h
+++ b/modules/vhacd/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 VHACD_REGISTER_TYPES_H
#define VHACD_REGISTER_TYPES_H
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
deleted file mode 100644
index b91cceae09..0000000000
--- a/modules/visual_script/SCsub
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_vs = env_modules.Clone()
-
-env_vs.add_source_files(env.modules_sources, "*.cpp")
-
-if env["tools"]:
- env_vs.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
deleted file mode 100644
index e8990c43c8..0000000000
--- a/modules/visual_script/config.py
+++ /dev/null
@@ -1,63 +0,0 @@
-def can_build(env, platform):
- return True
-
-
-def configure(env):
- pass
-
-
-def get_doc_classes():
- return [
- "VisualScriptBasicTypeConstant",
- "VisualScriptBuiltinFunc",
- "VisualScriptClassConstant",
- "VisualScriptComment",
- "VisualScriptComposeArray",
- "VisualScriptCondition",
- "VisualScriptConstant",
- "VisualScriptConstructor",
- "VisualScriptCustomNode",
- "VisualScriptCustomNodes",
- "VisualScriptDeconstruct",
- "VisualScriptEditor",
- "VisualScriptEmitSignal",
- "VisualScriptEngineSingleton",
- "VisualScriptExpression",
- "VisualScriptFunctionCall",
- "VisualScriptFunctionState",
- "VisualScriptFunction",
- "VisualScriptGlobalConstant",
- "VisualScriptIndexGet",
- "VisualScriptIndexSet",
- "VisualScriptInputAction",
- "VisualScriptIterator",
- "VisualScriptLists",
- "VisualScriptLocalVarSet",
- "VisualScriptLocalVar",
- "VisualScriptMathConstant",
- "VisualScriptNode",
- "VisualScriptOperator",
- "VisualScriptPreload",
- "VisualScriptPropertyGet",
- "VisualScriptPropertySet",
- "VisualScriptResourcePath",
- "VisualScriptReturn",
- "VisualScriptSceneNode",
- "VisualScriptSceneTree",
- "VisualScriptSelect",
- "VisualScriptSelf",
- "VisualScriptSequence",
- "VisualScriptSubCall",
- "VisualScriptSwitch",
- "VisualScriptTypeCast",
- "VisualScriptVariableGet",
- "VisualScriptVariableSet",
- "VisualScriptWhile",
- "VisualScript",
- "VisualScriptYieldSignal",
- "VisualScriptYield",
- ]
-
-
-def get_doc_path():
- return "doc_classes"
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
deleted file mode 100644
index 5807c98d32..0000000000
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ /dev/null
@@ -1,357 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScript" inherits="Script" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A script implemented in the Visual Script programming environment.
- </brief_description>
- <description>
- A script implemented in the Visual Script programming environment. The script extends the functionality of all objects that instance it.
- [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
- You are most likely to use this class via the Visual Script editor or when writing plugins for it.
- </description>
- <tutorials>
- <link title="VisualScript documentation index">$DOCS_URL/tutorials/scripting/visual_script/index.html</link>
- </tutorials>
- <methods>
- <method name="add_custom_signal">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Add a custom signal with the specified name to the VisualScript.
- </description>
- </method>
- <method name="add_function">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="func_node_id" type="int" />
- <description>
- Add a function with the specified name to the VisualScript, and assign the root [VisualScriptFunction] node's id as [code]func_node_id[/code].
- </description>
- </method>
- <method name="add_node">
- <return type="void" />
- <argument index="0" name="id" type="int" />
- <argument index="1" name="node" type="VisualScriptNode" />
- <argument index="2" name="position" type="Vector2" default="Vector2(0, 0)" />
- <description>
- Add a node to the VisualScript.
- </description>
- </method>
- <method name="add_variable">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="default_value" type="Variant" default="null" />
- <argument index="2" name="export" type="bool" default="false" />
- <description>
- Add a variable to the VisualScript, optionally giving it a default value or marking it as exported.
- </description>
- </method>
- <method name="custom_signal_add_argument">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="type" type="int" enum="Variant.Type" />
- <argument index="2" name="argname" type="String" />
- <argument index="3" name="index" type="int" default="-1" />
- <description>
- Add an argument to a custom signal added with [method add_custom_signal].
- </description>
- </method>
- <method name="custom_signal_get_argument_count" qualifiers="const">
- <return type="int" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Get the count of a custom signal's arguments.
- </description>
- </method>
- <method name="custom_signal_get_argument_name" qualifiers="const">
- <return type="String" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <description>
- Get the name of a custom signal's argument.
- </description>
- </method>
- <method name="custom_signal_get_argument_type" qualifiers="const">
- <return type="int" enum="Variant.Type" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <description>
- Get the type of a custom signal's argument.
- </description>
- </method>
- <method name="custom_signal_remove_argument">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <description>
- Remove a specific custom signal's argument.
- </description>
- </method>
- <method name="custom_signal_set_argument_name">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <argument index="2" name="argname" type="String" />
- <description>
- Rename a custom signal's argument.
- </description>
- </method>
- <method name="custom_signal_set_argument_type">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <argument index="2" name="type" type="int" enum="Variant.Type" />
- <description>
- Change the type of a custom signal's argument.
- </description>
- </method>
- <method name="custom_signal_swap_argument">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="argidx" type="int" />
- <argument index="2" name="withidx" type="int" />
- <description>
- Swap two of the arguments of a custom signal.
- </description>
- </method>
- <method name="data_connect">
- <return type="void" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_port" type="int" />
- <argument index="2" name="to_node" type="int" />
- <argument index="3" name="to_port" type="int" />
- <description>
- Connect two data ports. The value of [code]from_node[/code]'s [code]from_port[/code] would be fed into [code]to_node[/code]'s [code]to_port[/code].
- </description>
- </method>
- <method name="data_disconnect">
- <return type="void" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_port" type="int" />
- <argument index="2" name="to_node" type="int" />
- <argument index="3" name="to_port" type="int" />
- <description>
- Disconnect two data ports previously connected with [method data_connect].
- </description>
- </method>
- <method name="get_function_node_id" qualifiers="const">
- <return type="int" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns the id of a function's entry point node.
- </description>
- </method>
- <method name="get_node" qualifiers="const">
- <return type="VisualScriptNode" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns a node given its id.
- </description>
- </method>
- <method name="get_node_position" qualifiers="const">
- <return type="Vector2" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns a node's position in pixels.
- </description>
- </method>
- <method name="get_scroll" qualifiers="const">
- <return type="Vector2" />
- <description>
- Returns the current position of the center of the screen.
- </description>
- </method>
- <method name="get_variable_default_value" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns the default (initial) value of a variable.
- </description>
- </method>
- <method name="get_variable_export" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns whether a variable is exported.
- </description>
- </method>
- <method name="get_variable_info" qualifiers="const">
- <return type="Dictionary" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns the information for a given variable as a dictionary. The information includes its name, type, hint and usage.
- </description>
- </method>
- <method name="has_custom_signal" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns whether a signal exists with the specified name.
- </description>
- </method>
- <method name="has_data_connection" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_port" type="int" />
- <argument index="2" name="to_node" type="int" />
- <argument index="3" name="to_port" type="int" />
- <description>
- Returns whether the specified data ports are connected.
- </description>
- </method>
- <method name="has_function" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns whether a function exists with the specified name.
- </description>
- </method>
- <method name="has_node" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns whether a node exists with the given id.
- </description>
- </method>
- <method name="has_sequence_connection" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_output" type="int" />
- <argument index="2" name="to_node" type="int" />
- <description>
- Returns whether the specified sequence ports are connected.
- </description>
- </method>
- <method name="has_variable" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Returns whether a variable exists with the specified name.
- </description>
- </method>
- <method name="remove_custom_signal">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Remove a custom signal with the given name.
- </description>
- </method>
- <method name="remove_function">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Remove a specific function and its nodes from the script.
- </description>
- </method>
- <method name="remove_node">
- <return type="void" />
- <argument index="0" name="id" type="int" />
- <description>
- Remove the node with the specified id.
- </description>
- </method>
- <method name="remove_variable">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <description>
- Remove a variable with the given name.
- </description>
- </method>
- <method name="rename_custom_signal">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="new_name" type="StringName" />
- <description>
- Change the name of a custom signal.
- </description>
- </method>
- <method name="rename_function">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="new_name" type="StringName" />
- <description>
- Change the name of a function.
- </description>
- </method>
- <method name="rename_variable">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="new_name" type="StringName" />
- <description>
- Change the name of a variable.
- </description>
- </method>
- <method name="sequence_connect">
- <return type="void" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_output" type="int" />
- <argument index="2" name="to_node" type="int" />
- <description>
- Connect two sequence ports. The execution will flow from of [code]from_node[/code]'s [code]from_output[/code] into [code]to_node[/code].
- Unlike [method data_connect], there isn't a [code]to_port[/code], since the target node can have only one sequence port.
- </description>
- </method>
- <method name="sequence_disconnect">
- <return type="void" />
- <argument index="0" name="from_node" type="int" />
- <argument index="1" name="from_output" type="int" />
- <argument index="2" name="to_node" type="int" />
- <description>
- Disconnect two sequence ports previously connected with [method sequence_connect].
- </description>
- </method>
- <method name="set_instance_base_type">
- <return type="void" />
- <argument index="0" name="type" type="StringName" />
- <description>
- Set the base type of the script.
- </description>
- </method>
- <method name="set_node_position">
- <return type="void" />
- <argument index="0" name="id" type="int" />
- <argument index="1" name="position" type="Vector2" />
- <description>
- Set the node position in the VisualScript graph.
- </description>
- </method>
- <method name="set_scroll">
- <return type="void" />
- <argument index="0" name="offset" type="Vector2" />
- <description>
- Set the screen center to the given position.
- </description>
- </method>
- <method name="set_variable_default_value">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="value" type="Variant" />
- <description>
- Change the default (initial) value of a variable.
- </description>
- </method>
- <method name="set_variable_export">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="enable" type="bool" />
- <description>
- Change whether a variable is exported.
- </description>
- </method>
- <method name="set_variable_info">
- <return type="void" />
- <argument index="0" name="name" type="StringName" />
- <argument index="1" name="value" type="Dictionary" />
- <description>
- Set a variable's info, using the same format as [method get_variable_info].
- </description>
- </method>
- </methods>
- <signals>
- <signal name="node_ports_changed">
- <argument index="0" name="id" type="int" />
- <description>
- Emitted when the ports of a node are changed.
- </description>
- </signal>
- </signals>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
deleted file mode 100644
index 0ed66f44e1..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptBasicTypeConstant" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node representing a constant from the base types.
- </brief_description>
- <description>
- A Visual Script node representing a constant from base types, such as [constant Vector3.AXIS_X].
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type" default="0">
- The type to get the constant from.
- </member>
- <member name="constant" type="StringName" setter="set_basic_type_constant" getter="get_basic_type_constant">
- The name of the constant to return.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
deleted file mode 100644
index 647b627d25..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ /dev/null
@@ -1,221 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptBuiltinFunc" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node used to call built-in functions.
- </brief_description>
- <description>
- A built-in function used inside a [VisualScript]. It is usually a math function or an utility function.
- See also [@GDScript], for the same functions in the GDScript language.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="function" type="int" setter="set_func" getter="get_func" enum="VisualScriptBuiltinFunc.BuiltinFunc" default="0">
- The function to be executed.
- </member>
- </members>
- <constants>
- <constant name="MATH_SIN" value="0" enum="BuiltinFunc">
- Returns the sine of the input.
- </constant>
- <constant name="MATH_COS" value="1" enum="BuiltinFunc">
- Returns the cosine of the input.
- </constant>
- <constant name="MATH_TAN" value="2" enum="BuiltinFunc">
- Returns the tangent of the input.
- </constant>
- <constant name="MATH_SINH" value="3" enum="BuiltinFunc">
- Returns the hyperbolic sine of the input.
- </constant>
- <constant name="MATH_COSH" value="4" enum="BuiltinFunc">
- Returns the hyperbolic cosine of the input.
- </constant>
- <constant name="MATH_TANH" value="5" enum="BuiltinFunc">
- Returns the hyperbolic tangent of the input.
- </constant>
- <constant name="MATH_ASIN" value="6" enum="BuiltinFunc">
- Returns the arc sine of the input.
- </constant>
- <constant name="MATH_ACOS" value="7" enum="BuiltinFunc">
- Returns the arc cosine of the input.
- </constant>
- <constant name="MATH_ATAN" value="8" enum="BuiltinFunc">
- Returns the arc tangent of the input.
- </constant>
- <constant name="MATH_ATAN2" value="9" enum="BuiltinFunc">
- Returns the arc tangent of the input, using the signs of both parameters to determine the exact angle.
- </constant>
- <constant name="MATH_SQRT" value="10" enum="BuiltinFunc">
- Returns the square root of the input.
- </constant>
- <constant name="MATH_FMOD" value="11" enum="BuiltinFunc">
- Returns the remainder of one input divided by the other, using floating-point numbers.
- </constant>
- <constant name="MATH_FPOSMOD" value="12" enum="BuiltinFunc">
- Returns the positive remainder of one input divided by the other, using floating-point numbers.
- </constant>
- <constant name="MATH_FLOOR" value="13" enum="BuiltinFunc">
- Returns the input rounded down.
- </constant>
- <constant name="MATH_CEIL" value="14" enum="BuiltinFunc">
- Returns the input rounded up.
- </constant>
- <constant name="MATH_ROUND" value="15" enum="BuiltinFunc">
- Returns the input rounded to the nearest integer.
- </constant>
- <constant name="MATH_ABS" value="16" enum="BuiltinFunc">
- Returns the absolute value of the input.
- </constant>
- <constant name="MATH_SIGN" value="17" enum="BuiltinFunc">
- Returns the sign of the input, turning it into 1, -1, or 0. Useful to determine if the input is positive or negative.
- </constant>
- <constant name="MATH_POW" value="18" enum="BuiltinFunc">
- Returns the input raised to a given power.
- </constant>
- <constant name="MATH_LOG" value="19" enum="BuiltinFunc">
- Returns the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
- </constant>
- <constant name="MATH_EXP" value="20" enum="BuiltinFunc">
- Returns the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
- </constant>
- <constant name="MATH_ISNAN" value="21" enum="BuiltinFunc">
- Returns whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
- </constant>
- <constant name="MATH_ISINF" value="22" enum="BuiltinFunc">
- Returns whether the input is an infinite floating-point number or not. Infinity is usually produced by dividing a number by 0, though other ways exist.
- </constant>
- <constant name="MATH_EASE" value="23" enum="BuiltinFunc">
- Easing function, based on exponent. 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
- </constant>
- <constant name="MATH_STEP_DECIMALS" value="24" enum="BuiltinFunc">
- Returns the number of digit places after the decimal that the first non-zero digit occurs.
- </constant>
- <constant name="MATH_SNAPPED" value="25" enum="BuiltinFunc">
- Returns the input snapped to a given step.
- </constant>
- <constant name="MATH_LERP" value="26" enum="BuiltinFunc">
- Returns a number linearly interpolated between the first two inputs, based on the third input. Uses the formula [code]a + (a - b) * t[/code].
- </constant>
- <constant name="MATH_CUBIC_INTERPOLATE" value="27" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_INVERSE_LERP" value="28" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_RANGE_LERP" value="29" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_MOVE_TOWARD" value="30" enum="BuiltinFunc">
- Moves the number toward a value, based on the third input.
- </constant>
- <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc">
- Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
- </constant>
- <constant name="MATH_RANDI" value="32" enum="BuiltinFunc">
- Returns a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
- </constant>
- <constant name="MATH_RANDF" value="33" enum="BuiltinFunc">
- Returns a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
- </constant>
- <constant name="MATH_RANDI_RANGE" value="34" enum="BuiltinFunc">
- Returns a random 32-bit integer value between the two inputs.
- </constant>
- <constant name="MATH_RANDF_RANGE" value="35" enum="BuiltinFunc">
- Returns a random floating-point value between the two inputs.
- </constant>
- <constant name="MATH_RANDFN" value="36" enum="BuiltinFunc">
- Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified mean and a standard deviation. This is also called Gaussian distribution.
- </constant>
- <constant name="MATH_SEED" value="37" enum="BuiltinFunc">
- Set the seed for the random number generator.
- </constant>
- <constant name="MATH_RANDSEED" value="38" enum="BuiltinFunc">
- Returns a random value from the given seed, along with the new seed.
- </constant>
- <constant name="MATH_DEG2RAD" value="39" enum="BuiltinFunc">
- Convert the input from degrees to radians.
- </constant>
- <constant name="MATH_RAD2DEG" value="40" enum="BuiltinFunc">
- Convert the input from radians to degrees.
- </constant>
- <constant name="MATH_LINEAR2DB" value="41" enum="BuiltinFunc">
- Convert the input from linear volume to decibel volume.
- </constant>
- <constant name="MATH_DB2LINEAR" value="42" enum="BuiltinFunc">
- Convert the input from decibel volume to linear volume.
- </constant>
- <constant name="MATH_WRAP" value="43" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_PINGPONG" value="45" enum="BuiltinFunc">
- Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
- </constant>
- <constant name="LOGIC_MAX" value="46" enum="BuiltinFunc">
- Returns the greater of the two numbers, also known as their maximum.
- </constant>
- <constant name="LOGIC_MIN" value="47" enum="BuiltinFunc">
- Returns the lesser of the two numbers, also known as their minimum.
- </constant>
- <constant name="LOGIC_CLAMP" value="48" enum="BuiltinFunc">
- Returns the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
- </constant>
- <constant name="LOGIC_NEAREST_PO2" value="49" enum="BuiltinFunc">
- Returns the nearest power of 2 to the input.
- </constant>
- <constant name="OBJ_WEAKREF" value="50" enum="BuiltinFunc">
- Create a [WeakRef] from the input.
- </constant>
- <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc">
- Convert between types.
- </constant>
- <constant name="TYPE_OF" value="52" enum="BuiltinFunc">
- Returns the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
- </constant>
- <constant name="TYPE_EXISTS" value="53" enum="BuiltinFunc">
- Checks if a type is registered in the [ClassDB].
- </constant>
- <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc">
- Returns a character with the given ascii value.
- </constant>
- <constant name="TEXT_STR" value="55" enum="BuiltinFunc">
- Convert the input to a string.
- </constant>
- <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc">
- Print the given string to the output window.
- </constant>
- <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc">
- Print the given string to the standard error output.
- </constant>
- <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc">
- Print the given string to the standard output, without adding a newline.
- </constant>
- <constant name="TEXT_PRINT_VERBOSE" value="59" enum="BuiltinFunc">
- </constant>
- <constant name="VAR_TO_STR" value="60" enum="BuiltinFunc">
- Serialize a [Variant] to a string.
- </constant>
- <constant name="STR_TO_VAR" value="61" enum="BuiltinFunc">
- Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR].
- </constant>
- <constant name="VAR_TO_BYTES" value="62" enum="BuiltinFunc">
- Serialize a [Variant] to a [PackedByteArray].
- </constant>
- <constant name="BYTES_TO_VAR" value="63" enum="BuiltinFunc">
- Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES].
- </constant>
- <constant name="MATH_SMOOTHSTEP" value="64" enum="BuiltinFunc">
- Returns a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
- [codeblock]
- var t = clamp((weight - from) / (to - from), 0.0, 1.0)
- return t * t * (3.0 - 2.0 * t)
- [/codeblock]
- </constant>
- <constant name="MATH_POSMOD" value="65" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_LERP_ANGLE" value="66" enum="BuiltinFunc">
- </constant>
- <constant name="TEXT_ORD" value="67" enum="BuiltinFunc">
- </constant>
- <constant name="FUNC_MAX" value="68" enum="BuiltinFunc">
- Represents the size of the [enum BuiltinFunc] enum.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
deleted file mode 100644
index 2509084f0e..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptClassConstant" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Gets a constant from a given class.
- </brief_description>
- <description>
- This node returns a constant from a given class, such as [constant TYPE_INT]. See the given class' documentation for available constants.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (variant): [code]value[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The constant's parent class.
- </member>
- <member name="constant" type="StringName" setter="set_class_constant" getter="get_class_constant" default="&amp;&quot;&quot;">
- The constant to return. See the given class for its available constants.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml
deleted file mode 100644
index cf4b57ca19..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptComment.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptComment" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node used to annotate the script.
- </brief_description>
- <description>
- A Visual Script node used to display annotations in the script, so that code may be documented.
- Comment nodes can be resized so they encompass a group of nodes.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="description" type="String" setter="set_description" getter="get_description" default="&quot;&quot;">
- The text inside the comment node.
- </member>
- <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(150, 150)">
- The comment node's size (in pixels).
- </member>
- <member name="title" type="String" setter="set_title" getter="get_title" default="&quot;Comment&quot;">
- The comment node's title.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml
deleted file mode 100644
index ea73867c4b..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptComposeArray" inherits="VisualScriptLists" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script Node used to create array from a list of items.
- </brief_description>
- <description>
- A Visual Script Node used to compose array from the list of elements provided with custom in-graph UI hard coded in the VisualScript Editor.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml
deleted file mode 100644
index a29388569f..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptCondition.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptCondition" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node which branches the flow.
- </brief_description>
- <description>
- A Visual Script node that checks a [bool] input port. If [code]true[/code], it will exit via the "true" sequence port. If [code]false[/code], it will exit via the "false" sequence port. After exiting either, it exits via the "done" port. Sequence ports may be left disconnected.
- [b]Input Ports:[/b]
- - Sequence: [code]if (cond) is[/code]
- - Data (boolean): [code]cond[/code]
- [b]Output Ports:[/b]
- - Sequence: [code]true[/code]
- - Sequence: [code]false[/code]
- - Sequence: [code]done[/code]
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml
deleted file mode 100644
index 645ede9001..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptConstant.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptConstant" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Gets a contant's value.
- </brief_description>
- <description>
- This node returns a constant's value.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (variant): [code]get[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="type" type="int" setter="set_constant_type" getter="get_constant_type" enum="Variant.Type" default="0">
- The constant's type.
- </member>
- <member name="value" type="Variant" setter="set_constant_value" getter="get_constant_value">
- The constant's value.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
deleted file mode 100644
index 5ec17350bd..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptConstructor" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node which calls a base type constructor.
- </brief_description>
- <description>
- A Visual Script node which calls a base type constructor. It can be used for type conversion as well.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_constructor" qualifiers="const">
- <return type="Dictionary" />
- <description>
- </description>
- </method>
- <method name="get_constructor_type" qualifiers="const">
- <return type="int" enum="Variant.Type" />
- <description>
- </description>
- </method>
- <method name="set_constructor">
- <return type="void" />
- <argument index="0" name="constructor" type="Dictionary" />
- <description>
- </description>
- </method>
- <method name="set_constructor_type">
- <return type="void" />
- <argument index="0" name="type" type="int" enum="Variant.Type" />
- <description>
- </description>
- </method>
- </methods>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
deleted file mode 100644
index 97b89fb987..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ /dev/null
@@ -1,166 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptCustomNode" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A scripted Visual Script node.
- </brief_description>
- <description>
- A custom Visual Script node which can be scripted in powerful ways.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="_get_caption" qualifiers="virtual const">
- <return type="String" />
- <description>
- Returns the node's title.
- </description>
- </method>
- <method name="_get_category" qualifiers="virtual const">
- <return type="String" />
- <description>
- Returns the node's category.
- </description>
- </method>
- <method name="_get_input_value_port_count" qualifiers="virtual const">
- <return type="int" />
- <description>
- Returns the count of input value ports.
- </description>
- </method>
- <method name="_get_input_value_port_hint" qualifiers="virtual const">
- <return type="int" />
- <argument index="0" name="input_idx" type="int" />
- <description>
- Returns the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints.
- </description>
- </method>
- <method name="_get_input_value_port_hint_string" qualifiers="virtual const">
- <return type="String" />
- <argument index="0" name="input_idx" type="int" />
- <description>
- Returns the specified input port's hint string.
- </description>
- </method>
- <method name="_get_input_value_port_name" qualifiers="virtual const">
- <return type="String" />
- <argument index="0" name="input_idx" type="int" />
- <description>
- Returns the specified input port's name.
- </description>
- </method>
- <method name="_get_input_value_port_type" qualifiers="virtual const">
- <return type="int" />
- <argument index="0" name="input_idx" type="int" />
- <description>
- Returns the specified input port's type. See the [enum Variant.Type] values.
- </description>
- </method>
- <method name="_get_output_sequence_port_count" qualifiers="virtual const">
- <return type="int" />
- <description>
- Returns the amount of output [b]sequence[/b] ports.
- </description>
- </method>
- <method name="_get_output_sequence_port_text" qualifiers="virtual const">
- <return type="String" />
- <argument index="0" name="seq_idx" type="int" />
- <description>
- Returns the specified [b]sequence[/b] output's name.
- </description>
- </method>
- <method name="_get_output_value_port_count" qualifiers="virtual const">
- <return type="int" />
- <description>
- Returns the amount of output value ports.
- </description>
- </method>
- <method name="_get_output_value_port_hint" qualifiers="virtual const">
- <return type="int" />
- <argument index="0" name="output_idx" type="int" />
- <description>
- Returns the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints.
- </description>
- </method>
- <method name="_get_output_value_port_hint_string" qualifiers="virtual const">
- <return type="String" />
- <argument index="0" name="output_idx" type="int" />
- <description>
- Returns the specified output port's hint string.
- </description>
- </method>
- <method name="_get_output_value_port_name" qualifiers="virtual const">
- <return type="String" />
- <argument index="0" name="output_idx" type="int" />
- <description>
- Returns the specified output port's name.
- </description>
- </method>
- <method name="_get_output_value_port_type" qualifiers="virtual const">
- <return type="int" />
- <argument index="0" name="output_idx" type="int" />
- <description>
- Returns the specified output port's type. See the [enum Variant.Type] values.
- </description>
- </method>
- <method name="_get_text" qualifiers="virtual const">
- <return type="String" />
- <description>
- Returns the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it).
- </description>
- </method>
- <method name="_get_working_memory_size" qualifiers="virtual const">
- <return type="int" />
- <description>
- Returns the size of the custom node's working memory. See [method _step] for more details.
- </description>
- </method>
- <method name="_has_input_sequence_port" qualifiers="virtual const">
- <return type="bool" />
- <description>
- Returns whether the custom node has an input [b]sequence[/b] port.
- </description>
- </method>
- <method name="_step" qualifiers="virtual const">
- <return type="Variant" />
- <argument index="0" name="inputs" type="Array" />
- <argument index="1" name="outputs" type="Array" />
- <argument index="2" name="start_mode" type="int" />
- <argument index="3" name="working_mem" type="Array" />
- <description>
- Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error.
- The [code]inputs[/code] array contains the values of the input ports.
- [code]outputs[/code] is an array whose indices should be set to the respective outputs.
- The [code]start_mode[/code] is usually [constant START_MODE_BEGIN_SEQUENCE], unless you have used the [code]STEP_*[/code] constants.
- [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. The size needs to be predefined using [method _get_working_memory_size].
- When returning, you can mask the returned value with one of the [code]STEP_*[/code] constants.
- </description>
- </method>
- </methods>
- <constants>
- <constant name="START_MODE_BEGIN_SEQUENCE" value="0" enum="StartMode">
- The start mode used the first time when [method _step] is called.
- </constant>
- <constant name="START_MODE_CONTINUE_SEQUENCE" value="1" enum="StartMode">
- The start mode used when [method _step] is called after coming back from a [constant STEP_PUSH_STACK_BIT].
- </constant>
- <constant name="START_MODE_RESUME_YIELD" value="2" enum="StartMode">
- The start mode used when [method _step] is called after resuming from [constant STEP_YIELD_BIT].
- </constant>
- <constant name="STEP_PUSH_STACK_BIT" value="16777216">
- Hint used by [method _step] to tell that control should return to it when there is no other node left to execute.
- This is used by [VisualScriptCondition] to redirect the sequence to the "Done" port after the [code]true[/code]/[code]false[/code] branch has finished execution.
- </constant>
- <constant name="STEP_GO_BACK_BIT" value="33554432">
- Hint used by [method _step] to tell that control should return back, either hitting a previous [constant STEP_PUSH_STACK_BIT] or exiting the function.
- </constant>
- <constant name="STEP_NO_ADVANCE_BIT" value="67108864">
- </constant>
- <constant name="STEP_EXIT_FUNCTION_BIT" value="134217728">
- Hint used by [method _step] to tell that control should stop and exit the function.
- </constant>
- <constant name="STEP_YIELD_BIT" value="268435456">
- Hint used by [method _step] to tell that the function should be yielded.
- Using this requires you to have at least one working memory slot, which is used for the [VisualScriptFunctionState].
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
deleted file mode 100644
index f04c862174..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptCustomNodes" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Manages custom nodes for the Visual Script editor.
- </brief_description>
- <description>
- This singleton can be used to manage (i.e., add or remove) custom nodes for the Visual Script editor.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="add_custom_node">
- <return type="void" />
- <argument index="0" name="name" type="String" />
- <argument index="1" name="category" type="String" />
- <argument index="2" name="script" type="Script" />
- <description>
- Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter.
- </description>
- </method>
- <method name="remove_custom_node">
- <return type="void" />
- <argument index="0" name="name" type="String" />
- <argument index="1" name="category" type="String" />
- <description>
- Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed.
- </description>
- </method>
- </methods>
- <signals>
- <signal name="custom_nodes_updated">
- <description>
- Emitted when a custom Visual Script node is added or removed.
- </description>
- </signal>
- </signals>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
deleted file mode 100644
index b544fd9d90..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptDeconstruct" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node which deconstructs a base type instance into its parts.
- </brief_description>
- <description>
- A Visual Script node which deconstructs a base type instance into its parts.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="type" type="int" setter="set_deconstruct_type" getter="get_deconstruct_type" enum="Variant.Type" default="0">
- The type to deconstruct.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
deleted file mode 100644
index c0cefa0ab7..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEmitSignal" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Emits a specified signal.
- </brief_description>
- <description>
- Emits a specified signal when it is executed.
- [b]Input Ports:[/b]
- - Sequence: [code]emit[/code]
- [b]Output Ports:[/b]
- - Sequence
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="signal" type="StringName" setter="set_signal" getter="get_signal" default="&amp;&quot;&quot;">
- The signal to emit.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
deleted file mode 100644
index f60a048845..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEngineSingleton" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node returning a singleton from [@GlobalScope].
- </brief_description>
- <description>
- A Visual Script node returning a singleton from [@GlobalScope].
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="constant" type="String" setter="set_singleton" getter="get_singleton" default="&quot;&quot;">
- The singleton's name.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml
deleted file mode 100644
index 14750e7c8d..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptExpression.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptExpression" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node that can execute a custom expression.
- </brief_description>
- <description>
- A Visual Script node that can execute a custom expression. Values can be provided for the input and the expression result can be retrieved from the output.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml
deleted file mode 100644
index 74d9f194eb..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptFunction.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunction" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node representing a function.
- </brief_description>
- <description>
- [VisualScriptFunction] represents a function header. It is the starting point for the function body and can be used to tweak the function's properties (e.g. RPC mode).
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
deleted file mode 100644
index 543263ff8e..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node for calling a function.
- </brief_description>
- <description>
- [VisualScriptFunctionCall] is created when you add or drag and drop a function onto the Visual Script graph. It allows to tweak parameters of the call, e.g. what object the function is called on.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="base_script" type="String" setter="set_base_script" getter="get_base_script">
- The script to be used when [member call_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The base type to be used when [member call_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
- The type to be used when [member call_mode] is set to [constant CALL_MODE_BASIC_TYPE].
- </member>
- <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptFunctionCall.CallMode" default="0">
- [code]call_mode[/code] determines the target object on which the method will be called. See [enum CallMode] for options.
- </member>
- <member name="function" type="StringName" setter="set_function" getter="get_function" default="&amp;&quot;&quot;">
- The name of the function to be called.
- </member>
- <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
- The node path to use when [member call_mode] is set to [constant CALL_MODE_NODE_PATH].
- </member>
- <member name="rpc_call_mode" type="int" setter="set_rpc_call_mode" getter="get_rpc_call_mode" enum="VisualScriptFunctionCall.RPCCallMode" default="0">
- The mode for RPC calls. See [method Node.rpc] for more details and [enum RPCCallMode] for available options.
- </member>
- <member name="singleton" type="StringName" setter="set_singleton" getter="get_singleton">
- The singleton to call the method on. Used when [member call_mode] is set to [constant CALL_MODE_SINGLETON].
- </member>
- <member name="use_default_args" type="int" setter="set_use_default_args" getter="get_use_default_args">
- Number of default arguments that will be used when calling the function. Can't be higher than the number of available default arguments in the method's declaration.
- </member>
- <member name="validate" type="bool" setter="set_validate" getter="get_validate" default="true">
- If [code]false[/code], call errors (e.g. wrong number of arguments) will be ignored.
- </member>
- </members>
- <constants>
- <constant name="CALL_MODE_SELF" value="0" enum="CallMode">
- The method will be called on this [Object].
- </constant>
- <constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
- The method will be called on the given [Node] in the scene tree.
- </constant>
- <constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
- The method will be called on an instanced node with the given type and script.
- </constant>
- <constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
- The method will be called on a GDScript basic type (e.g. [Vector2]).
- </constant>
- <constant name="CALL_MODE_SINGLETON" value="4" enum="CallMode">
- The method will be called on a singleton.
- </constant>
- <constant name="RPC_DISABLED" value="0" enum="RPCCallMode">
- The method will be called locally.
- </constant>
- <constant name="RPC_RELIABLE" value="1" enum="RPCCallMode">
- The method will be called remotely.
- </constant>
- <constant name="RPC_UNRELIABLE" value="2" enum="RPCCallMode">
- The method will be called remotely using an unreliable protocol.
- </constant>
- <constant name="RPC_RELIABLE_TO_ID" value="3" enum="RPCCallMode">
- The method will be called remotely for the given peer.
- </constant>
- <constant name="RPC_UNRELIABLE_TO_ID" value="4" enum="RPCCallMode">
- The method will be called remotely for the given peer, using an unreliable protocol.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
deleted file mode 100644
index ef09c9d4a0..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunctionState" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node representing a function state.
- </brief_description>
- <description>
- [VisualScriptFunctionState] is returned from [VisualScriptYield] and can be used to resume a paused function call.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="connect_to_signal">
- <return type="void" />
- <argument index="0" name="obj" type="Object" />
- <argument index="1" name="signals" type="String" />
- <argument index="2" name="args" type="Array" />
- <description>
- Connects this [VisualScriptFunctionState] to a signal in the given object to automatically resume when it's emitted.
- </description>
- </method>
- <method name="is_valid" qualifiers="const">
- <return type="bool" />
- <description>
- Returns whether the function state is valid.
- </description>
- </method>
- <method name="resume">
- <return type="Variant" />
- <argument index="0" name="args" type="Array" default="[]" />
- <description>
- Resumes the function to run from the point it was yielded.
- </description>
- </method>
- </methods>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
deleted file mode 100644
index 42ada99257..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node returning a constant from [@GlobalScope].
- </brief_description>
- <description>
- A Visual Script node returning a constant from [@GlobalScope].
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="constant" type="int" setter="set_global_constant" getter="get_global_constant" default="0">
- The constant to be used.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
deleted file mode 100644
index 8828bf9039..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIndexGet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node for getting a value from an array or a dictionary.
- </brief_description>
- <description>
- [VisualScriptIndexGet] will return the value stored in an array or a dictionary under the given index.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
deleted file mode 100644
index 5c81dcd339..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIndexSet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node for setting a value in an array or a dictionary.
- </brief_description>
- <description>
- [VisualScriptIndexSet] will set the value stored in an array or a dictionary under the given index to the provided new value.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
deleted file mode 100644
index 51c2eaf353..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptInputAction" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node returning a state of an action.
- </brief_description>
- <description>
- [VisualScriptInputAction] can be used to check if an action is pressed or released.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="action" type="StringName" setter="set_action_name" getter="get_action_name" default="&amp;&quot;&quot;">
- Name of the action.
- </member>
- <member name="mode" type="int" setter="set_action_mode" getter="get_action_mode" enum="VisualScriptInputAction.Mode" default="0">
- State of the action to check. See [enum Mode] for options.
- </member>
- </members>
- <constants>
- <constant name="MODE_PRESSED" value="0" enum="Mode">
- [code]True[/code] if action is pressed.
- </constant>
- <constant name="MODE_RELEASED" value="1" enum="Mode">
- [code]True[/code] if action is released (i.e. not pressed).
- </constant>
- <constant name="MODE_JUST_PRESSED" value="2" enum="Mode">
- [code]True[/code] on the frame the action was pressed.
- </constant>
- <constant name="MODE_JUST_RELEASED" value="3" enum="Mode">
- [code]True[/code] on the frame the action was released.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml
deleted file mode 100644
index ef6846deba..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptIterator.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptIterator" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Steps through items in a given input.
- </brief_description>
- <description>
- This node steps through each item in a given input. Input can be any sequence data type, such as an [Array] or [String]. When each item has been processed, execution passed out the [code]exit[/code] Sequence port.
- [b]Input Ports:[/b]
- - Sequence: [code]for (elem) in (input)[/code]
- - Data (variant): [code]input[/code]
- [b]Output Ports:[/b]
- - Sequence: [code]each[/code]
- - Sequence: [code]exit[/code]
- - Data (variant): [code]elem[/code]
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLists.xml b/modules/visual_script/doc_classes/VisualScriptLists.xml
deleted file mode 100644
index 27a81fce2f..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptLists.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptLists" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script virtual class for in-graph editable nodes.
- </brief_description>
- <description>
- A Visual Script virtual class that defines the shape and the default behavior of the nodes that have to be in-graph editable nodes.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="add_input_data_port">
- <return type="void" />
- <argument index="0" name="type" type="int" enum="Variant.Type" />
- <argument index="1" name="name" type="String" />
- <argument index="2" name="index" type="int" />
- <description>
- Adds an input port to the Visual Script node.
- </description>
- </method>
- <method name="add_output_data_port">
- <return type="void" />
- <argument index="0" name="type" type="int" enum="Variant.Type" />
- <argument index="1" name="name" type="String" />
- <argument index="2" name="index" type="int" />
- <description>
- Adds an output port to the Visual Script node.
- </description>
- </method>
- <method name="remove_input_data_port">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <description>
- Removes an input port from the Visual Script node.
- </description>
- </method>
- <method name="remove_output_data_port">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <description>
- Removes an output port from the Visual Script node.
- </description>
- </method>
- <method name="set_input_data_port_name">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <argument index="1" name="name" type="String" />
- <description>
- Sets the name of an input port.
- </description>
- </method>
- <method name="set_input_data_port_type">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <argument index="1" name="type" type="int" enum="Variant.Type" />
- <description>
- Sets the type of an input port.
- </description>
- </method>
- <method name="set_output_data_port_name">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <argument index="1" name="name" type="String" />
- <description>
- Sets the name of an output port.
- </description>
- </method>
- <method name="set_output_data_port_type">
- <return type="void" />
- <argument index="0" name="index" type="int" />
- <argument index="1" name="type" type="int" enum="Variant.Type" />
- <description>
- Sets the type of an output port.
- </description>
- </method>
- </methods>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
deleted file mode 100644
index dbf9049f0a..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptLocalVar" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Gets a local variable's value.
- </brief_description>
- <description>
- Returns a local variable's value. "Var Name" must be supplied, with an optional type.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (variant): [code]get[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0">
- The local variable's type.
- </member>
- <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="&amp;&quot;new_local&quot;">
- The local variable's name.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
deleted file mode 100644
index 1ae4e20f97..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptLocalVarSet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Changes a local variable's value.
- </brief_description>
- <description>
- Changes a local variable's value to the given input. The new value is also provided on an output Data port.
- [b]Input Ports:[/b]
- - Sequence
- - Data (variant): [code]set[/code]
- [b]Output Ports:[/b]
- - Sequence
- - Data (variant): [code]get[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0">
- The local variable's type.
- </member>
- <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="&amp;&quot;new_local&quot;">
- The local variable's name.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
deleted file mode 100644
index 01c36e763b..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptMathConstant" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Commonly used mathematical constants.
- </brief_description>
- <description>
- Provides common math constants, such as Pi, on an output Data port.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (variant): [code]get[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="constant" type="int" setter="set_math_constant" getter="get_math_constant" enum="VisualScriptMathConstant.MathConstant" default="0">
- The math constant.
- </member>
- </members>
- <constants>
- <constant name="MATH_CONSTANT_ONE" value="0" enum="MathConstant">
- Unity: [code]1[/code].
- </constant>
- <constant name="MATH_CONSTANT_PI" value="1" enum="MathConstant">
- Pi: [code]3.141593[/code].
- </constant>
- <constant name="MATH_CONSTANT_HALF_PI" value="2" enum="MathConstant">
- Pi divided by two: [code]1.570796[/code].
- </constant>
- <constant name="MATH_CONSTANT_TAU" value="3" enum="MathConstant">
- Tau: [code]6.283185[/code].
- </constant>
- <constant name="MATH_CONSTANT_E" value="4" enum="MathConstant">
- Mathematical constant [code]e[/code], the natural log base: [code]2.718282[/code].
- </constant>
- <constant name="MATH_CONSTANT_SQRT2" value="5" enum="MathConstant">
- Square root of two: [code]1.414214[/code].
- </constant>
- <constant name="MATH_CONSTANT_INF" value="6" enum="MathConstant">
- Infinity: [code]inf[/code].
- </constant>
- <constant name="MATH_CONSTANT_NAN" value="7" enum="MathConstant">
- Not a number: [code]nan[/code].
- </constant>
- <constant name="MATH_CONSTANT_MAX" value="8" enum="MathConstant">
- Represents the size of the [enum MathConstant] enum.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml
deleted file mode 100644
index 2eb99dc25f..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptNode.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptNode" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A node which is part of a [VisualScript].
- </brief_description>
- <description>
- A node which is part of a [VisualScript]. Not to be confused with [Node], which is a part of a [SceneTree].
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_default_input_value" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="port_idx" type="int" />
- <description>
- Returns the default value of a given port. The default value is used when nothing is connected to the port.
- </description>
- </method>
- <method name="get_visual_script" qualifiers="const">
- <return type="VisualScript" />
- <description>
- Returns the [VisualScript] instance the node is bound to.
- </description>
- </method>
- <method name="ports_changed_notify">
- <return type="void" />
- <description>
- Notify that the node's ports have changed. Usually used in conjunction with [VisualScriptCustomNode] .
- </description>
- </method>
- <method name="set_default_input_value">
- <return type="void" />
- <argument index="0" name="port_idx" type="int" />
- <argument index="1" name="value" type="Variant" />
- <description>
- Change the default value of a given port.
- </description>
- </method>
- </methods>
- <signals>
- <signal name="ports_changed">
- <description>
- Emitted when the available input/output ports are changed.
- </description>
- </signal>
- </signals>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml
deleted file mode 100644
index 47ca6ddb90..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptOperator.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptOperator" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node that performs an operation on two values.
- </brief_description>
- <description>
- [b]Input Ports:[/b]
- - Data (variant): [code]A[/code]
- - Data (variant): [code]B[/code]
- [b]Output Ports:[/b]
- - Data (variant): [code]result[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="operator" type="int" setter="set_operator" getter="get_operator" enum="Variant.Operator" default="6">
- The operation to be performed. See [enum Variant.Operator] for available options.
- </member>
- <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0">
- The type of the values for this operation. See [enum Variant.Type] for available options.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml
deleted file mode 100644
index 146d6cd9c3..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptPreload.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPreload" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Creates a new [Resource] or loads one from the filesystem.
- </brief_description>
- <description>
- Creates a new [Resource] or loads one from the filesystem.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (object): [code]res[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="resource" type="Resource" setter="set_preload" getter="get_preload">
- The [Resource] to load.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
deleted file mode 100644
index 77cd6393a9..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node returning a value of a property from an [Object].
- </brief_description>
- <description>
- [VisualScriptPropertyGet] can return a value of any property from the current object or other objects.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="base_script" type="String" setter="set_base_script" getter="get_base_script">
- The script to be used when [member set_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The base type to be used when [member set_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
- The type to be used when [member set_mode] is set to [constant CALL_MODE_BASIC_TYPE].
- </member>
- <member name="index" type="StringName" setter="set_index" getter="get_index">
- The indexed name of the property to retrieve. See [method Object.get_indexed] for details.
- </member>
- <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
- The node path to use when [member set_mode] is set to [constant CALL_MODE_NODE_PATH].
- </member>
- <member name="property" type="StringName" setter="set_property" getter="get_property" default="&amp;&quot;&quot;">
- The name of the property to retrieve. Changing this will clear [member index].
- </member>
- <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertyGet.CallMode" default="0">
- [code]set_mode[/code] determines the target object from which the property will be retrieved. See [enum CallMode] for options.
- </member>
- </members>
- <constants>
- <constant name="CALL_MODE_SELF" value="0" enum="CallMode">
- The property will be retrieved from this [Object].
- </constant>
- <constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
- The property will be retrieved from the given [Node] in the scene tree.
- </constant>
- <constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
- The property will be retrieved from an instanced node with the given type and script.
- </constant>
- <constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
- The property will be retrieved from a GDScript basic type (e.g. [Vector2]).
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
deleted file mode 100644
index 6cffa328c7..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptPropertySet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node that sets a property of an [Object].
- </brief_description>
- <description>
- [VisualScriptPropertySet] can set the value of any property from the current object or other objects.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="assign_op" type="int" setter="set_assign_op" getter="get_assign_op" enum="VisualScriptPropertySet.AssignOp" default="0">
- The additional operation to perform when assigning. See [enum AssignOp] for options.
- </member>
- <member name="base_script" type="String" setter="set_base_script" getter="get_base_script">
- The script to be used when [member set_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The base type to be used when [member set_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
- The type to be used when [member set_mode] is set to [constant CALL_MODE_BASIC_TYPE].
- </member>
- <member name="index" type="StringName" setter="set_index" getter="get_index">
- The indexed name of the property to set. See [method Object.set_indexed] for details.
- </member>
- <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
- The node path to use when [member set_mode] is set to [constant CALL_MODE_NODE_PATH].
- </member>
- <member name="property" type="StringName" setter="set_property" getter="get_property" default="&amp;&quot;&quot;">
- The name of the property to set. Changing this will clear [member index].
- </member>
- <member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertySet.CallMode" default="0">
- [code]set_mode[/code] determines the target object on which the property will be set. See [enum CallMode] for options.
- </member>
- </members>
- <constants>
- <constant name="CALL_MODE_SELF" value="0" enum="CallMode">
- The property will be set on this [Object].
- </constant>
- <constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
- The property will be set on the given [Node] in the scene tree.
- </constant>
- <constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
- The property will be set on an instanced node with the given type and script.
- </constant>
- <constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
- The property will be set on a GDScript basic type (e.g. [Vector2]).
- </constant>
- <constant name="ASSIGN_OP_NONE" value="0" enum="AssignOp">
- The property will be assigned regularly.
- </constant>
- <constant name="ASSIGN_OP_ADD" value="1" enum="AssignOp">
- The value will be added to the property. Equivalent of doing [code]+=[/code].
- </constant>
- <constant name="ASSIGN_OP_SUB" value="2" enum="AssignOp">
- The value will be subtracted from the property. Equivalent of doing [code]-=[/code].
- </constant>
- <constant name="ASSIGN_OP_MUL" value="3" enum="AssignOp">
- The property will be multiplied by the value. Equivalent of doing [code]*=[/code].
- </constant>
- <constant name="ASSIGN_OP_DIV" value="4" enum="AssignOp">
- The property will be divided by the value. Equivalent of doing [code]/=[/code].
- </constant>
- <constant name="ASSIGN_OP_MOD" value="5" enum="AssignOp">
- A modulo operation will be performed on the property and the value. Equivalent of doing [code]%=[/code].
- </constant>
- <constant name="ASSIGN_OP_SHIFT_LEFT" value="6" enum="AssignOp">
- The property will be binarly shifted to the left by the given value. Equivalent of doing [code]&lt;&lt;[/code].
- </constant>
- <constant name="ASSIGN_OP_SHIFT_RIGHT" value="7" enum="AssignOp">
- The property will be binarly shifted to the right by the given value. Equivalent of doing [code]&gt;&gt;[/code].
- </constant>
- <constant name="ASSIGN_OP_BIT_AND" value="8" enum="AssignOp">
- A binary [code]AND[/code] operation will be performed on the property. Equivalent of doing [code]&amp;=[/code].
- </constant>
- <constant name="ASSIGN_OP_BIT_OR" value="9" enum="AssignOp">
- A binary [code]OR[/code] operation will be performed on the property. Equivalent of doing [code]|=[/code].
- </constant>
- <constant name="ASSIGN_OP_BIT_XOR" value="10" enum="AssignOp">
- A binary [code]XOR[/code] operation will be performed on the property. Equivalent of doing [code]^=[/code].
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
deleted file mode 100644
index 6ca8260ade..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptResourcePath" inherits="VisualScriptNode" 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>
- <members>
- <member name="path" type="String" setter="set_resource_path" getter="get_resource_path" default="&quot;&quot;">
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml
deleted file mode 100644
index 1d59392782..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptReturn.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptReturn" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Exits a function and returns an optional value.
- </brief_description>
- <description>
- Ends the execution of a function and returns control to the calling function. Optionally, it can return a [Variant] value.
- [b]Input Ports:[/b]
- - Sequence
- - Data (variant): [code]result[/code] (optional)
- [b]Output Ports:[/b]
- none
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="return_enabled" type="bool" setter="set_enable_return_value" getter="is_return_value_enabled" default="false">
- If [code]true[/code], the [code]return[/code] input port is available.
- </member>
- <member name="return_type" type="int" setter="set_return_type" getter="get_return_type" enum="Variant.Type" default="0">
- The return value's data type.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
deleted file mode 100644
index a769d11d94..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSceneNode" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Node reference.
- </brief_description>
- <description>
- A direct reference to a node.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data: [code]node[/code] (obj)
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="node_path" type="NodePath" setter="set_node_path" getter="get_node_path" default="NodePath(&quot;.&quot;)">
- The node's path in the scene tree.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
deleted file mode 100644
index 84ab90892f..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSceneTree" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node for accessing [SceneTree] methods.
- </brief_description>
- <description>
- A Visual Script node for accessing [SceneTree] methods.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml
deleted file mode 100644
index 1aa916f779..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSelect.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSelect" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Chooses between two input values.
- </brief_description>
- <description>
- Chooses between two input values based on a Boolean condition.
- [b]Input Ports:[/b]
- - Data (boolean): [code]cond[/code]
- - Data (variant): [code]a[/code]
- - Data (variant): [code]b[/code]
- [b]Output Ports:[/b]
- - Data (variant): [code]out[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0">
- The input variables' type.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml
deleted file mode 100644
index 8cc59dbccd..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSelf.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSelf" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Outputs a reference to the current instance.
- </brief_description>
- <description>
- Provides a reference to the node running the visual script.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (object): [code]instance[/code]
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml
deleted file mode 100644
index 9adbc30e0d..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSequence.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSequence" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Executes a series of Sequence ports.
- </brief_description>
- <description>
- Steps through a series of one or more output Sequence ports. The [code]current[/code] data port outputs the currently executing item.
- [b]Input Ports:[/b]
- - Sequence: [code]in order[/code]
- [b]Output Ports:[/b]
- - Sequence: [code]1[/code]
- - Sequence: [code]2 - n[/code] (optional)
- - Data (int): [code]current[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="steps" type="int" setter="set_steps" getter="get_steps" default="1">
- The number of steps in the sequence.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
deleted file mode 100644
index 535e89fc82..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSubCall" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Calls a method called [code]_subcall[/code] in this object.
- </brief_description>
- <description>
- [VisualScriptSubCall] will call method named [code]_subcall[/code] in the current script. It will fail if the method doesn't exist or the provided arguments are wrong.
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml
deleted file mode 100644
index 7befe89f50..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptSwitch" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Branches program flow based on a given input's value.
- </brief_description>
- <description>
- Branches the flow based on an input's value. Use [b]Case Count[/b] in the Inspector to set the number of branches and each comparison's optional type.
- [b]Input Ports:[/b]
- - Sequence: [code]'input' is[/code]
- - Data (variant): [code]=[/code]
- - Data (variant): [code]=[/code] (optional)
- - Data (variant): [code]input[/code]
- [b]Output Ports:[/b]
- - Sequence
- - Sequence (optional)
- - Sequence: [code]done[/code]
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
deleted file mode 100644
index ec84a75601..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptTypeCast" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node that casts the given value to another type.
- </brief_description>
- <description>
- [VisualScriptTypeCast] will perform a type conversion to an [Object]-derived type.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="base_script" type="String" setter="set_base_script" getter="get_base_script" default="&quot;&quot;">
- The target script class to be converted to. If none, only the [member base_type] will be used.
- </member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The target type to be converted to.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
deleted file mode 100644
index 8d99b4b9d0..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptVariableGet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Gets a variable's value.
- </brief_description>
- <description>
- Returns a variable's value. "Var Name" must be supplied, with an optional type.
- [b]Input Ports:[/b]
- none
- [b]Output Ports:[/b]
- - Data (variant): [code]value[/code]
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="var_name" type="StringName" setter="set_variable" getter="get_variable" default="&amp;&quot;&quot;">
- The variable's name.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
deleted file mode 100644
index 4f568cc0f6..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptVariableSet" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Changes a variable's value.
- </brief_description>
- <description>
- Changes a variable's value to the given input.
- [b]Input Ports:[/b]
- - Sequence
- - Data (variant): [code]set[/code]
- [b]Output Ports:[/b]
- - Sequence
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="var_name" type="StringName" setter="set_variable" getter="get_variable" default="&amp;&quot;&quot;">
- The variable's name.
- </member>
- </members>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml
deleted file mode 100644
index 4e7cccef17..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptWhile.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptWhile" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- Conditional loop.
- </brief_description>
- <description>
- Loops while a condition is [code]true[/code]. Execution continues out the [code]exit[/code] Sequence port when the loop terminates.
- [b]Input Ports:[/b]
- - Sequence: [code]while(cond)[/code]
- - Data (bool): [code]cond[/code]
- [b]Output Ports:[/b]
- - Sequence: [code]repeat[/code]
- - Sequence: [code]exit[/code]
- </description>
- <tutorials>
- </tutorials>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml
deleted file mode 100644
index ec757a3ac4..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptYield.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptYield" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node used to pause a function execution.
- </brief_description>
- <description>
- [VisualScriptYield] will pause the function call and return [VisualScriptFunctionState], which can be used to resume the function.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="mode" type="int" setter="set_yield_mode" getter="get_yield_mode" enum="VisualScriptYield.YieldMode" default="1">
- The mode to use for yielding. See [enum YieldMode] for available options.
- </member>
- <member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time">
- The time to wait when [member mode] is set to [constant YIELD_WAIT].
- </member>
- </members>
- <constants>
- <constant name="YIELD_FRAME" value="1" enum="YieldMode">
- Yields during an idle frame.
- </constant>
- <constant name="YIELD_PHYSICS_FRAME" value="2" enum="YieldMode">
- Yields during a physics frame.
- </constant>
- <constant name="YIELD_WAIT" value="3" enum="YieldMode">
- Yields a function and waits the given time.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
deleted file mode 100644
index c3f4bc49c5..0000000000
--- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A Visual Script node yielding for a signal.
- </brief_description>
- <description>
- [VisualScriptYieldSignal] will pause the function execution until the provided signal is emitted.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
- The base type to be used when [member call_mode] is set to [constant CALL_MODE_INSTANCE].
- </member>
- <member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptYieldSignal.CallMode" default="0">
- [code]call_mode[/code] determines the target object to wait for the signal emission. See [enum CallMode] for options.
- </member>
- <member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
- The node path to use when [member call_mode] is set to [constant CALL_MODE_NODE_PATH].
- </member>
- <member name="signal" type="StringName" setter="set_signal" getter="get_signal" default="&amp;&quot;&quot;">
- The signal name to be waited for.
- </member>
- </members>
- <constants>
- <constant name="CALL_MODE_SELF" value="0" enum="CallMode">
- A signal from this [Object] will be used.
- </constant>
- <constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
- A signal from the given [Node] in the scene tree will be used.
- </constant>
- <constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
- A signal from an instanced node with the given type will be used.
- </constant>
- </constants>
-</class>
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
deleted file mode 100644
index 1e9755f45f..0000000000
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ /dev/null
@@ -1,4936 +0,0 @@
-/*************************************************************************/
-/* visual_script_editor.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 "visual_script_editor.h"
-
-#include "../visual_script_expression.h"
-#include "../visual_script_flow_control.h"
-#include "../visual_script_func_nodes.h"
-#include "../visual_script_nodes.h"
-#include "core/input/input.h"
-#include "core/object/class_db.h"
-#include "core/object/script_language.h"
-#include "core/os/keyboard.h"
-#include "core/variant/variant.h"
-#include "editor/editor_node.h"
-#include "editor/editor_resource_preview.h"
-#include "editor/editor_scale.h"
-#include "scene/gui/view_panner.h"
-#include "scene/main/window.h"
-
-#ifdef TOOLS_ENABLED
-class VisualScriptEditorSignalEdit : public Object {
- GDCLASS(VisualScriptEditorSignalEdit, Object);
-
- StringName sig;
-
-public:
- UndoRedo *undo_redo;
- Ref<VisualScript> script;
-
-protected:
- static void _bind_methods() {
- ClassDB::bind_method("_sig_changed", &VisualScriptEditorSignalEdit::_sig_changed);
- ADD_SIGNAL(MethodInfo("changed"));
- }
-
- void _sig_changed() {
- notify_property_list_changed();
- emit_signal(SNAME("changed"));
- }
-
- bool _set(const StringName &p_name, const Variant &p_value) {
- if (sig == StringName()) {
- return false;
- }
-
- if (p_name == "argument_count") {
- int new_argc = p_value;
- int argc = script->custom_signal_get_argument_count(sig);
- if (argc == new_argc) {
- return true;
- }
-
- undo_redo->create_action(TTR("Change Signal Arguments"));
-
- if (new_argc < argc) {
- for (int i = new_argc; i < argc; i++) {
- undo_redo->add_do_method(script.ptr(), "custom_signal_remove_argument", sig, new_argc);
- undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", sig, script->custom_signal_get_argument_name(sig, i), script->custom_signal_get_argument_type(sig, i), -1);
- }
- } else if (new_argc > argc) {
- for (int i = argc; i < new_argc; i++) {
- undo_redo->add_do_method(script.ptr(), "custom_signal_add_argument", sig, Variant::NIL, "arg" + itos(i + 1), -1);
- undo_redo->add_undo_method(script.ptr(), "custom_signal_remove_argument", sig, argc);
- }
- }
-
- undo_redo->add_do_method(this, "_sig_changed");
- undo_redo->add_undo_method(this, "_sig_changed");
-
- undo_redo->commit_action();
-
- return true;
- }
- if (String(p_name).begins_with("argument/")) {
- int idx = String(p_name).get_slice("/", 1).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, script->custom_signal_get_argument_count(sig), false);
- String what = String(p_name).get_slice("/", 2);
- if (what == "type") {
- int old_type = script->custom_signal_get_argument_type(sig, idx);
- int new_type = p_value;
- undo_redo->create_action(TTR("Change Argument Type"));
- undo_redo->add_do_method(script.ptr(), "custom_signal_set_argument_type", sig, idx, new_type);
- undo_redo->add_undo_method(script.ptr(), "custom_signal_set_argument_type", sig, idx, old_type);
- undo_redo->commit_action();
-
- return true;
- }
-
- if (what == "name") {
- String old_name = script->custom_signal_get_argument_name(sig, idx);
- String new_name = p_value;
- undo_redo->create_action(TTR("Change Argument name"));
- undo_redo->add_do_method(script.ptr(), "custom_signal_set_argument_name", sig, idx, new_name);
- undo_redo->add_undo_method(script.ptr(), "custom_signal_set_argument_name", sig, idx, old_name);
- undo_redo->commit_action();
- return true;
- }
- }
-
- return false;
- }
-
- bool _get(const StringName &p_name, Variant &r_ret) const {
- if (sig == StringName()) {
- return false;
- }
-
- if (p_name == "argument_count") {
- r_ret = script->custom_signal_get_argument_count(sig);
- return true;
- }
- if (String(p_name).begins_with("argument/")) {
- int idx = String(p_name).get_slice("/", 1).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, script->custom_signal_get_argument_count(sig), false);
- String what = String(p_name).get_slice("/", 2);
- if (what == "type") {
- r_ret = script->custom_signal_get_argument_type(sig, idx);
- return true;
- }
- if (what == "name") {
- r_ret = script->custom_signal_get_argument_name(sig, idx);
- return true;
- }
- }
-
- return false;
- }
- void _get_property_list(List<PropertyInfo> *p_list) const {
- if (sig == StringName()) {
- return;
- }
-
- p_list->push_back(PropertyInfo(Variant::INT, "argument_count", PROPERTY_HINT_RANGE, "0,256"));
- String argt = "Variant";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < script->custom_signal_get_argument_count(sig); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "argument/" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::STRING, "argument/" + itos(i + 1) + "/name"));
- }
- }
-
-public:
- void edit(const StringName &p_sig) {
- sig = p_sig;
- notify_property_list_changed();
- }
-
- VisualScriptEditorSignalEdit() { undo_redo = nullptr; }
-};
-
-class VisualScriptEditorVariableEdit : public Object {
- GDCLASS(VisualScriptEditorVariableEdit, Object);
-
- StringName var;
-
-public:
- UndoRedo *undo_redo;
- Ref<VisualScript> script;
-
-protected:
- static void _bind_methods() {
- ClassDB::bind_method("_var_changed", &VisualScriptEditorVariableEdit::_var_changed);
- ClassDB::bind_method("_var_value_changed", &VisualScriptEditorVariableEdit::_var_value_changed);
- ADD_SIGNAL(MethodInfo("changed"));
- }
-
- void _var_changed() {
- notify_property_list_changed();
- emit_signal(SNAME("changed"));
- }
- void _var_value_changed() {
- emit_signal(SNAME("changed"));
- }
-
- bool _set(const StringName &p_name, const Variant &p_value) {
- if (var == StringName()) {
- return false;
- }
-
- if (String(p_name) == "value") {
- undo_redo->create_action(TTR("Set Variable Default Value"));
- Variant current = script->get_variable_default_value(var);
- undo_redo->add_do_method(script.ptr(), "set_variable_default_value", var, p_value);
- undo_redo->add_undo_method(script.ptr(), "set_variable_default_value", var, current);
- undo_redo->add_do_method(this, "_var_value_changed");
- undo_redo->add_undo_method(this, "_var_value_changed");
- undo_redo->commit_action();
- return true;
- }
-
- Dictionary d = script->call("get_variable_info", var);
-
- if (String(p_name) == "type") {
- Dictionary dc = d.duplicate();
- dc["type"] = p_value;
- undo_redo->create_action(TTR("Set Variable Type"));
- undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
- undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
-
- // Setting the default value.
- Variant::Type type = (Variant::Type)(int)p_value;
- if (type != Variant::NIL) {
- Variant default_value;
- Callable::CallError ce;
- Variant::construct(type, default_value, nullptr, 0, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- undo_redo->add_do_method(script.ptr(), "set_variable_default_value", var, default_value);
- undo_redo->add_undo_method(script.ptr(), "set_variable_default_value", var, dc["value"]);
- }
- }
-
- undo_redo->add_do_method(this, "_var_changed");
- undo_redo->add_undo_method(this, "_var_changed");
- undo_redo->commit_action();
- return true;
- }
-
- if (String(p_name) == "hint") {
- Dictionary dc = d.duplicate();
- dc["hint"] = p_value;
- undo_redo->create_action(TTR("Set Variable Type"));
- undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
- undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
- undo_redo->add_do_method(this, "_var_changed");
- undo_redo->add_undo_method(this, "_var_changed");
- undo_redo->commit_action();
- return true;
- }
-
- if (String(p_name) == "hint_string") {
- Dictionary dc = d.duplicate();
- dc["hint_string"] = p_value;
- undo_redo->create_action(TTR("Set Variable Type"));
- undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
- undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
- undo_redo->add_do_method(this, "_var_changed");
- undo_redo->add_undo_method(this, "_var_changed");
- undo_redo->commit_action();
- return true;
- }
-
- if (String(p_name) == "export") {
- script->set_variable_export(var, p_value);
- InspectorDock::get_inspector_singleton()->update_tree();
- return true;
- }
-
- return false;
- }
-
- bool _get(const StringName &p_name, Variant &r_ret) const {
- if (var == StringName()) {
- return false;
- }
-
- if (String(p_name) == "value") {
- r_ret = script->get_variable_default_value(var);
- return true;
- }
-
- PropertyInfo pinfo = script->get_variable_info(var);
-
- if (String(p_name) == "type") {
- r_ret = pinfo.type;
- return true;
- }
- if (String(p_name) == "hint") {
- r_ret = pinfo.hint;
- return true;
- }
- if (String(p_name) == "hint_string") {
- r_ret = pinfo.hint_string;
- return true;
- }
-
- if (String(p_name) == "export") {
- r_ret = script->get_variable_export(var);
- return true;
- }
-
- return false;
- }
- void _get_property_list(List<PropertyInfo> *p_list) const {
- if (var == StringName()) {
- return;
- }
-
- String argt = "Variant";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
- p_list->push_back(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(script->get_variable_info(var).type, "value", script->get_variable_info(var).hint, script->get_variable_info(var).hint_string, PROPERTY_USAGE_DEFAULT));
- // Update this when PropertyHint changes.
- p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,PlaceholderText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig,NodePathValidTypes"));
- p_list->push_back(PropertyInfo(Variant::STRING, "hint_string"));
- p_list->push_back(PropertyInfo(Variant::BOOL, "export"));
- }
-
-public:
- void edit(const StringName &p_var) {
- var = p_var;
- notify_property_list_changed();
- }
-
- VisualScriptEditorVariableEdit() { undo_redo = nullptr; }
-};
-
-static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
- Color color;
- if (dark_theme) {
- switch (p_type) {
- case Variant::NIL:
- color = Color(0.41, 0.93, 0.74);
- break;
-
- case Variant::BOOL:
- color = Color(0.55, 0.65, 0.94);
- break;
- case Variant::INT:
- color = Color(0.49, 0.78, 0.94);
- break;
- case Variant::FLOAT:
- color = Color(0.38, 0.85, 0.96);
- break;
- case Variant::STRING:
- color = Color(0.42, 0.65, 0.93);
- break;
-
- case Variant::VECTOR2:
- color = Color(0.74, 0.57, 0.95);
- break;
- case Variant::VECTOR2I:
- color = Color(0.74, 0.57, 0.95);
- break;
- case Variant::RECT2:
- color = Color(0.95, 0.57, 0.65);
- break;
- case Variant::RECT2I:
- color = Color(0.95, 0.57, 0.65);
- break;
- case Variant::VECTOR3:
- color = Color(0.84, 0.49, 0.93);
- break;
- 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;
- case Variant::PLANE:
- color = Color(0.97, 0.44, 0.44);
- break;
- case Variant::QUATERNION:
- color = Color(0.93, 0.41, 0.64);
- break;
- case Variant::AABB:
- color = Color(0.93, 0.47, 0.57);
- break;
- case Variant::BASIS:
- color = Color(0.89, 0.93, 0.41);
- break;
- case Variant::TRANSFORM3D:
- color = Color(0.96, 0.66, 0.43);
- break;
-
- case Variant::COLOR:
- color = Color(0.62, 1.0, 0.44);
- break;
- case Variant::NODE_PATH:
- color = Color(0.41, 0.58, 0.93);
- break;
- case Variant::RID:
- color = Color(0.41, 0.93, 0.6);
- break;
- case Variant::OBJECT:
- color = Color(0.47, 0.95, 0.91);
- break;
- case Variant::DICTIONARY:
- color = Color(0.47, 0.93, 0.69);
- break;
-
- case Variant::ARRAY:
- color = Color(0.88, 0.88, 0.88);
- break;
- case Variant::PACKED_BYTE_ARRAY:
- color = Color(0.67, 0.96, 0.78);
- break;
- case Variant::PACKED_INT32_ARRAY:
- color = Color(0.69, 0.86, 0.96);
- break;
- case Variant::PACKED_FLOAT32_ARRAY:
- color = Color(0.59, 0.91, 0.97);
- break;
- case Variant::PACKED_INT64_ARRAY:
- color = Color(0.69, 0.86, 0.96);
- break;
- case Variant::PACKED_FLOAT64_ARRAY:
- color = Color(0.59, 0.91, 0.97);
- break;
- case Variant::PACKED_STRING_ARRAY:
- color = Color(0.62, 0.77, 0.95);
- break;
- case Variant::PACKED_VECTOR2_ARRAY:
- color = Color(0.82, 0.7, 0.96);
- break;
- case Variant::PACKED_VECTOR3_ARRAY:
- color = Color(0.87, 0.61, 0.95);
- break;
- case Variant::PACKED_COLOR_ARRAY:
- color = Color(0.91, 1.0, 0.59);
- break;
-
- default:
- color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.7, 0.7);
- }
- } else {
- switch (p_type) {
- case Variant::NIL:
- color = Color(0.15, 0.89, 0.63);
- break;
-
- case Variant::BOOL:
- color = Color(0.43, 0.56, 0.92);
- break;
- case Variant::INT:
- color = Color(0.31, 0.7, 0.91);
- break;
- case Variant::FLOAT:
- color = Color(0.15, 0.8, 0.94);
- break;
- case Variant::STRING:
- color = Color(0.27, 0.56, 0.91);
- break;
-
- case Variant::VECTOR2:
- color = Color(0.68, 0.46, 0.93);
- break;
- case Variant::VECTOR2I:
- color = Color(0.68, 0.46, 0.93);
- break;
- case Variant::RECT2:
- color = Color(0.93, 0.46, 0.56);
- break;
- case Variant::RECT2I:
- color = Color(0.93, 0.46, 0.56);
- break;
- case Variant::VECTOR3:
- color = Color(0.86, 0.42, 0.93);
- break;
- case Variant::VECTOR3I:
- color = Color(0.86, 0.42, 0.93);
- break;
- case Variant::TRANSFORM2D:
- color = Color(0.59, 0.81, 0.1);
- break;
- case Variant::PLANE:
- color = Color(0.97, 0.44, 0.44);
- break;
- case Variant::QUATERNION:
- color = Color(0.93, 0.41, 0.64);
- break;
- case Variant::AABB:
- color = Color(0.93, 0.47, 0.57);
- break;
- case Variant::BASIS:
- color = Color(0.7, 0.73, 0.1);
- break;
- case Variant::TRANSFORM3D:
- color = Color(0.96, 0.56, 0.28);
- break;
-
- case Variant::COLOR:
- color = Color(0.24, 0.75, 0.0);
- break;
- case Variant::NODE_PATH:
- color = Color(0.41, 0.58, 0.93);
- break;
- case Variant::RID:
- color = Color(0.17, 0.9, 0.45);
- break;
- case Variant::OBJECT:
- color = Color(0.07, 0.84, 0.76);
- break;
- case Variant::DICTIONARY:
- color = Color(0.34, 0.91, 0.62);
- break;
-
- case Variant::ARRAY:
- color = Color(0.45, 0.45, 0.45);
- break;
- case Variant::PACKED_BYTE_ARRAY:
- color = Color(0.38, 0.92, 0.6);
- break;
- case Variant::PACKED_INT32_ARRAY:
- color = Color(0.38, 0.73, 0.92);
- break;
- case Variant::PACKED_FLOAT32_ARRAY:
- color = Color(0.25, 0.83, 0.95);
- break;
- case Variant::PACKED_INT64_ARRAY:
- color = Color(0.38, 0.73, 0.92);
- break;
- case Variant::PACKED_FLOAT64_ARRAY:
- color = Color(0.25, 0.83, 0.95);
- break;
- case Variant::PACKED_STRING_ARRAY:
- color = Color(0.38, 0.62, 0.92);
- break;
- case Variant::PACKED_VECTOR2_ARRAY:
- color = Color(0.62, 0.36, 0.92);
- break;
- case Variant::PACKED_VECTOR3_ARRAY:
- color = Color(0.79, 0.35, 0.92);
- break;
- case Variant::PACKED_COLOR_ARRAY:
- color = Color(0.57, 0.73, 0.0);
- break;
-
- default:
- color.set_hsv(p_type / float(Variant::VARIANT_MAX), 0.3, 0.3);
- }
- }
-
- return color;
-}
-
-void VisualScriptEditor::_update_graph_connections() {
- graph->clear_connections();
-
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(&sequence_conns);
-
- for (const VisualScript::SequenceConnection &E : sequence_conns) {
- graph->connect_node(itos(E.from_node), E.from_output, itos(E.to_node), 0);
- }
-
- List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(&data_conns);
-
- for (VisualScript::DataConnection &dc : data_conns) {
- Ref<VisualScriptNode> from_node = script->get_node(dc.from_node);
- Ref<VisualScriptNode> to_node = script->get_node(dc.to_node);
-
- if (to_node->has_input_sequence_port()) {
- dc.to_port++;
- }
-
- dc.from_port += from_node->get_output_sequence_port_count();
-
- graph->connect_node(itos(dc.from_node), dc.from_port, itos(dc.to_node), dc.to_port);
- }
-}
-
-void VisualScriptEditor::_update_graph(int p_only_id) {
- if (updating_graph) {
- return;
- }
-
- updating_graph = true;
-
- //byebye all nodes
- if (p_only_id >= 0) {
- if (graph->has_node(itos(p_only_id))) {
- Node *gid = graph->get_node(itos(p_only_id));
- if (gid) {
- memdelete(gid);
- }
- }
- } else {
- for (int i = 0; i < graph->get_child_count(); i++) {
- if (Object::cast_to<GraphNode>(graph->get_child(i))) {
- memdelete(graph->get_child(i));
- i--;
- }
- }
- }
- graph->show();
- select_func_text->hide();
-
- Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- Control::get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
- 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);
-
- List<int> ids;
- script->get_node_list(&ids);
-
- for (int &E : ids) {
- if (p_only_id >= 0 && p_only_id != E) {
- continue;
- }
-
- Ref<VisualScriptNode> node = script->get_node(E);
- Vector2 pos = script->get_node_position(E);
-
- GraphNode *gnode = memnew(GraphNode);
- gnode->set_title(node->get_caption());
- gnode->set_position_offset(pos * EDSCALE);
- if (error_line == E) {
- gnode->set_overlay(GraphNode::OVERLAY_POSITION);
- } else if (node->is_breakpoint()) {
- gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT);
- }
-
- gnode->set_meta("__vnode", node);
- gnode->set_name(itos(E));
- 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;
- if (!v.is_valid()) {
- gnode->set_show_close_button(true);
- }
- }
-
- bool has_gnode_text = false;
-
- Ref<VisualScriptLists> nd_list = node;
- bool is_vslist = nd_list.is_valid();
- if (is_vslist) {
- HBoxContainer *hbnc = memnew(HBoxContainer);
- if (nd_list->is_input_port_editable()) {
- has_gnode_text = true;
- 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).bind(E), CONNECT_DEFERRED);
- }
- if (nd_list->is_output_port_editable()) {
- if (nd_list->is_input_port_editable()) {
- hbnc->add_spacer();
- }
- has_gnode_text = true;
- 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).bind(E), CONNECT_DEFERRED);
- }
- gnode->add_child(hbnc);
- } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
- has_gnode_text = true;
- LineEdit *line_edit = memnew(LineEdit);
- line_edit->set_text(node->get_text());
- 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).bind(E));
- } else {
- String text = node->get_text();
- if (!text.is_empty()) {
- has_gnode_text = true;
- Label *label = memnew(Label);
- label->set_text(text);
- gnode->add_child(label);
- }
- }
-
- if (Object::cast_to<VisualScriptComment>(node.ptr())) {
- Ref<VisualScriptComment> vsc = node;
- 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).bind(E));
- }
-
- if (node_styles.has(node->get_category())) {
- Ref<StyleBoxFlat> sbf = node_styles[node->get_category()];
- if (gnode->is_comment()) {
- sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox(SNAME("comment"), SNAME("GraphNode"));
- }
-
- Color c = sbf->get_border_color();
- c = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0, 0.85) : Color(0.0, 0.0, 0.0, 0.85);
- Color ic = c;
- gnode->add_theme_color_override("title_color", c);
- c.a = 1;
- gnode->add_theme_color_override("close_color", c);
- gnode->add_theme_color_override("resizer_color", ic);
- gnode->add_theme_style_override("frame", sbf);
- }
-
- const Color mono_color = get_theme_color(SNAME("mono_color"), SNAME("Editor"));
-
- int slot_idx = 0;
-
- bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
- if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
- // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
- // we still draw the disabled default ones to shift up the slots by one,
- // so the slots DON'T start with the content text.
-
- // IF has_gnode_text is false, but we DO want to draw default sequence ports,
- // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
- if (!has_gnode_text) {
- Label *dummy = memnew(Label);
- dummy->set_text(" ");
- gnode->add_child(dummy);
- }
- gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- slot_idx++;
- }
-
- int mixed_seq_ports = 0;
-
- if (!single_seq_output) {
- if (node->has_mixed_input_and_sequence_ports()) {
- mixed_seq_ports = node->get_output_sequence_port_count();
- } else {
- for (int i = 0; i < node->get_output_sequence_port_count(); i++) {
- Label *text2 = memnew(Label);
- text2->set_text(node->get_output_sequence_port_text(i));
- text2->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- gnode->add_child(text2);
- gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- slot_idx++;
- }
- }
- }
-
- for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) {
- bool left_ok = false;
- Variant::Type left_type = Variant::NIL;
- String left_name;
-
- if (i < node->get_input_value_port_count()) {
- PropertyInfo pi = node->get_input_value_port_info(i);
- left_ok = true;
- left_type = pi.type;
- left_name = pi.name;
- }
-
- bool right_ok = false;
- Variant::Type right_type = Variant::NIL;
- String right_name;
-
- if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) {
- PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports);
- right_ok = true;
- right_type = pi.type;
- right_name = pi.name;
- }
- VBoxContainer *vbc = memnew(VBoxContainer);
- HBoxContainer *hbc = memnew(HBoxContainer);
- HBoxContainer *hbc2 = memnew(HBoxContainer);
- vbc->add_child(hbc);
- vbc->add_child(hbc2);
- if (left_ok) {
- Ref<Texture2D> t;
- if (left_type >= 0 && left_type < Variant::VARIANT_MAX) {
- t = type_icons[left_type];
- }
- if (t.is_valid()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(t);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
- hbc->add_child(tf);
- }
-
- if (is_vslist) {
- if (nd_list->is_input_port_name_editable()) {
- LineEdit *name_box = memnew(LineEdit);
- hbc->add_child(name_box);
- 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).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)));
- }
-
- if (nd_list->is_input_port_type_editable()) {
- OptionButton *opbtn = memnew(OptionButton);
- for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
- opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
- }
- 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).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).bind(E, i), CONNECT_DEFERRED);
- } else {
- hbc->add_child(memnew(Label(left_name)));
- }
-
- if (left_type != Variant::NIL && !script->is_input_value_port_connected(E, i)) {
- PropertyInfo pi = node->get_input_value_port_info(i);
- Button *button = memnew(Button);
- Variant value = node->get_default_input_value(i);
- if (value.get_type() != left_type) {
- //different type? for now convert
- //not the same, reconvert
- Callable::CallError ce;
- const Variant *existingp = &value;
- Variant::construct(left_type, value, &existingp, 1, ce);
- }
-
- 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).bind(button, value));
- } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
- Ref<Resource> res = value;
- Array arr;
- arr.push_back(button->get_instance_id());
- arr.push_back(String(value));
- EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
-
- } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
- 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).bind(button, E, i));
- hbc2->add_child(button);
- }
- } else {
- Control *c = memnew(Control);
- c->set_custom_minimum_size(Size2(10, 0) * EDSCALE);
- hbc->add_child(c);
- }
-
- hbc->add_spacer();
- hbc2->add_spacer();
-
- if (i < mixed_seq_ports) {
- Label *text2 = memnew(Label);
- text2->set_text(node->get_output_sequence_port_text(i));
- text2->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- hbc->add_child(text2);
- }
-
- if (right_ok) {
- if (is_vslist) {
- 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).bind(E, i), CONNECT_DEFERRED);
-
- if (nd_list->is_output_port_type_editable()) {
- OptionButton *opbtn = memnew(OptionButton);
- for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
- opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
- }
- 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).bind(E, i, false), CONNECT_DEFERRED);
- }
-
- if (nd_list->is_output_port_name_editable()) {
- LineEdit *name_box = memnew(LineEdit);
- hbc->add_child(name_box);
- 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).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)));
- }
- } else {
- hbc->add_child(memnew(Label(right_name)));
- }
-
- Ref<Texture2D> t;
- if (right_type >= 0 && right_type < Variant::VARIANT_MAX) {
- t = type_icons[right_type];
- }
- if (t.is_valid()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(t);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
- hbc->add_child(tf);
- }
- }
-
- gnode->add_child(vbc);
-
- bool dark_theme = get_theme_constant(SNAME("dark_theme"), SNAME("Editor"));
- if (i < mixed_seq_ports) {
- gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture2D>(), seq_port);
- } else {
- gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme));
- }
-
- slot_idx++;
- }
- graph->add_child(gnode);
- gnode->set_theme(vstheme);
- if (gnode->is_comment()) {
- graph->move_child(gnode, 0);
- }
- }
-
- _update_graph_connections();
-
- 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;
-}
-
-void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- undo_redo->create_action(TTR("Change Port Type"));
- if (is_input) {
- undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select));
- undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type);
- } else {
- undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_type", p_port, Variant::Type(p_select));
- undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_type", p_port, vsn->get_output_value_port_info(p_port).type);
- }
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_update_node_size(int p_id) {
- Node *node = graph->get_node(itos(p_id));
- if (Object::cast_to<Control>(node)) {
- Object::cast_to<Control>(node)->reset_size(); // Shrink if text is smaller.
- }
-}
-
-void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- String text;
-
- if (Object::cast_to<LineEdit>(p_name_box)) {
- text = Object::cast_to<LineEdit>(p_name_box)->get_text();
- } else {
- return;
- }
-
- undo_redo->create_action(TTR("Change Port Name"));
- if (is_input) {
- undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text);
- undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name);
- } else {
- undo_redo->add_do_method(vsn.ptr(), "set_output_data_port_name", p_port, text);
- undo_redo->add_undo_method(vsn.ptr(), "set_output_data_port_name", p_port, vsn->get_output_value_port_info(p_port).name);
- }
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_update_members() {
- ERR_FAIL_COND(!script.is_valid());
-
- updating_members = true;
-
- members->clear();
- TreeItem *root = members->create_item();
-
- TreeItem *functions = members->create_item(root);
- functions->set_selectable(0, false);
- functions->set_text(0, TTR("Functions:"));
- functions->add_button(0, Control::get_theme_icon(SNAME("Override"), SNAME("EditorIcons")), 1, false, TTR("Override an existing built-in function."));
- functions->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), 0, false, TTR("Create a new function."));
- functions->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
-
- List<StringName> func_names;
- script->get_function_list(&func_names);
- func_names.sort_custom<StringName::AlphCompare>();
- for (const StringName &E : func_names) {
- TreeItem *ti = members->create_item(functions);
- ti->set_text(0, E);
- ti->set_selectable(0, true);
- ti->set_metadata(0, E);
- ti->add_button(0, Control::get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), 0);
- if (selected == E) {
- ti->select(0);
- }
- }
-
- TreeItem *variables = members->create_item(root);
- variables->set_selectable(0, false);
- variables->set_text(0, TTR("Variables:"));
- variables->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), -1, false, TTR("Create a new variable."));
- variables->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
-
- Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- Control::get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
- Control::get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
- };
-
- List<StringName> var_names;
- script->get_variable_list(&var_names);
- var_names.sort_custom<StringName::AlphCompare>();
- for (const StringName &E : var_names) {
- TreeItem *ti = members->create_item(variables);
-
- ti->set_text(0, E);
-
- ti->set_suffix(0, "= " + _sanitized_variant_text(E));
- ti->set_icon(0, type_icons[script->get_variable_info(E).type]);
-
- ti->set_selectable(0, true);
- ti->set_editable(0, true);
- ti->set_metadata(0, E);
- if (selected == E) {
- ti->select(0);
- }
- }
-
- TreeItem *_signals = members->create_item(root);
- _signals->set_selectable(0, false);
- _signals->set_text(0, TTR("Signals:"));
- _signals->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), -1, false, TTR("Create a new signal."));
- _signals->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
-
- List<StringName> signal_names;
- script->get_custom_signal_list(&signal_names);
- for (const StringName &E : signal_names) {
- TreeItem *ti = members->create_item(_signals);
- ti->set_text(0, E);
- ti->set_selectable(0, true);
- ti->set_editable(0, true);
- ti->set_metadata(0, E);
- if (selected == E) {
- ti->select(0);
- }
- }
-
- String base_type = script->get_instance_base_type();
- String icon_type = base_type;
- if (!Control::has_theme_icon(base_type, SNAME("EditorIcons"))) {
- icon_type = "Object";
- }
-
- base_type_select->set_text(base_type);
- base_type_select->set_icon(Control::get_theme_icon(icon_type, SNAME("EditorIcons")));
-
- updating_members = false;
-}
-
-String VisualScriptEditor::_sanitized_variant_text(const StringName &property_name) {
- Variant var = script->get_variable_default_value(property_name);
-
- if (script->get_variable_info(property_name).type != Variant::NIL) {
- Callable::CallError ce;
- const Variant *converted = &var;
- Variant n;
- Variant::construct(script->get_variable_info(property_name).type, n, &converted, 1, ce);
- var = n;
- }
-
- return String(var);
-}
-
-void VisualScriptEditor::_member_selected() {
- if (updating_members) {
- return;
- }
-
- TreeItem *ti = members->get_selected();
- ERR_FAIL_COND(!ti);
-
- selected = ti->get_metadata(0);
-
- if (ti->get_parent() == members->get_root()->get_first_child()) {
-#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
- if (held_ctrl) {
- ERR_FAIL_COND(!script->has_function(selected));
- _center_on_node(script->get_function_node_id(selected));
- }
- }
-}
-
-void VisualScriptEditor::_member_edited() {
- if (updating_members) {
- return;
- }
-
- TreeItem *ti = members->get_edited();
- ERR_FAIL_COND(!ti);
-
- String name = ti->get_metadata(0);
- String new_name = ti->get_text(0);
-
- if (name == new_name) {
- return;
- }
-
- if (!new_name.is_valid_identifier()) {
- EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name);
- updating_members = true;
- ti->set_text(0, name);
- updating_members = false;
- return;
- }
-
- if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) {
- EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name);
- updating_members = true;
- ti->set_text(0, name);
- updating_members = false;
- return;
- }
-
- TreeItem *root = members->get_root();
-
- if (ti->get_parent() == root->get_first_child()) {
- selected = new_name;
-
- int node_id = script->get_function_node_id(name);
- Ref<VisualScriptFunction> func;
- if (script->has_node(node_id)) {
- func = script->get_node(node_id);
- }
- undo_redo->create_action(TTR("Rename Function"));
- undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
- undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name);
- if (func.is_valid()) {
- undo_redo->add_do_method(func.ptr(), "set_name", new_name);
- undo_redo->add_undo_method(func.ptr(), "set_name", name);
- }
-
- // Also fix all function calls.
- List<int> lst;
- script->get_node_list(&lst);
- for (int &F : lst) {
- Ref<VisualScriptFunctionCall> fncall = script->get_node(F);
- if (!fncall.is_valid()) {
- continue;
- }
- if (fncall->get_function() == name) {
- undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
- undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
- }
- }
-
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-
- return; // Or crash because it will become invalid.
- }
-
- if (ti->get_parent() == root->get_first_child()->get_next()) {
- selected = new_name;
- undo_redo->create_action(TTR("Rename Variable"));
- undo_redo->add_do_method(script.ptr(), "rename_variable", name, new_name);
- undo_redo->add_undo_method(script.ptr(), "rename_variable", new_name, name);
-
- // Also fix all variable setter & getter calls
- List<int> lst;
- script->get_node_list(&lst);
- for (int &P : lst) {
- Ref<VisualScriptPropertySet> pset = script->get_node(P);
- if (pset.is_valid() && pset->get_property() == name) {
- undo_redo->add_do_method(pset.ptr(), "set_property", new_name);
- undo_redo->add_undo_method(pset.ptr(), "set_property", name);
- }
- Ref<VisualScriptPropertyGet> pget = script->get_node(P);
- if (pget.is_valid() && pget->get_property() == name) {
- undo_redo->add_do_method(pget.ptr(), "set_property", new_name);
- undo_redo->add_undo_method(pget.ptr(), "set_property", name);
- }
- }
-
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-
- return; // Or crash because it will become invalid.
- }
-
- if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
- selected = new_name;
- undo_redo->create_action(TTR("Rename Signal"));
- undo_redo->add_do_method(script.ptr(), "rename_custom_signal", name, new_name);
- undo_redo->add_undo_method(script.ptr(), "rename_custom_signal", new_name, name);
-
- // Also fix all signal emitting nodes
- List<int> lst;
- script->get_node_list(&lst);
- for (int &P : lst) {
- Ref<VisualScriptEmitSignal> psig = script->get_node(P);
- if (psig.is_valid() && psig->get_signal() == name) {
- undo_redo->add_do_method(psig.ptr(), "set_signal", new_name);
- undo_redo->add_undo_method(psig.ptr(), "set_signal", name);
- }
- }
-
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-
- return; // Or crash because it will become invalid.
- }
-}
-
-void VisualScriptEditor::_create_function_dialog() {
- function_create_dialog->popup_centered();
- func_name_box->set_text("");
- func_name_box->grab_focus();
- for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
- Node *nd = func_input_vbox->get_child(i);
- nd->queue_delete();
- }
-}
-
-void VisualScriptEditor::_create_function() {
- String name = _validate_name((func_name_box->get_text().is_empty()) ? "new_func" : func_name_box->get_text());
- selected = name;
- Vector2 pos = _get_available_pos();
-
- Ref<VisualScriptFunction> func_node;
- func_node.instantiate();
- func_node->set_name(name);
-
- for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
- OptionButton *opbtn = Object::cast_to<OptionButton>(func_input_vbox->get_child(i)->get_child(3));
- LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1));
- if (!opbtn || !lne) {
- continue;
- }
- Variant::Type arg_type = Variant::Type(opbtn->get_selected());
- String arg_name = lne->get_text();
- func_node->add_argument(arg_type, arg_name);
- }
-
- int func_node_id = script->get_available_id();
-
- undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name, func_node_id);
- undo_redo->add_undo_method(script.ptr(), "remove_function", name);
- undo_redo->add_do_method(script.ptr(), "add_node", func_node_id, func_node, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", func_node_id);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-
- _update_graph();
-}
-
-void VisualScriptEditor::_add_node_dialog() {
- _generic_search(graph->get_global_position() + Vector2(55, 80), true);
-}
-
-void VisualScriptEditor::_add_func_input() {
- HBoxContainer *hbox = memnew(HBoxContainer);
- hbox->set_h_size_flags(SIZE_EXPAND_FILL);
-
- Label *name_label = memnew(Label);
- name_label->set_text(TTR("Name:"));
- hbox->add_child(name_label);
-
- LineEdit *name_box = memnew(LineEdit);
- name_box->set_h_size_flags(SIZE_EXPAND_FILL);
- name_box->set_text("input");
- name_box->connect("focus_entered", callable_mp(this, &VisualScriptEditor::_deselect_input_names));
- hbox->add_child(name_box);
-
- Label *type_label = memnew(Label);
- type_label->set_text(TTR("Type:"));
- hbox->add_child(type_label);
-
- OptionButton *type_box = memnew(OptionButton);
- type_box->set_custom_minimum_size(Size2(120 * EDSCALE, 0));
- for (int i = Variant::NIL; i < Variant::VARIANT_MAX; i++) {
- type_box->add_item(Variant::get_type_name(Variant::Type(i)));
- }
- type_box->select(1);
- hbox->add_child(type_box);
-
- Button *delete_button = memnew(Button);
- delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- delete_button->set_tooltip(vformat(TTR("Delete input port")));
- hbox->add_child(delete_button);
-
- for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
- LineEdit *line_edit = (LineEdit *)func_input_vbox->get_child(i)->get_child(1);
- line_edit->deselect();
- }
-
- func_input_vbox->add_child(hbox);
- hbox->set_meta("id", hbox->get_index());
-
- delete_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_func_input).bind(hbox));
-
- name_box->select_all();
- name_box->grab_focus();
-}
-
-void VisualScriptEditor::_remove_func_input(Node *p_node) {
- func_input_vbox->remove_child(p_node);
- p_node->queue_delete();
-}
-
-void VisualScriptEditor::_deselect_input_names() {
- int cn = func_input_vbox->get_child_count();
- for (int i = 0; i < cn; i++) {
- LineEdit *lne = Object::cast_to<LineEdit>(func_input_vbox->get_child(i)->get_child(1));
- if (lne) {
- lne->deselect();
- }
- }
-}
-
-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();
-
- if (ti->get_parent() == root) {
- //main buttons
- if (ti == root->get_first_child()) {
- // Add function, this one uses menu.
-
- if (p_button == 1) {
- // Ensure script base exists otherwise use custom base type.
- ERR_FAIL_COND(script.is_null());
- new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), true);
- return;
- } else if (p_button == 0) {
- String name = _validate_name("new_function");
- selected = name;
- Vector2 pos = _get_available_pos();
-
- Ref<VisualScriptFunction> func_node;
- func_node.instantiate();
- func_node->set_name(name);
- int fn_id = script->get_available_id();
-
- undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id);
- undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_function", name);
- undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-
- _update_graph();
- }
-
- return; // Or crash because it will become invalid.
- }
-
- if (ti == root->get_first_child()->get_next()) {
- // Add variable.
- String name = _validate_name("new_variable");
- selected = name;
-
- undo_redo->create_action(TTR("Add Variable"));
- undo_redo->add_do_method(script.ptr(), "add_variable", name);
- undo_redo->add_undo_method(script.ptr(), "remove_variable", name);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
- return; // Or crash because it will become invalid.
- }
-
- if (ti == root->get_first_child()->get_next()->get_next()) {
- // Add variable.
- String name = _validate_name("new_signal");
- selected = name;
-
- undo_redo->create_action(TTR("Add Signal"));
- undo_redo->add_do_method(script.ptr(), "add_custom_signal", name);
- undo_redo->add_undo_method(script.ptr(), "remove_custom_signal", name);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
- return; // Or crash because it will become invalid.
- }
- } else if (ti->get_parent() == root->get_first_child()) {
- selected = ti->get_text(0);
- function_name_edit->set_position(get_screen_position() + get_local_mouse_position() - Vector2(60, -10));
- function_name_edit->popup();
- function_name_box->set_text(selected);
- function_name_box->select_all();
- function_name_box->grab_focus();
- }
-}
-
-void VisualScriptEditor::_add_input_port(int p_id) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- undo_redo->create_action(TTR("Add Input Port"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_method(vsn.ptr(), "add_input_data_port", Variant::NIL, "arg", -1);
- undo_redo->add_do_method(this, "_update_graph", p_id);
-
- undo_redo->add_undo_method(vsn.ptr(), "remove_input_data_port", vsn->get_input_value_port_count());
- undo_redo->add_undo_method(this, "_update_graph", p_id);
-
- updating_graph = false;
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_add_output_port(int p_id) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- undo_redo->create_action(TTR("Add Output Port"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_method(vsn.ptr(), "add_output_data_port", Variant::NIL, "arg", -1);
- undo_redo->add_do_method(this, "_update_graph", p_id);
-
- undo_redo->add_undo_method(vsn.ptr(), "remove_output_data_port", vsn->get_output_value_port_count());
- undo_redo->add_undo_method(this, "_update_graph", p_id);
-
- updating_graph = false;
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_remove_input_port(int p_id, int p_port) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS);
-
- int conn_from = -1, conn_port = -1;
- script->get_input_value_port_connection_source(p_id, p_port, &conn_from, &conn_port);
-
- if (conn_from != -1) {
- undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_id, p_port);
- }
-
- undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port);
- undo_redo->add_do_method(this, "_update_graph", p_id);
-
- if (conn_from != -1) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_id, p_port);
- }
-
- undo_redo->add_undo_method(vsn.ptr(), "add_input_data_port", vsn->get_input_value_port_info(p_port).type, vsn->get_input_value_port_info(p_port).name, p_port);
- undo_redo->add_undo_method(this, "_update_graph", p_id);
-
- updating_graph = false;
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
- Ref<VisualScriptLists> vsn = script->get_node(p_id);
- if (!vsn.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS);
-
- List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(&data_connections);
-
- 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.insert(E.to_node, RBSet<int>());
- }
- conn_map[E.to_node].insert(E.to_port);
- }
- }
-
- undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port);
- undo_redo->add_do_method(this, "_update_graph", p_id);
-
- 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);
- }
- }
-
- undo_redo->add_undo_method(vsn.ptr(), "add_output_data_port", vsn->get_output_value_port_info(p_port).type, vsn->get_output_value_port_info(p_port).name, p_port);
- undo_redo->add_undo_method(this, "_update_graph", p_id);
-
- updating_graph = false;
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) {
- Ref<VisualScriptExpression> vse = script->get_node(p_id);
- if (!vse.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- undo_redo->create_action(TTR("Change Expression"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_property(vse.ptr(), "expression", p_text);
- undo_redo->add_undo_property(vse.ptr(), "expression", vse->get("expression"));
- undo_redo->add_do_method(this, "_update_graph", p_id);
- undo_redo->add_undo_method(this, "_update_graph", p_id);
- undo_redo->commit_action();
-
- Node *node = graph->get_node(itos(p_id));
- if (Object::cast_to<Control>(node)) {
- Object::cast_to<Control>(node)->reset_size(); // Shrink if text is smaller.
- }
-
- updating_graph = false;
-}
-
-Vector2 VisualScriptEditor::_get_pos_in_graph(Vector2 p_point) const {
- Vector2 pos = (graph->get_scroll_ofs() + p_point) / (graph->get_zoom() * EDSCALE);
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- pos = pos.snapped(Vector2(snap, snap));
- }
- return pos;
-}
-
-Vector2 VisualScriptEditor::_get_available_pos(bool p_centered, Vector2 p_pos) const {
- if (p_centered) {
- p_pos = _get_pos_in_graph(graph->get_size() * 0.5);
- }
-
- while (true) {
- bool exists = false;
- List<int> existing;
- script->get_node_list(&existing);
- for (int &E : existing) {
- Point2 pos = script->get_node_position(E);
- if (pos.distance_to(p_pos) < 50) {
- p_pos += Vector2(graph->get_snap(), graph->get_snap());
- exists = true;
- break;
- }
- }
- if (exists) {
- continue;
- }
- break;
- }
-
- return p_pos;
-}
-
-String VisualScriptEditor::_validate_name(const String &p_name) const {
- String valid = p_name;
-
- int counter = 1;
- while (true) {
- bool exists = script->has_function(valid) || script->has_variable(valid) || script->has_custom_signal(valid);
-
- if (exists) {
- counter++;
- valid = p_name + "_" + itos(counter);
- continue;
- }
-
- break;
- }
-
- return valid;
-}
-
-void VisualScriptEditor::_on_nodes_copy() {
- clipboard->nodes.clear();
- clipboard->data_connections.clear();
- clipboard->sequence_connections.clear();
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- if (gn->is_selected()) {
- int id = gn->get_name().operator String().to_int();
- Ref<VisualScriptNode> node = script->get_node(id);
- if (Object::cast_to<VisualScriptFunction>(*node)) {
- EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node."));
- return;
- }
- if (node.is_valid()) {
- clipboard->nodes[id] = node->duplicate(true);
- clipboard->nodes_positions[id] = script->get_node_position(id);
- }
- }
- }
- }
-
- if (clipboard->nodes.is_empty()) {
- return;
- }
-
- List<VisualScript::SequenceConnection> sequence_connections;
- script->get_sequence_connection_list(&sequence_connections);
-
- for (const VisualScript::SequenceConnection &E : sequence_connections) {
- if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
- clipboard->sequence_connections.insert(E);
- }
- }
-
- List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(&data_connections);
-
- for (const VisualScript::DataConnection &E : data_connections) {
- if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
- clipboard->data_connections.insert(E);
- }
- }
-}
-
-void VisualScriptEditor::_on_nodes_paste() {
- if (clipboard->nodes.is_empty()) {
- EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!"));
- return;
- }
-
- HashMap<int, int> remap;
-
- undo_redo->create_action(TTR("Paste VisualScript Nodes"));
- int idc = script->get_available_id() + 1;
-
- RBSet<int> to_select;
-
- RBSet<Vector2> existing_positions;
-
- {
- List<int> nodes;
- script->get_node_list(&nodes);
- for (int &E : nodes) {
- Vector2 pos = script->get_node_position(E).snapped(Vector2(2, 2));
- existing_positions.insert(pos);
- }
- }
-
- bool first_paste = true;
- Vector2 position_offset = Vector2(0, 0);
-
- for (KeyValue<int, Ref<VisualScriptNode>> &E : clipboard->nodes) {
- Ref<VisualScriptNode> node = E.value->duplicate();
-
- int new_id = idc++;
- to_select.insert(new_id);
-
- remap[E.key] = new_id;
-
- Vector2 paste_pos = clipboard->nodes_positions[E.key];
-
- if (first_paste) {
- position_offset = _get_pos_in_graph(mouse_up_position - graph->get_global_position()) - paste_pos;
- first_paste = false;
- }
-
- paste_pos += position_offset;
-
- while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) {
- paste_pos += Vector2(20, 20) * EDSCALE;
- }
-
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- }
-
- 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 (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");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = gn->get_name().operator String().to_int();
- gn->set_selected(to_select.has(id));
- }
- }
-}
-
-void VisualScriptEditor::_on_nodes_delete() {
- // Delete all the selected nodes.
-
- List<int> to_erase;
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- if (gn->is_selected() && gn->is_close_button_visible()) {
- to_erase.push_back(gn->get_name().operator String().to_int());
- }
- }
- }
-
- if (to_erase.is_empty()) {
- return;
- }
-
- undo_redo->create_action(TTR("Remove VisualScript Nodes"));
-
- for (int &F : to_erase) {
- int cr_node = F;
-
- undo_redo->add_do_method(script.ptr(), "remove_node", cr_node);
- undo_redo->add_undo_method(script.ptr(), "add_node", cr_node, script->get_node(cr_node), script->get_node_position(cr_node));
-
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(&sequence_conns);
-
- for (const VisualScript::SequenceConnection &E : sequence_conns) {
- if (E.from_node == cr_node || E.to_node == cr_node) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
- }
- }
-
- List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(&data_conns);
-
- for (const VisualScript::DataConnection &E : data_conns) {
- if (E.from_node == F || E.to_node == F) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
- }
- }
- }
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_on_nodes_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));
- if (gn) {
- if (gn->is_selected() && gn->is_close_button_visible()) {
- int id = gn->get_name().operator String().to_int();
- to_duplicate.insert(id);
- }
- }
- }
-
- if (to_duplicate.is_empty()) {
- return;
- }
-
- undo_redo->create_action(TTR("Duplicate VisualScript Nodes"));
- int idc = script->get_available_id() + 1;
-
- RBSet<int> to_select;
- HashMap<int, int> remap;
-
- 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);
-
- Ref<VisualScriptNode> dupe = node->duplicate(true);
-
- int new_id = idc++;
- 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) + Vector2(20, 20));
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- }
-
- List<VisualScript::SequenceConnection> seqs;
- script->get_sequence_connection_list(&seqs);
- for (const VisualScript::SequenceConnection &E : seqs) {
- if (to_duplicate.has(E.from_node) && to_duplicate.has(E.to_node)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E.from_node], E.from_output, remap[E.to_node]);
- }
- }
-
- List<VisualScript::DataConnection> data;
- script->get_data_connection_list(&data);
- for (const VisualScript::DataConnection &E : data) {
- if (to_duplicate.has(E.from_node) && to_duplicate.has(E.to_node)) {
- 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_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = gn->get_name().operator String().to_int();
- gn->set_selected(to_select.has(id));
- }
- }
-
- if (to_select.size()) {
- EditorNode::get_singleton()->push_item(script->get_node(to_select.front()->get()).ptr());
- }
-}
-
-void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) {
- if (node_centered) {
- port_action_pos = graph->get_size() / 2.0f;
- } else {
- port_action_pos = graph->get_viewport()->get_mouse_position() - graph->get_global_position();
- }
-
- new_connect_node_select->select_from_visual_script(script, false); // do not reset text
-}
-
-void VisualScriptEditor::input(const Ref<InputEvent> &p_event) {
- ERR_FAIL_COND(p_event.is_null());
-
- // GUI input for VS Editor Plugin
- Ref<InputEventMouseButton> key = p_event;
-
- if (key.is_valid() && key->is_pressed()) {
- mouse_up_position = get_screen_position() + get_local_mouse_position();
- }
-}
-
-void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
- Ref<InputEventMouseButton> key = p_event;
-
- if (key.is_valid() && key->is_pressed() && key->get_button_mask() == MouseButton::RIGHT) {
- bool is_empty_selection = true;
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn && gn->is_selected()) {
- is_empty_selection = false;
- break;
- }
- }
- if (is_empty_selection && clipboard->nodes.is_empty()) {
- _generic_search();
- } else {
- popup_menu->set_item_disabled(int(EDIT_CUT_NODES), is_empty_selection);
- popup_menu->set_item_disabled(int(EDIT_COPY_NODES), is_empty_selection);
- popup_menu->set_item_disabled(int(EDIT_PASTE_NODES), clipboard->nodes.is_empty());
- popup_menu->set_item_disabled(int(EDIT_DELETE_NODES), is_empty_selection);
- popup_menu->set_item_disabled(int(EDIT_DUPLICATE_NODES), is_empty_selection);
- popup_menu->set_item_disabled(int(EDIT_CLEAR_COPY_BUFFER), clipboard->nodes.is_empty());
-
- popup_menu->set_position(mouse_up_position);
- popup_menu->popup();
- }
- }
-}
-
-void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
- Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
- if (members->has_focus()) {
- TreeItem *ti = members->get_selected();
- if (ti) {
- TreeItem *root = members->get_root();
- if (ti->get_parent() == root->get_first_child()) {
- member_type = MEMBER_FUNCTION;
- }
- if (ti->get_parent() == root->get_first_child()->get_next()) {
- member_type = MEMBER_VARIABLE;
- }
- if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
- member_type = MEMBER_SIGNAL;
- }
- member_name = ti->get_text(0);
- }
- if (ED_IS_SHORTCUT("ui_graph_delete", p_event)) {
- _member_option(MEMBER_REMOVE);
- }
- if (ED_IS_SHORTCUT("visual_script_editor/edit_member", p_event)) {
- _member_option(MEMBER_EDIT);
- }
- }
- }
-
- Ref<InputEventMouseButton> btn = p_event;
- if (btn.is_valid() && btn->is_double_click()) {
- TreeItem *ti = members->get_selected();
- if (ti && ti->get_parent() == members->get_root()->get_first_child()) { // to check if it's a function
- _center_on_node(script->get_function_node_id(ti->get_metadata(0)));
- }
- }
-}
-
-void VisualScriptEditor::_rename_function(const String &name, const String &new_name) {
- if (!new_name.is_valid_identifier()) {
- EditorNode::get_singleton()->show_warning(TTR("Name is not a valid identifier:") + " " + new_name);
- return;
- }
-
- if (script->has_function(new_name) || script->has_variable(new_name) || script->has_custom_signal(new_name)) {
- EditorNode::get_singleton()->show_warning(TTR("Name already in use by another func/var/signal:") + " " + new_name);
- return;
- }
-
- int node_id = script->get_function_node_id(name);
- Ref<VisualScriptFunction> func;
- if (script->has_node(node_id)) {
- func = script->get_node(node_id);
- }
- undo_redo->create_action(TTR("Rename Function"));
- undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
- undo_redo->add_undo_method(script.ptr(), "rename_function", new_name, name);
- if (func.is_valid()) {
- undo_redo->add_do_method(func.ptr(), "set_name", new_name);
- undo_redo->add_undo_method(func.ptr(), "set_name", name);
- }
-
- // Also fix all function calls.
- List<int> lst;
- script->get_node_list(&lst);
- for (int &F : lst) {
- Ref<VisualScriptFunctionCall> fncall = script->get_node(F);
- if (!fncall.is_valid()) {
- continue;
- }
- if (fncall->get_function() == name) {
- undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
- undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
- }
- }
-
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) {
- if (!function_name_edit->is_visible()) {
- return;
- }
-
- Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ENTER) {
- function_name_edit->hide();
- _on_fn_name_box_confirmed();
- function_name_box->clear();
- }
-}
-
-void VisualScriptEditor::_on_fn_name_box_confirmed() {
- _rename_function(selected, function_name_box->get_text());
-}
-
-Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
- if (p_from == members) {
- TreeItem *it = members->get_item_at_position(p_point);
- if (!it) {
- return Variant();
- }
-
- String type = it->get_metadata(0);
-
- if (type.is_empty()) {
- return Variant();
- }
-
- Dictionary dd;
- TreeItem *root = members->get_root();
-
- if (it->get_parent() == root->get_first_child()) {
- dd["type"] = "visual_script_function_drag";
- dd["function"] = type;
- } else if (it->get_parent() == root->get_first_child()->get_next()) {
- dd["type"] = "visual_script_variable_drag";
- dd["variable"] = type;
- } else if (it->get_parent() == root->get_first_child()->get_next()->get_next()) {
- dd["type"] = "visual_script_signal_drag";
- dd["signal"] = type;
-
- } else {
- return Variant();
- }
-
- Label *label = memnew(Label);
- label->set_text(it->get_text(0));
- set_drag_preview(label);
- return dd;
- }
- return Variant();
-}
-
-bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
- if (p_from == graph) {
- Dictionary d = p_data;
- if (d.has("type") &&
- (String(d["type"]) == "visual_script_node_drag" ||
- String(d["type"]) == "visual_script_function_drag" ||
- String(d["type"]) == "visual_script_variable_drag" ||
- String(d["type"]) == "visual_script_signal_drag" ||
- String(d["type"]) == "obj_property" ||
- String(d["type"]) == "resource" ||
- String(d["type"]) == "files" ||
- String(d["type"]) == "nodes")) {
- if (String(d["type"]) == "obj_property") {
-#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."));
-#endif
- }
-
- if (String(d["type"]) == "nodes") {
-#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."));
-#endif
- }
-
- if (String(d["type"]) == "visual_script_variable_drag") {
-#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."));
-#endif
- }
-
- return true;
- }
- }
-
- return false;
-}
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
- if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
- return nullptr;
- }
-
- Ref<Script> scr = p_current_node->get_script();
-
- if (scr.is_valid() && scr == script) {
- return p_current_node;
- }
-
- for (int i = 0; i < p_current_node->get_child_count(); i++) {
- Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
- if (n) {
- return n;
- }
- }
-
- return nullptr;
-}
-
-void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
- if (p_from != graph) {
- return;
- }
-
- Dictionary d = p_data;
-
- if (!d.has("type")) {
- return;
- }
-
- if (String(d["type"]) == "visual_script_node_drag") {
- if (!d.has("node_type") || String(d["node_type"]) == "Null") {
- return;
- }
-
- Vector2 pos = _get_pos_in_graph(p_point);
-
- int new_id = _create_new_node_from_name(d["node_type"], pos);
-
- Node *node = graph->get_node(itos(new_id));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
-
- if (String(d["type"]) == "visual_script_variable_drag") {
-#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);
-#endif
- Vector2 pos = _get_pos_in_graph(p_point);
-
- Ref<VisualScriptNode> vnode;
- if (use_set) {
- Ref<VisualScriptPropertySet> pset;
- pset.instantiate();
- vnode = pset;
- } else {
- Ref<VisualScriptPropertyGet> pget;
- pget.instantiate();
- vnode = pget;
- }
-
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(vnode.ptr(), "set_property", d["variable"]);
- undo_redo->add_do_method(vnode.ptr(), "set_base_script", script->get_path());
-
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
-
- Node *node = graph->get_node(itos(new_id));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
-
- if (String(d["type"]) == "visual_script_function_drag") {
- Vector2 pos = _get_pos_in_graph(p_point);
-
- Ref<VisualScriptFunctionCall> vnode;
- vnode.instantiate();
- vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
-
- int new_id = script->get_available_id();
-
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type());
- undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]);
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
-
- Node *node = graph->get_node(itos(new_id));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
-
- if (String(d["type"]) == "visual_script_signal_drag") {
- Vector2 pos = _get_pos_in_graph(p_point);
-
- Ref<VisualScriptEmitSignal> vnode;
- vnode.instantiate();
- vnode->set_signal(d["signal"]);
-
- int new_id = script->get_available_id();
-
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
-
- Node *node = graph->get_node(itos(new_id));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
-
- if (String(d["type"]) == "resource") {
- Vector2 pos = _get_pos_in_graph(p_point);
-
- Ref<VisualScriptPreload> prnode;
- prnode.instantiate();
- prnode->set_preload(d["resource"]);
-
- int new_id = script->get_available_id();
-
- undo_redo->create_action(TTR("Add Preload Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
-
- Node *node = graph->get_node(itos(new_id));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
-
- if (String(d["type"]) == "files") {
-#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);
-#endif
- Vector2 pos = _get_pos_in_graph(p_point);
-
- Array files = d["files"];
-
- List<int> new_ids;
- int new_id = script->get_available_id();
-
- if (files.size()) {
- undo_redo->create_action(TTR("Add Node(s)"));
-
- for (int i = 0; i < files.size(); i++) {
- Ref<Resource> res = ResourceLoader::load(files[i]);
- if (!res.is_valid()) {
- continue;
- }
- Ref<Script> drop_script = ResourceLoader::load(files[i]);
- if (drop_script.is_valid() && drop_script->is_tool() && drop_script->get_instance_base_type() == "VisualScriptCustomNode" && !use_preload) {
- Ref<VisualScriptCustomNode> vscn;
- vscn.instantiate();
- vscn->set_script(drop_script);
-
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vscn, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- } else {
- Ref<VisualScriptPreload> prnode;
- prnode.instantiate();
- prnode->set_preload(res);
-
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- }
- new_ids.push_back(new_id);
- new_id++;
- pos += Vector2(20, 20);
- }
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- }
-
- for (int &E : new_ids) {
- Node *node = graph->get_node(itos(E));
- if (node) {
- graph->set_selected(node);
- _node_selected(node);
- }
- }
- }
-
- if (String(d["type"]) == "nodes") {
- Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
-
- if (!sn) {
- EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop nodes because script '%s' is not used in this scene."), get_name()));
- return;
- }
-
-#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);
-#endif
-
- Array nodes = d["nodes"];
-
- Vector2 pos = _get_pos_in_graph(p_point);
-
- undo_redo->create_action(TTR("Add Node(s) From Tree"));
- int base_id = script->get_available_id();
-
- if (use_node || nodes.size() > 1) {
- for (int i = 0; i < nodes.size(); i++) {
- NodePath np = nodes[i];
- Node *node = get_node(np);
- if (!node) {
- continue;
- }
-
- Ref<VisualScriptNode> n;
-
- Ref<VisualScriptSceneNode> scene_node;
- scene_node.instantiate();
- scene_node->set_node_path(sn->get_path_to(node));
- n = scene_node;
-
- undo_redo->add_do_method(script.ptr(), "add_node", base_id, n, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
-
- base_id++;
- pos += Vector2(25, 25);
- }
-
- } else {
- NodePath np = nodes[0];
- Node *node = get_node(np);
- drop_position = pos;
- drop_node = node;
- drop_path = sn->get_path_to(node);
- new_connect_node_select->select_from_instance(node, false);
- }
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- }
-
- if (String(d["type"]) == "obj_property") {
- Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
-
- if (!sn && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
- EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop properties because script '%s' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."), get_name()));
- return;
- }
-
- Object *obj = d["object"];
-
- if (!obj) {
- return;
- }
-
- Node *node = Object::cast_to<Node>(obj);
- Vector2 pos = _get_pos_in_graph(p_point);
-
-#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);
-#endif
-
- if (!node || Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
- if (use_get) {
- undo_redo->create_action(TTR("Add Getter Property"));
- } else {
- undo_redo->create_action(TTR("Add Setter Property"));
- }
-
- int base_id = script->get_available_id();
-
- Ref<VisualScriptNode> vnode;
-
- if (!use_get) {
- Ref<VisualScriptPropertySet> pset;
- pset.instantiate();
- pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
- pset->set_base_type(obj->get_class());
- vnode = pset;
- } else {
- Ref<VisualScriptPropertyGet> pget;
- pget.instantiate();
- pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
- pget->set_base_type(obj->get_class());
- vnode = pget;
- }
-
- undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos);
- undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
- if (!obj->get_script().is_null()) {
- undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path());
- }
- if (!use_get) {
- undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
- }
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
-
- } else {
- if (use_get) {
- undo_redo->create_action(TTR("Add Getter Property"));
- } else {
- undo_redo->create_action(TTR("Add Setter Property"));
- }
-
- int base_id = script->get_available_id();
-
- Ref<VisualScriptNode> vnode;
-
- if (!use_get) {
- Ref<VisualScriptPropertySet> pset;
- pset.instantiate();
- if (sn == node) {
- pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
- } else {
- pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
- pset->set_base_path(sn->get_path_to(node));
- }
- vnode = pset;
- } else {
- Ref<VisualScriptPropertyGet> pget;
- pget.instantiate();
- if (sn == node) {
- pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
- } else {
- pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
- pget->set_base_path(sn->get_path_to(node));
- }
- vnode = pget;
- }
- undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos);
- undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
- if (!obj->get_script().is_null()) {
- undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path());
- }
- if (!use_get) {
- undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
- }
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- }
- }
-}
-
-void VisualScriptEditor::_draw_color_over_button(Object *obj, Color p_color) {
- Button *button = Object::cast_to<Button>(obj);
- if (!button) {
- return;
- }
-
- Ref<StyleBox> normal = get_theme_stylebox(SNAME("normal"), SNAME("Button"));
- button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
-}
-
-void VisualScriptEditor::_button_resource_previewed(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud) {
- Array ud = p_ud;
- ERR_FAIL_COND(ud.size() != 2);
-
- ObjectID id = ud[0];
- Object *obj = ObjectDB::get_instance(id);
-
- if (!obj) {
- return;
- }
-
- Button *b = Object::cast_to<Button>(obj);
- ERR_FAIL_COND(!b);
-
- if (p_preview.is_null()) {
- b->set_text(ud[1]);
- } else {
- b->set_icon(p_preview);
- }
-}
-
-/////////////////////////
-
-void VisualScriptEditor::apply_code() {
-}
-
-Ref<Resource> VisualScriptEditor::get_edited_resource() const {
- return script;
-}
-
-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;
- signal_editor->script = script;
- signal_editor->undo_redo = undo_redo;
- variable_editor->script = script;
- variable_editor->undo_redo = undo_redo;
-
- script->connect("node_ports_changed", callable_mp(this, &VisualScriptEditor::_node_ports_changed));
-
- _update_graph();
- call_deferred(SNAME("_update_members"));
-}
-
-void VisualScriptEditor::enable_editor() {
-}
-
-Vector<String> VisualScriptEditor::get_functions() {
- return Vector<String>();
-}
-
-void VisualScriptEditor::reload_text() {
-}
-
-String VisualScriptEditor::get_name() {
- String name;
-
- name = script->get_path().get_file();
- if (name.is_empty()) {
- // This appears for newly created built-in scripts before saving the scene.
- name = TTR("[unsaved]");
- } else if (script->is_built_in()) {
- const String &script_name = script->get_name();
- if (!script_name.is_empty()) {
- // If the built-in script has a custom resource name defined,
- // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
- name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
- }
- }
-
- if (is_unsaved()) {
- name += "(*)";
- }
-
- return name;
-}
-
-Ref<Texture2D> VisualScriptEditor::get_theme_icon() {
- String icon_name = "VisualScript";
- if (script->is_built_in()) {
- icon_name += "Internal";
- }
-
- if (Control::has_theme_icon(icon_name, "EditorIcons")) {
- return Control::get_theme_icon(icon_name, SNAME("EditorIcons"));
- }
-
- return Control::get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
-}
-
-bool VisualScriptEditor::is_unsaved() {
- bool unsaved =
- script->is_edited() ||
- script->are_subnodes_edited() ||
- script->get_path().is_empty(); // In memory.
- return unsaved;
-}
-
-Variant VisualScriptEditor::get_edit_state() {
- Dictionary d;
- d["scroll"] = graph->get_scroll_ofs();
- d["zoom"] = graph->get_zoom();
- d["using_snap"] = graph->is_using_snap();
- d["snap"] = graph->get_snap();
- return d;
-}
-
-void VisualScriptEditor::set_edit_state(const Variant &p_state) {
- Dictionary d = p_state;
-
- _update_graph();
- _update_members();
-
- if (d.has("scroll")) {
- graph->set_scroll_ofs(d["scroll"]);
- }
- if (d.has("zoom")) {
- graph->set_zoom(d["zoom"]);
- }
- if (d.has("snap")) {
- graph->set_snap(d["snap"]);
- }
- if (d.has("snap_enabled")) {
- graph->set_use_snap(d["snap_enabled"]);
- }
-}
-
-void VisualScriptEditor::_center_on_node(int p_id) {
- Node *n = graph->get_node(itos(p_id));
- GraphNode *gn = Object::cast_to<GraphNode>(n);
-
- // Clear selection.
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gnd) {
- gnd->set_selected(false);
- }
- }
-
- if (gn) {
- gn->set_selected(true);
- Vector2 new_scroll = gn->get_position_offset() * graph->get_zoom() - graph->get_size() * 0.5 + gn->get_size() * 0.5;
- graph->set_scroll_ofs(new_scroll);
- script->set_scroll(new_scroll / EDSCALE);
- script->set_edited(true);
- }
-}
-
-void VisualScriptEditor::goto_line(int p_line, bool p_with_error) {
- p_line += 1; // Add one because script lines begin from 0.
-
- if (p_with_error) {
- error_line = p_line;
- }
-
- if (script->has_node(p_line)) {
- _update_graph();
- _update_members();
-
- call_deferred(SNAME("call_deferred"), "_center_on_node", p_line); // The editor might be just created and size might not exist yet.
- }
-}
-
-void VisualScriptEditor::set_executing_line(int p_line) {
- // todo: add a way to show which node is executing right now.
-}
-
-void VisualScriptEditor::clear_executing_line() {
- // todo: add a way to show which node is executing right now.
-}
-
-void VisualScriptEditor::trim_trailing_whitespace() {
-}
-
-void VisualScriptEditor::insert_final_newline() {
-}
-
-void VisualScriptEditor::convert_indent_to_spaces() {
-}
-
-void VisualScriptEditor::convert_indent_to_tabs() {
-}
-
-void VisualScriptEditor::ensure_focus() {
- graph->grab_focus();
-}
-
-void VisualScriptEditor::tag_saved_version() {
-}
-
-void VisualScriptEditor::reload(bool p_soft) {
- _update_graph();
-}
-
-Array VisualScriptEditor::get_breakpoints() {
- Array breakpoints;
- List<StringName> functions;
- script->get_function_list(&functions);
- for (int i = 0; i < functions.size(); i++) {
- List<int> nodes;
- script->get_node_list(&nodes);
- for (int &F : nodes) {
- Ref<VisualScriptNode> vsn = script->get_node(F);
- if (vsn->is_breakpoint()) {
- breakpoints.push_back(F - 1); // Subtract 1 because breakpoints in text start from zero.
- }
- }
- }
- return breakpoints;
-}
-
-void VisualScriptEditor::add_callback(const String &p_function, PackedStringArray p_args) {
- if (script->has_function(p_function)) {
- _update_members();
- _update_graph();
- _center_on_node(script->get_function_node_id(p_function));
- return;
- }
-
- Ref<VisualScriptFunction> func;
- func.instantiate();
- for (int i = 0; i < p_args.size(); i++) {
- String name = p_args[i];
- Variant::Type type = Variant::NIL;
-
- if (name.contains(":")) {
- String tt = name.get_slice(":", 1);
- name = name.get_slice(":", 0);
- for (int j = 0; j < Variant::VARIANT_MAX; j++) {
- String tname = Variant::get_type_name(Variant::Type(j));
- if (tname == tt) {
- type = Variant::Type(j);
- break;
- }
- }
- }
-
- func->add_argument(type, name);
- }
- int fn_id = script->get_available_id();
- func->set_name(p_function);
- script->add_function(p_function, fn_id);
- script->add_node(fn_id, func);
-
- _update_members();
- _update_graph();
-
- _center_on_node(script->get_function_node_id(p_function));
-}
-
-bool VisualScriptEditor::show_members_overview() {
- return false;
-}
-
-void VisualScriptEditor::update_settings() {
- _update_graph();
-}
-
-void VisualScriptEditor::set_debugger_active(bool p_active) {
- if (!p_active) {
- error_line = -1;
- _update_graph(); //clear line break
- }
-}
-
-Control *VisualScriptEditor::get_base_editor() const {
- return graph;
-}
-
-void VisualScriptEditor::set_tooltip_request_func(const Callable &p_toolip_callback) {
-}
-
-Control *VisualScriptEditor::get_edit_menu() {
- return edit_menu;
-}
-
-void VisualScriptEditor::_change_base_type() {
- select_base_type->popup_create(true, true);
-}
-
-void VisualScriptEditor::_toggle_tool_script() {
- script->set_tool_enabled(!script->is_tool());
-}
-
-void VisualScriptEditor::clear_edit_menu() {
- memdelete(edit_menu);
- memdelete(members_section);
-}
-
-void VisualScriptEditor::_change_base_type_callback() {
- String bt = select_base_type->get_selected_type();
-
- ERR_FAIL_COND(bt.is_empty());
- undo_redo->create_action(TTR("Change Base Type"));
- undo_redo->add_do_method(script.ptr(), "set_instance_base_type", bt);
- undo_redo->add_undo_method(script.ptr(), "set_instance_base_type", script->get_instance_base_type());
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_node_selected(Node *p_node) {
- Ref<VisualScriptNode> vnode = p_node->get_meta("__vnode");
- if (vnode.is_null()) {
- return;
- }
-
- EditorNode::get_singleton()->push_item(vnode.ptr()); //edit node in inspector
-}
-
-static bool _get_out_slot(const Ref<VisualScriptNode> &p_node, int p_slot, int &r_real_slot, bool &r_sequence) {
- if (p_slot < p_node->get_output_sequence_port_count()) {
- r_sequence = true;
- r_real_slot = p_slot;
-
- return true;
- }
-
- r_real_slot = p_slot - p_node->get_output_sequence_port_count();
- r_sequence = false;
-
- return (r_real_slot < p_node->get_output_value_port_count());
-}
-
-static bool _get_in_slot(const Ref<VisualScriptNode> &p_node, int p_slot, int &r_real_slot, bool &r_sequence) {
- if (p_slot == 0 && p_node->has_input_sequence_port()) {
- r_sequence = true;
- r_real_slot = 0;
- return true;
- }
-
- r_real_slot = p_slot - (p_node->has_input_sequence_port() ? 1 : 0);
- r_sequence = false;
-
- return r_real_slot < p_node->get_input_value_port_count();
-}
-
-void VisualScriptEditor::_begin_node_move() {
- undo_redo->create_action(TTR("Move Node(s)"));
-}
-
-void VisualScriptEditor::_end_node_move() {
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_move_node(int p_id, const Vector2 &p_to) {
- if (!script->has_node(p_id)) {
- return;
- }
-
- Node *node = graph->get_node(itos(p_id));
-
- if (Object::cast_to<GraphNode>(node)) {
- Object::cast_to<GraphNode>(node)->set_position_offset(p_to);
- }
-
- script->set_node_position(p_id, p_to / EDSCALE);
-}
-
-void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) {
- undo_redo->add_do_method(this, "_move_node", p_id, p_to);
- undo_redo->add_undo_method(this, "_move_node", p_id, p_from);
-}
-
-void VisualScriptEditor::_remove_node(int p_id) {
- undo_redo->create_action(TTR("Remove VisualScript Node"));
-
- undo_redo->add_do_method(script.ptr(), "remove_node", p_id);
- undo_redo->add_undo_method(script.ptr(), "add_node", p_id, script->get_node(p_id), script->get_node_position(p_id));
-
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(&sequence_conns);
-
- for (const VisualScript::SequenceConnection &E : sequence_conns) {
- if (E.from_node == p_id || E.to_node == p_id) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
- }
- }
-
- List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(&data_conns);
-
- for (const VisualScript::DataConnection &E : data_conns) {
- if (E.from_node == p_id || E.to_node == p_id) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
- }
- }
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_node_ports_changed(int p_id) {
- _update_graph(p_id);
-}
-
-bool VisualScriptEditor::node_has_sequence_connections(int p_id) {
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(&sequence_conns);
-
- for (const VisualScript::SequenceConnection &E : sequence_conns) {
- int from = E.from_node;
- int to = E.to_node;
-
- if (to == p_id || from == p_id) {
- return true;
- }
- }
-
- return false;
-}
-
-void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
- Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int());
- ERR_FAIL_COND(!from_node.is_valid());
-
- bool from_seq;
- int from_port;
-
- if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) {
- return; //can't connect this, it's invalid
- }
-
- Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int());
- ERR_FAIL_COND(!to_node.is_valid());
-
- bool to_seq;
- int to_port;
-
- if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) {
- return; //can't connect this, it's invalid
- }
-
- ERR_FAIL_COND(from_seq != to_seq);
-
- // Checking to prevent warnings.
- if (from_seq) {
- if (script->has_sequence_connection(p_from.to_int(), from_port, p_to.to_int())) {
- return;
- }
- } else if (script->has_data_connection(p_from.to_int(), from_port, p_to.to_int(), to_port)) {
- return;
- }
-
- // Preventing connection to itself.
- if (p_from.to_int() == p_to.to_int()) {
- return;
- }
-
- // Do all the checks here.
- StringName func; // This the func where we store the one the nodes at the end of the resolution on having multiple nodes.
-
- undo_redo->create_action(TTR("Connect Nodes"));
-
- if (from_seq) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int());
- // This undo error on undo after move can't be removed without painful gymnastics
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int());
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- } else {
- bool converted = false;
-
- Ref<VisualScriptOperator> oper = to_node;
- if (oper.is_valid() && oper->get_typed() == Variant::NIL) {
- // It's an operator Node and if the type is already nil
- if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) {
- oper->set_typed(from_node->get_output_value_port_info(from_port).type);
- }
- }
-
- Ref<VisualScriptOperator> operf = from_node;
- if (operf.is_valid() && operf->get_typed() == Variant::NIL) {
- // It's an operator Node and if the type is already nil
- if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) {
- operf->set_typed(to_node->get_input_value_port_info(to_port).type);
- }
- }
-
- // Disconnect current, and connect the new one
- if (script->is_input_value_port_connected(p_to.to_int(), to_port)) {
- if (can_swap && data_disconnect_node == p_to.to_int()) {
- int conn_from;
- int conn_port;
- script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port);
- undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port);
- undo_redo->add_do_method(script.ptr(), "data_connect", conn_from, conn_port, data_disconnect_node, data_disconnect_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", conn_from, conn_port, data_disconnect_node, data_disconnect_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port);
- can_swap = false; // swapped
- } else {
- int conn_from;
- int conn_port;
- script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port);
- undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port);
- }
- }
- if (!converted) {
- undo_redo->add_do_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port);
-
- // Update nodes in graph
- undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
- undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
- undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
- undo_redo->add_undo_method(this, "_update_graph", p_to.to_int());
- } else {
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- }
- }
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
- Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int());
- ERR_FAIL_COND(!from_node.is_valid());
-
- bool from_seq;
- int from_port;
-
- if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) {
- return; // Can't connect this, it's invalid.
- }
-
- Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int());
- ERR_FAIL_COND(!to_node.is_valid());
-
- bool to_seq;
- int to_port;
-
- if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) {
- return; // Can't connect this, it's invalid.
- }
-
- ERR_FAIL_COND(from_seq != to_seq);
-
- undo_redo->create_action(TTR("Disconnect Nodes"));
-
- if (from_seq) {
- undo_redo->add_do_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int());
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int());
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- } else {
- can_swap = true;
- data_disconnect_node = p_to.to_int();
- data_disconnect_port = to_port;
-
- undo_redo->add_do_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port);
- // Update relevant nodes in the graph.
- undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
- undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
- undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
- undo_redo->add_undo_method(this, "_update_graph", p_to.to_int());
- }
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) {
- Node *node = graph->get_node(p_from);
- GraphNode *gn = Object::cast_to<GraphNode>(node);
- if (!gn) {
- return;
- }
-
- Ref<VisualScriptNode> vsn = script->get_node(p_from.to_int());
- if (!vsn.is_valid()) {
- return;
- }
- if (vsn->get_output_value_port_count() || vsn->get_output_sequence_port_count()) {
- port_action_pos = p_release_pos;
- }
-
- if (p_from_slot < vsn->get_output_sequence_port_count()) {
- port_action_node = p_from.to_int();
- port_action_output = p_from_slot;
- _port_action_menu(CREATE_ACTION);
- } else {
- port_action_output = p_from_slot - vsn->get_output_sequence_port_count();
- port_action_node = p_from.to_int();
- _port_action_menu(CREATE_CALL_SET_GET);
- }
-}
-
-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;
-
- if (visited_nodes.has(p_port_action_node)) {
- return tg; //no loop
- }
-
- visited_nodes.insert(p_port_action_node);
-
- Ref<VisualScriptNode> node = script->get_node(p_port_action_node);
-
- if (!node.is_valid() || node->get_output_value_port_count() <= p_port_action_output) {
- return tg;
- }
-
- Vector<VisualScriptNode::TypeGuess> in_guesses;
-
- for (int i = 0; i < node->get_input_value_port_count(); i++) {
- PropertyInfo pi = node->get_input_value_port_info(i);
- VisualScriptNode::TypeGuess g;
- g.type = pi.type;
-
- if (g.type == Variant::NIL || g.type == Variant::OBJECT) {
- // Any or object input, must further guess what this is.
- int from_node;
- int from_port;
-
- if (script->get_input_value_port_connection_source(p_port_action_node, i, &from_node, &from_port)) {
- g = _guess_output_type(from_node, from_port, visited_nodes);
- } else {
- Variant defval = node->get_default_input_value(i);
- if (defval.get_type() == Variant::OBJECT) {
- Object *obj = defval;
-
- if (obj) {
- g.type = Variant::OBJECT;
- g.gdclass = obj->get_class();
- g.script = obj->get_script();
- }
- }
- }
- }
-
- in_guesses.push_back(g);
- }
-
- return node->guess_output_type(in_guesses.ptrw(), p_port_action_output);
-}
-
-void VisualScriptEditor::_port_action_menu(int p_option) {
- RBSet<int> vn;
-
- switch (p_option) {
- case CREATE_CALL_SET_GET: {
- Ref<VisualScriptFunctionCall> n;
- n.instantiate();
-
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
-
- if (tg.gdclass != StringName()) {
- n->set_base_type(tg.gdclass);
- } else {
- n->set_base_type("Object");
- }
- String type_string;
- String base_script = "";
- if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
- type_string = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- VisualScriptFunctionCall *vsfc = Object::cast_to<VisualScriptFunctionCall>(*script->get_node(port_action_node));
- if (vsfc) {
- base_script = vsfc->get_base_script();
- } else {
- VisualScriptPropertyGet *vspg = Object::cast_to<VisualScriptPropertyGet>(*script->get_node(port_action_node));
- if (vspg) {
- base_script = vspg->get_base_script();
- } else {
- VisualScriptPropertySet *vsps = Object::cast_to<VisualScriptPropertySet>(*script->get_node(port_action_node));
- if (vsps) {
- base_script = vsps->get_base_script();
- }
- }
- }
- }
- if (tg.type == Variant::OBJECT) {
- if (tg.script.is_valid()) {
- new_connect_node_select->select_from_script(tg.script);
- } else if (type_string != String()) {
- new_connect_node_select->select_from_base_type(type_string, base_script);
- } else {
- new_connect_node_select->select_from_base_type(n->get_base_type(), base_script);
- }
- } else if (tg.type == Variant::NIL) {
- new_connect_node_select->select_from_base_type("", base_script);
- } else {
- new_connect_node_select->select_from_basic_type(tg.type);
- }
- // Ensure that the dialog fits inside the graph.
- Vector2 pos = mouse_up_position;
- 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;
- new_connect_node_select->set_position(pos);
- } break;
- case CREATE_ACTION: {
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- PropertyInfo property_info;
- if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
- property_info = script->get_node(port_action_node)->get_output_value_port_info(port_action_output);
- }
- if (tg.type == Variant::OBJECT) {
- if (property_info.type == Variant::OBJECT && !property_info.hint_string.is_empty()) {
- new_connect_node_select->select_from_action(property_info.hint_string);
- } else {
- new_connect_node_select->select_from_action("");
- }
- } else if (tg.type == Variant::NIL) {
- new_connect_node_select->select_from_action("");
- } else {
- new_connect_node_select->select_from_action(Variant::get_type_name(tg.type));
- }
- // Ensure that the dialog fits inside the graph.
- Vector2 pos = mouse_up_position;
- 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;
- new_connect_node_select->set_position(pos);
- } break;
- }
-}
-
-void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) {
- undo_redo->create_action(TTR("Connect Node Data"));
- VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr());
- if (vnode_return != nullptr && vnode_old->get_output_value_port_count() > 0) {
- vnode_return->set_enable_return_value(true);
- }
- if (vnode_old->get_output_value_port_count() <= 0) {
- undo_redo->commit_action();
- return;
- }
- if (vnode->get_input_value_port_count() <= 0) {
- undo_redo->commit_action();
- return;
- }
- int port = port_action_output;
- int value_count = vnode_old->get_output_value_port_count();
- if (port >= value_count) {
- port = 0;
- }
- undo_redo->add_do_method(script.ptr(), "data_connect", port_action_node, port, new_id, 0);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", port_action_node, port, new_id, 0);
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
-#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);
-
- RBSet<int> vn;
- bool port_node_exists = true;
-
- if (drop_position != Vector2()) {
- pos = drop_position;
- }
- drop_position = Vector2();
-
- Ref<VisualScriptNode> vnode;
- Ref<VisualScriptNode> vnode_old;
- if (port_node_exists && p_connecting) {
- vnode_old = script->get_node(port_action_node);
- }
-
- if (p_category.begins_with("VisualScriptNode")) {
- Ref<VisualScriptNode> n = VisualScriptLanguage::singleton->create_node_from_name(p_text);
-
- if (Object::cast_to<VisualScriptTypeCast>(n.ptr()) && vnode_old.is_valid()) {
- Variant::Type type = vnode_old->get_output_value_port_info(port_action_output).type;
- String hint_name = vnode_old->get_output_value_port_info(port_action_output).hint_string;
-
- if (type == Variant::OBJECT) {
- Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(hint_name);
- } else if (type == Variant::NIL) {
- Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type("");
- } else {
- Object::cast_to<VisualScriptTypeCast>(n.ptr())->set_base_type(Variant::get_type_name(type));
- }
- }
- vnode = n;
- }
-
- if (p_category == String("Class") && !p_connecting) {
- Ref<VisualScriptFunctionCall> n;
- n.instantiate();
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SINGLETON);
- n->set_singleton("ClassDB");
- n->set_function("instantiate");
- // Did not find a way to edit the input port value
- vnode = n;
- } else if (p_category == String("class_method")) {
- Ref<VisualScriptFunctionCall> n;
- n.instantiate();
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
- }
- } else {
- n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
- }
- }
- vnode = n;
- } else if (p_category == String("class_property")) {
- Vector<String> property_path = p_text.split(":");
- if (held_ctrl) {
- Ref<VisualScriptPropertySet> n;
- n.instantiate();
- n->set_property(property_path[1]);
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
- }
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
- }
- }
- vnode = n;
- } else {
- Ref<VisualScriptPropertyGet> n;
- n.instantiate();
- n->set_property(property_path[1]);
- if (!drop_path.is_empty()) {
- if (drop_path == ".") {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
- } else {
- n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
- n->set_base_path(drop_path);
- }
- }
- if (drop_node) {
- n->set_base_type(drop_node->get_class());
- if (drop_node->get_script_instance()) {
- n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
- }
- }
- vnode = n;
- }
- } else if (p_category == String("class_constant")) {
- Vector<String> property_path = p_text.split(":");
- if (ClassDB::class_exists(property_path[0])) {
- Ref<VisualScriptClassConstant> n;
- n.instantiate();
- n->set_base_type(property_path[0]);
- n->set_class_constant(property_path[1]);
- vnode = n;
- } else {
- Ref<VisualScriptBasicTypeConstant> n;
- n.instantiate();
- if (property_path[0] == "Nil") {
- n->set_basic_type(Variant::NIL);
- } else if (property_path[0] == "bool") {
- n->set_basic_type(Variant::BOOL);
- } else if (property_path[0] == "int") {
- n->set_basic_type(Variant::INT);
- } else if (property_path[0] == "float") {
- n->set_basic_type(Variant::FLOAT);
- } else if (property_path[0] == "String") {
- n->set_basic_type(Variant::STRING);
- } else if (property_path[0] == "Vector2") {
- n->set_basic_type(Variant::VECTOR2);
- } else if (property_path[0] == "Vector2i") {
- n->set_basic_type(Variant::VECTOR2I);
- } else if (property_path[0] == "Rect2") {
- n->set_basic_type(Variant::RECT2);
- } else if (property_path[0] == "Rect2i") {
- n->set_basic_type(Variant::RECT2I);
- } else if (property_path[0] == "Transform2D") {
- n->set_basic_type(Variant::TRANSFORM2D);
- } else if (property_path[0] == "Vector3") {
- n->set_basic_type(Variant::VECTOR3);
- } else if (property_path[0] == "Vector3i") {
- n->set_basic_type(Variant::VECTOR3I);
- } else if (property_path[0] == "Plane") {
- n->set_basic_type(Variant::PLANE);
- } else if (property_path[0] == "ABB") {
- n->set_basic_type(Variant::AABB);
- } else if (property_path[0] == "Quaternion") {
- n->set_basic_type(Variant::QUATERNION);
- } else if (property_path[0] == "Basis") {
- n->set_basic_type(Variant::BASIS);
- } else if (property_path[0] == "Transform3D") {
- n->set_basic_type(Variant::TRANSFORM3D);
- } else if (property_path[0] == "Color") {
- n->set_basic_type(Variant::COLOR);
- } else if (property_path[0] == "RID") {
- n->set_basic_type(Variant::RID);
- } else if (property_path[0] == "Object") {
- n->set_basic_type(Variant::OBJECT);
- } else if (property_path[0] == "Callable") {
- n->set_basic_type(Variant::CALLABLE);
- } else if (property_path[0] == "Signal") {
- n->set_basic_type(Variant::SIGNAL);
- } else if (property_path[0] == "StringName") {
- n->set_basic_type(Variant::STRING_NAME);
- } else if (property_path[0] == "NodePath") {
- n->set_basic_type(Variant::NODE_PATH);
- } else if (property_path[0] == "Dictionary") {
- n->set_basic_type(Variant::DICTIONARY);
- } else if (property_path[0] == "Array") {
- n->set_basic_type(Variant::ARRAY);
- } else if (property_path[0] == "PackedByteArray") {
- n->set_basic_type(Variant::PACKED_BYTE_ARRAY);
- } else if (property_path[0] == "PackedInt32Array") {
- n->set_basic_type(Variant::PACKED_INT32_ARRAY);
- } else if (property_path[0] == "PackedInt64Array") {
- n->set_basic_type(Variant::PACKED_INT64_ARRAY);
- } else if (property_path[0] == "PackedFloat32Array") {
- n->set_basic_type(Variant::PACKED_FLOAT32_ARRAY);
- } else if (property_path[0] == "PackedStringArray") {
- n->set_basic_type(Variant::PACKED_STRING_ARRAY);
- } else if (property_path[0] == "PackedVector2Array") {
- n->set_basic_type(Variant::PACKED_VECTOR2_ARRAY);
- } else if (property_path[0] == "PackedVector3Array") {
- n->set_basic_type(Variant::PACKED_VECTOR3_ARRAY);
- } else if (property_path[0] == "PackedColorArray") {
- n->set_basic_type(Variant::PACKED_COLOR_ARRAY);
- }
- n->set_basic_type_constant(property_path[1]);
- vnode = n;
- }
-
- } else if (p_category == String("class_signal")) {
- Vector<String> property_path = p_text.split(":");
- ERR_FAIL_COND(!(script->has_custom_signal(property_path[1]) || ClassDB::has_signal(script->get_instance_base_type(), property_path[1])));
-
- Ref<VisualScriptEmitSignal> n;
- n.instantiate();
- n->set_signal(property_path[1]);
- vnode = n;
- }
- if (vnode == nullptr) {
- print_error("Category not handled: " + p_category.quote());
- }
-
- if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr()) && p_category != "Class" && p_category != "VisualScriptNode") {
- Vector<String> property_path = p_text.split(":");
- String class_of_method = property_path[0];
- String method_name = property_path[1];
-
- Ref<VisualScriptFunctionCall> vsfc = vnode;
- vsfc->set_function(method_name);
-
- if (port_node_exists && p_connecting) {
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
-
- if (tg.type == Variant::OBJECT) {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
- vsfc->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsfc->set_base_type(tg.gdclass);
- } else if (script->get_node(port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
-
- if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsfc->set_base_type(base_type);
- }
- if (method_name == "call" || method_name == "call_deferred") {
- vsfc->set_function(String(""));
- }
- }
- if (tg.script.is_valid()) {
- vsfc->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE);
- vsfc->set_base_type(String(""));
- } else {
- vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
- vsfc->set_basic_type(tg.type);
- }
- }
- }
-
- if (port_node_exists && p_connecting) {
- if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
- Ref<VisualScriptPropertySet> vsp = vnode;
-
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsp->set_base_type(tg.gdclass);
-
- } else if (script->get_node(port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
-
- if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsp->set_base_type(base_type);
- }
- }
- if (tg.script.is_valid()) {
- vsp->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- } else {
- vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE);
- vsp->set_basic_type(tg.type);
- }
- }
-
- if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
- Ref<VisualScriptPropertyGet> vsp = vnode;
-
- VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
- if (tg.type == Variant::OBJECT) {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- if (tg.gdclass != StringName()) {
- vsp->set_base_type(tg.gdclass);
-
- } else if (script->get_node(port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
- vsp->set_base_type(base_type);
- }
- }
- if (tg.script.is_valid()) {
- vsp->set_base_script(tg.script->get_path());
- }
- } else if (tg.type == Variant::NIL) {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
- vsp->set_base_type(String(""));
- } else {
- vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE);
- vsp->set_basic_type(tg.type);
- }
- }
- }
- if (vnode == nullptr) {
- print_error("Not able to create node from category: \"" + p_category + "\" and text \"" + p_text + "\" Not created");
- return;
- }
-
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph", new_id);
- undo_redo->add_undo_method(this, "_update_graph", new_id);
- undo_redo->commit_action();
-
- port_action_new_node = new_id;
-
- String base_script = "";
- String base_type = "";
- if (port_node_exists) {
- if (vnode_old.is_valid()) {
- if (Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())) {
- base_type = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_type();
- base_script = Object::cast_to<VisualScriptTypeCast>(vnode_old.ptr())->get_base_script();
- } else if (Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())) {
- base_type = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_type();
- base_script = Object::cast_to<VisualScriptFunctionCall>(vnode_old.ptr())->get_base_script();
- } else if (Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())) {
- base_type = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_type();
- base_script = Object::cast_to<VisualScriptPropertySet>(vnode_old.ptr())->get_base_script();
- } else if (Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())) {
- base_type = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_type();
- base_script = Object::cast_to<VisualScriptPropertyGet>(vnode_old.ptr())->get_base_script();
- }
- }
-
- Vector<String> property_path = p_text.split(":");
- if (ClassDB::is_parent_class(script->get_instance_base_type(), property_path[0]) || script->get_path().ends_with(property_path[0].unquote())) {
- if (!p_connecting) {
- base_type = script->get_instance_base_type();
- base_script = script->get_path();
- }
- } else {
- base_type = property_path[0];
- base_script = "";
- }
-
- if (drop_node) {
- Ref<Script> script = drop_node->get_script();
- if (script != nullptr) {
- base_script = script->get_path();
- }
- }
-
- if (vnode_old.is_valid() && p_connecting) {
- if (base_type == "") {
- base_type = property_path[0];
- } else if (ClassDB::is_parent_class(property_path[0], base_type)) {
- base_type = property_path[0];
- }
- connect_seq(vnode_old, vnode, port_action_new_node);
- connect_data(vnode_old, vnode, port_action_new_node);
- }
- }
- if (Object::cast_to<VisualScriptTypeCast>(vnode.ptr())) {
- Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_type(base_type);
- Object::cast_to<VisualScriptTypeCast>(vnode.ptr())->set_base_script(base_script);
- } else if (Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())) {
- if (base_type_map.has(base_type)) {
- Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_basic_type(base_type_map[base_type]);
- Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
- } else {
- Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_type(base_type);
- Object::cast_to<VisualScriptFunctionCall>(vnode.ptr())->set_base_script(base_script);
- }
- } else if (Object::cast_to<VisualScriptPropertySet>(vnode.ptr())) {
- Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_type(base_type);
- Object::cast_to<VisualScriptPropertySet>(vnode.ptr())->set_base_script(base_script);
- } else if (Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())) {
- Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_type(base_type);
- Object::cast_to<VisualScriptPropertyGet>(vnode.ptr())->set_base_script(base_script);
- }
-
- drop_path = String();
- drop_node = nullptr;
-
- _update_graph(port_action_new_node);
-}
-
-void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) {
- VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr());
- if (vnode_operator != nullptr && !vnode_operator->has_input_sequence_port()) {
- return;
- }
- VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr());
- if (vnode_constructor != nullptr) {
- return;
- }
- if (vnode_old->get_output_sequence_port_count() <= 0) {
- return;
- }
- if (!vnode_new->has_input_sequence_port()) {
- return;
- }
-
- undo_redo->create_action(TTR("Connect Node Sequence"));
- int pass_port = -vnode_old->get_output_sequence_port_count() + 1;
- int return_port = port_action_output - 1;
- if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") &&
- !script->get_output_sequence_ports_connected(port_action_node).has(pass_port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, pass_port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, pass_port, new_id);
- } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") &&
- !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, return_port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, return_port, new_id);
- } else {
- for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) {
- int count = vnode_old->get_output_sequence_port_count();
- if (port_action_output < count && !script->get_output_sequence_ports_connected(port_action_node).has(port_action_output)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port_action_output, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port_action_output, new_id);
- break;
- } else if (!script->get_output_sequence_ports_connected(port_action_node).has(port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port, new_id);
- break;
- }
- }
- }
-
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting) {
- String name = p_text.substr(p_text.find_char(':') + 1);
- if (script->has_function(name)) {
- EditorNode::get_singleton()->show_warning(vformat(TTR("Script already has function '%s'"), name));
- return;
- }
-
- MethodInfo minfo;
- {
- List<MethodInfo> methods;
- bool found = false;
- ClassDB::get_virtual_methods(script->get_instance_base_type(), &methods);
- for (const MethodInfo &E : methods) {
- if (E.name == name) {
- minfo = E;
- found = true;
- }
- }
-
- ERR_FAIL_COND(!found);
- }
-
- selected = name;
- Ref<VisualScriptFunction> func_node;
- func_node.instantiate();
- func_node->set_name(name);
- int fn_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id);
-
- for (int i = 0; i < minfo.arguments.size(); i++) {
- func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string);
- }
-
- Vector2 pos = _get_available_pos();
-
- undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
- if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
- Ref<VisualScriptReturn> ret_node;
- ret_node.instantiate();
- ret_node->set_return_type(minfo.return_val.type);
- ret_node->set_enable_return_value(true);
- ret_node->set_name(name);
- int nid = script->get_available_id() + 1;
- undo_redo->add_do_method(script.ptr(), "add_node", nid, ret_node, _get_available_pos(false, pos + Vector2(500, 0)));
- undo_redo->add_undo_method(script.ptr(), "remove_node", nid);
- }
-
- undo_redo->add_undo_method(script.ptr(), "remove_function", name);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-
- _update_graph();
-}
-
-void VisualScriptEditor::_cancel_connect_node() {
- // Ensure the cancel is done.
- port_action_new_node = -1;
-}
-
-int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point) {
- Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text);
- int new_id = script->get_available_id();
- undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, p_point);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- return new_id;
-}
-
-void VisualScriptEditor::_default_value_changed() {
- Ref<VisualScriptNode> vsn = script->get_node(editing_id);
- if (vsn.is_null()) {
- return;
- }
-
- undo_redo->create_action(TTR("Change Input Value"));
- undo_redo->add_do_method(vsn.ptr(), "set_default_input_value", editing_input, default_value_edit->get_variant());
- undo_redo->add_undo_method(vsn.ptr(), "set_default_input_value", editing_input, vsn->get_default_input_value(editing_input));
-
- undo_redo->add_do_method(this, "_update_graph", editing_id);
- undo_redo->add_undo_method(this, "_update_graph", editing_id);
- undo_redo->commit_action();
-}
-
-void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) {
- Ref<VisualScriptNode> vsn = script->get_node(p_id);
- if (vsn.is_null()) {
- return;
- }
-
- PropertyInfo pinfo = vsn->get_input_value_port_info(p_input_port);
- Variant existing = vsn->get_default_input_value(p_input_port);
- if (pinfo.type != Variant::NIL && existing.get_type() != pinfo.type) {
- Callable::CallError ce;
- Variant e = existing;
- const Variant *existingp = &e;
- Variant::construct(pinfo.type, existing, &existingp, 1, ce);
- }
-
- default_value_edit->set_position(Object::cast_to<Control>(p_button)->get_screen_position() + Vector2(0, Object::cast_to<Control>(p_button)->get_size().y) * graph->get_zoom());
- default_value_edit->reset_size();
-
- if (pinfo.type == Variant::NODE_PATH) {
- Node *edited_scene = get_tree()->get_edited_scene_root();
- if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open).
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (script_node) {
- // Pick a node relative to the script, IF the script exists.
- pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
- pinfo.hint_string = script_node->get_path();
- } else {
- // Pick a path relative to edited scene.
- pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
- pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path();
- }
- }
- }
-
- if (default_value_edit->edit(nullptr, pinfo.name, pinfo.type, existing, pinfo.hint, pinfo.hint_string)) {
- if (pinfo.hint == PROPERTY_HINT_MULTILINE_TEXT) {
- default_value_edit->popup_centered_ratio();
- } else {
- default_value_edit->popup();
- }
- }
-
- editing_id = p_id;
- editing_input = p_input_port;
-}
-
-void VisualScriptEditor::_show_hint(const String &p_hint) {
- hint_text->set_text(p_hint);
- hint_text->show();
- hint_text_timer->start();
-}
-
-void VisualScriptEditor::_hide_timer() {
- hint_text->hide();
-}
-
-void VisualScriptEditor::_toggle_scripts_pressed() {
- ScriptEditor::get_singleton()->toggle_scripts_panel();
- update_toggle_scripts_button();
-}
-
-void VisualScriptEditor::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- 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).bind(-1), CONNECT_DEFERRED);
- signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_members));
- signal_editor->connect("changed", callable_mp(this, &VisualScriptEditor::_update_graph).bind(-1), CONNECT_DEFERRED);
- [[fallthrough]];
- }
- case NOTIFICATION_THEME_CHANGED: {
- if (p_what != NOTIFICATION_READY && !is_visible_in_tree()) {
- return;
- }
-
- update_toggle_scripts_button();
-
- edit_variable_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- edit_signal_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
- func_input_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
-
- Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
-
- bool dark_theme = tm->get_constant("dark_theme", "Editor");
-
- if (dark_theme) {
- node_colors["flow_control"] = Color(0.96, 0.96, 0.96);
- node_colors["functions"] = Color(0.96, 0.52, 0.51);
- node_colors["data"] = Color(0.5, 0.96, 0.81);
- node_colors["operators"] = Color(0.67, 0.59, 0.87);
- node_colors["custom"] = Color(0.5, 0.73, 0.96);
- node_colors["constants"] = Color(0.96, 0.5, 0.69);
- } else {
- node_colors["flow_control"] = Color(0.26, 0.26, 0.26);
- node_colors["functions"] = Color(0.95, 0.4, 0.38);
- node_colors["data"] = Color(0.07, 0.73, 0.51);
- node_colors["operators"] = Color(0.51, 0.4, 0.82);
- node_colors["custom"] = Color(0.31, 0.63, 0.95);
- node_colors["constants"] = Color(0.94, 0.18, 0.49);
- }
-
- for (const KeyValue<StringName, Color> &E : node_colors) {
- const Ref<StyleBoxFlat> sb = tm->get_stylebox(SNAME("frame"), SNAME("GraphNode"));
-
- if (!sb.is_null()) {
- Ref<StyleBoxFlat> frame_style = sb->duplicate();
- // Adjust the border color to be close to the GraphNode's background color.
- // This keeps the node's title area from being too distracting.
- Color color = dark_theme ? E.value.darkened(0.75) : E.value.lightened(0.75);
- color.a = 0.9;
- frame_style->set_border_color(color);
- node_styles[E.key] = frame_style;
- }
- }
-
- if (is_visible_in_tree() && script.is_valid()) {
- _update_members();
- _update_graph();
- }
- } break;
-
- case NOTIFICATION_VISIBILITY_CHANGED: {
- update_toggle_scripts_button();
- members_section->set_visible(is_visible_in_tree());
- } break;
- }
-}
-
-void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) {
- if (updating_graph || !script.is_valid()) {
- return;
- }
-
- updating_graph = true;
-
- script->set_scroll(graph->get_scroll_ofs() / EDSCALE);
- script->set_edited(true);
- updating_graph = false;
-}
-
-void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_node) {
- if (updating_graph) {
- return;
- }
- Ref<VisualScriptComment> vsc = script->get_node(p_node);
- if (vsc.is_null()) {
- return;
- }
-
- Node *node = graph->get_node(itos(p_node));
- GraphNode *gn = Object::cast_to<GraphNode>(node);
- if (!gn) {
- return;
- }
-
- Vector2 new_size = p_new_size;
- if (graph->is_using_snap()) {
- Vector2 snap = Vector2(graph->get_snap(), graph->get_snap());
- Vector2 min_size = (gn->get_minimum_size() + (snap * 0.5)).snapped(snap);
- new_size = new_size.snapped(snap).max(min_size);
- }
-
- updating_graph = true;
-
- graph->set_block_minimum_size_adjust(true); //faster resize
-
- undo_redo->create_action(TTR("Resize Comment"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_method(vsc.ptr(), "set_size", new_size / EDSCALE);
- undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size());
- undo_redo->commit_action();
-
- gn->set_custom_minimum_size(new_size);
- gn->reset_size();
- graph->set_block_minimum_size_adjust(false);
- updating_graph = false;
-}
-
-void VisualScriptEditor::_menu_option(int p_what) {
- switch (p_what) {
- case EDIT_ADD_NODE: {
- _generic_search();
- } break;
- case EDIT_DELETE_NODES: {
- _on_nodes_delete();
- } break;
- case EDIT_TOGGLE_BREAKPOINT: {
- List<String> reselect;
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- if (gn->is_selected()) {
- int id = String(gn->get_name()).to_int();
- Ref<VisualScriptNode> vsn = script->get_node(id);
- if (vsn.is_valid()) {
- vsn->set_breakpoint(!vsn->is_breakpoint());
- reselect.push_back(gn->get_name());
- }
- }
- }
- }
-
- _update_graph();
-
- for (const String &E : reselect) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(E));
- gn->set_selected(true);
- }
-
- } break;
- case EDIT_FIND_NODE_TYPE: {
- _generic_search();
- } break;
- case EDIT_COPY_NODES: {
- _on_nodes_copy();
- } break;
- case EDIT_CUT_NODES: {
- _on_nodes_copy();
- _on_nodes_delete();
- } break;
- case EDIT_PASTE_NODES: {
- _on_nodes_paste();
- } break;
- case EDIT_DUPLICATE_NODES: {
- _on_nodes_duplicate();
- } break;
- case EDIT_CREATE_FUNCTION: {
- // Create Function.
- 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) {
- if (gn->is_selected()) {
- int id = String(gn->get_name()).to_int();
- Ref<VisualScriptNode> node = script->get_node(id);
- if (Object::cast_to<VisualScriptFunction>(*node)) {
- EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node."));
- return;
- }
- if (node.is_valid()) {
- nodes.insert(id, node);
- selections.insert(id);
- }
- }
- }
- }
-
- if (nodes.size() == 0) {
- return; // nothing to be done if there are no valid nodes selected
- }
-
- RBSet<VisualScript::SequenceConnection> seqmove;
- RBSet<VisualScript::DataConnection> datamove;
-
- RBSet<VisualScript::SequenceConnection> seqext;
- RBSet<VisualScript::DataConnection> dataext;
-
- int start_node = -1;
- RBSet<int> end_nodes;
- if (nodes.size() == 1) {
- Ref<VisualScriptNode> nd = script->get_node(nodes.begin()->key);
- if (nd.is_valid() && nd->has_input_sequence_port()) {
- start_node = nodes.begin()->key;
- } else {
- EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
- return;
- }
- } else {
- List<VisualScript::SequenceConnection> seqs;
- script->get_sequence_connection_list(&seqs);
-
- if (seqs.size() == 0) {
- // In case there are no sequence connections,
- // select the top most node cause that's probably how,
- // the user wants to connect the nodes.
- int top_nd = -1;
- Vector2 top;
- for (const KeyValue<int, Ref<VisualScriptNode>> &E : nodes) {
- Ref<VisualScriptNode> nd = script->get_node(E.key);
- if (nd.is_valid() && nd->has_input_sequence_port()) {
- if (top_nd < 0) {
- top_nd = E.key;
- top = script->get_node_position(top_nd);
- }
- Vector2 pos = script->get_node_position(E.key);
- if (top.y > pos.y) {
- top_nd = E.key;
- top = pos;
- }
- }
- }
- Ref<VisualScriptNode> nd = script->get_node(top_nd);
- if (nd.is_valid() && nd->has_input_sequence_port()) {
- start_node = top_nd;
- } else {
- EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
- return;
- }
- } else {
- // Pick the node with input sequence.
- 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);
- nodes_from.insert(E.from_node);
- } else if (nodes.has(E.from_node) && !nodes.has(E.to_node)) {
- seqext.insert(E);
- } else if (!nodes.has(E.from_node) && nodes.has(E.to_node)) {
- if (start_node == -1) {
- seqext.insert(E);
- start_node = E.to_node;
- } else {
- EditorNode::get_singleton()->show_warning(TTR("Try to only have one sequence input in selection."));
- return;
- }
- }
- nodes_to.insert(E.to_node);
- }
-
- // To use to add return nodes.
- _get_ends(start_node, seqs, selections, end_nodes);
-
- if (start_node == -1) {
- // 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 (const int &E : nodes_from) {
- if (!nodes_to.has(E)) {
- start_node = E;
- }
- }
- }
- }
- }
-
- if (start_node == -1) {
- return; // This should not happen, but just in case something goes wrong.
- }
-
- List<Variant::Type> inputs; // input types
- List<Pair<int, int>> input_connections;
- {
- List<VisualScript::DataConnection> dats;
- script->get_data_connection_list(&dats);
- for (const VisualScript::DataConnection &E : dats) {
- if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
- datamove.insert(E);
- } else if (!nodes.has(E.from_node) && nodes.has(E.to_node)) {
- // Add all these as inputs for the Function.
- Ref<VisualScriptNode> node = script->get_node(E.to_node);
- if (node.is_valid()) {
- dataext.insert(E);
- PropertyInfo pi = node->get_input_value_port_info(E.to_port);
- inputs.push_back(pi.type);
- input_connections.push_back(Pair<int, int>(E.to_node, E.to_port));
- }
- } else if (nodes.has(E.from_node) && !nodes.has(E.to_node)) {
- dataext.insert(E);
- }
- }
- }
- int fn_id = script->get_available_id();
- {
- String new_fn = _validate_name("new_function");
-
- Vector2 pos = _get_available_pos(false, script->get_node_position(start_node) - Vector2(80, 150));
-
- Ref<VisualScriptFunction> func_node;
- func_node.instantiate();
- func_node->set_name(new_fn);
-
- undo_redo->create_action(TTR("Create Function"));
-
- undo_redo->add_do_method(script.ptr(), "add_function", new_fn, fn_id);
- undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
- undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn);
- undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
- // Might make the system more intelligent by checking port from info.
- int i = 0;
- List<Pair<int, int>>::Element *F = input_connections.front();
- for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) {
- func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i);
- undo_redo->add_do_method(script.ptr(), "data_connect", fn_id, i, F->get().first, F->get().second);
- i++; // increment i
- }
- // Ensure Preview Selection is of newly created function node.
- if (selections.size()) {
- EditorNode::get_singleton()->push_item(func_node.ptr());
- }
- }
- // Move the nodes.
-
- // Handles reconnection of sequence connections on undo, start here in case of issues.
- 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 (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.
- undo_redo->add_do_method(script.ptr(), "sequence_connect", fn_id, 0, start_node);
-
- // Could fail with the new changes, start here when searching for bugs in create function shortcut.
- int m = 1;
- 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) + 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, 0, ret_id);
- // Add data outputs from each of the end_nodes.
- 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, 0, ret_id, 0);
- }
- }
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-
- // Make sure all Nodes get marked for selection so that they can be moved together.
- selections.insert(fn_id);
- for (int k = 0; k < graph->get_child_count(); k++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k));
- if (gn) {
- int id = gn->get_name().operator String().to_int();
- gn->set_selected(selections.has(id));
- }
- }
-
- } break;
- case REFRESH_GRAPH: {
- _update_graph();
- } break;
- case EDIT_CLEAR_COPY_BUFFER: {
- clipboard->nodes.clear();
- clipboard->nodes_positions.clear();
- clipboard->data_connections.clear();
- clipboard->sequence_connections.clear();
- } break;
- }
-}
-
-// 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 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;
-
- if (from == p_node && p_selected.has(to)) {
- // This is an interior connection move forward to the to node.
- _get_ends(to, p_seqs, p_selected, r_end_nodes);
- } else if (from == p_node && !p_selected.has(to)) {
- r_end_nodes.insert(from);
- }
- }
-}
-
-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);
-
- member_popup->clear();
- member_popup->set_position(members->get_screen_position() + p_pos);
- member_popup->reset_size();
-
- function_name_edit->set_position(members->get_screen_position() + p_pos);
- function_name_edit->reset_size();
-
- TreeItem *root = members->get_root();
-
- Ref<Texture2D> del_icon = Control::get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
-
- Ref<Texture2D> edit_icon = Control::get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"));
-
- if (ti->get_parent() == root->get_first_child()) {
- member_type = MEMBER_FUNCTION;
- member_name = ti->get_text(0);
- member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
- member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
- member_popup->popup();
- return;
- }
-
- if (ti->get_parent() == root->get_first_child()->get_next()) {
- member_type = MEMBER_VARIABLE;
- member_name = ti->get_text(0);
- member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
- member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
- member_popup->popup();
- return;
- }
-
- if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
- member_type = MEMBER_SIGNAL;
- member_name = ti->get_text(0);
- member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
- member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
- member_popup->popup();
- return;
- }
-}
-
-void VisualScriptEditor::_member_option(int p_option) {
- switch (member_type) {
- case MEMBER_FUNCTION: {
- if (p_option == MEMBER_REMOVE) {
- // Delete the function.
- String name = member_name;
- List<String> lst;
- int fn_node = script->get_function_node_id(name);
- undo_redo->create_action(TTR("Remove Function"));
- undo_redo->add_do_method(script.ptr(), "remove_function", name);
- undo_redo->add_do_method(script.ptr(), "remove_node", fn_node);
- undo_redo->add_undo_method(script.ptr(), "add_function", name, fn_node);
- undo_redo->add_undo_method(script.ptr(), "add_node", fn_node, script->get_node(fn_node), script->get_node_position(fn_node));
- List<VisualScript::SequenceConnection> seqcons;
- script->get_sequence_connection_list(&seqcons);
- for (const VisualScript::SequenceConnection &E : seqcons) {
- if (E.from_node == fn_node) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", fn_node, E.from_output, E.to_node);
- }
- }
- List<VisualScript::DataConnection> datcons;
- script->get_data_connection_list(&datcons);
- for (const VisualScript::DataConnection &E : datcons) {
- if (E.from_node == fn_node) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", fn_node, E.from_port, E.to_node, E.to_port);
- }
- }
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
- undo_redo->commit_action();
- } else if (p_option == MEMBER_EDIT) {
- selected = members->get_selected()->get_text(0);
- function_name_edit->popup();
- function_name_box->set_text(selected);
- function_name_box->select_all();
- function_name_box->grab_focus();
- }
- } break;
- case MEMBER_VARIABLE: {
- String name = member_name;
-
- if (p_option == MEMBER_REMOVE) {
- undo_redo->create_action(TTR("Remove Variable"));
- undo_redo->add_do_method(script.ptr(), "remove_variable", name);
- undo_redo->add_undo_method(script.ptr(), "add_variable", name, script->get_variable_default_value(name));
- undo_redo->add_undo_method(script.ptr(), "set_variable_info", name, script->call("get_variable_info", name)); //return as dict
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->commit_action();
- } else if (p_option == MEMBER_EDIT) {
- variable_editor->edit(name);
- edit_variable_dialog->set_title(TTR("Editing Variable:") + " " + name);
- edit_variable_dialog->popup_centered(Size2(400, 200) * EDSCALE);
- }
- } break;
- case MEMBER_SIGNAL: {
- String name = member_name;
-
- if (p_option == MEMBER_REMOVE) {
- undo_redo->create_action(TTR("Remove Signal"));
- undo_redo->add_do_method(script.ptr(), "remove_custom_signal", name);
- undo_redo->add_undo_method(script.ptr(), "add_custom_signal", name);
-
- for (int i = 0; i < script->custom_signal_get_argument_count(name); i++) {
- undo_redo->add_undo_method(script.ptr(), "custom_signal_add_argument", name, script->custom_signal_get_argument_name(name, i), script->custom_signal_get_argument_type(name, i));
- }
-
- undo_redo->add_do_method(this, "_update_members");
- undo_redo->add_undo_method(this, "_update_members");
- undo_redo->commit_action();
- } else if (p_option == MEMBER_EDIT) {
- signal_editor->edit(name);
- edit_signal_dialog->set_title(TTR("Editing Signal:") + " " + name);
- edit_signal_dialog->popup_centered(Size2(400, 300) * EDSCALE);
- }
- } break;
- }
-}
-
-void VisualScriptEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
-}
-
-void VisualScriptEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
-}
-
-void VisualScriptEditor::update_toggle_scripts_button() {
- if (is_layout_rtl()) {
- toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"), SNAME("EditorIcons")));
- } else {
- toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons")));
- }
- toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
-}
-
-void VisualScriptEditor::_bind_methods() {
- ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node);
- ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1));
-
- ClassDB::bind_method("_center_on_node", &VisualScriptEditor::_center_on_node);
- ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed);
- ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu);
-
- ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name);
-
- ClassDB::bind_method("_get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
- ClassDB::bind_method("_can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw);
- ClassDB::bind_method("_drop_data_fw", &VisualScriptEditor::drop_data_fw);
-
- ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections);
- ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
-
- ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
-}
-
-VisualScriptEditor::VisualScriptEditor() {
- if (!clipboard) {
- clipboard = memnew(Clipboard);
- }
-
- edit_menu = memnew(MenuButton);
- edit_menu->set_shortcut_context(this);
- edit_menu->set_text(TTR("Edit"));
- edit_menu->set_switch_on_hover(true);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_graph_delete"), EDIT_DELETE_NODES);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY_NODES);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT_NODES);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE_NODES);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH);
- edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &VisualScriptEditor::_menu_option));
-
- members_section = memnew(VBoxContainer);
- // Add but wait until done setting up this.
- ScriptEditor::get_singleton()->get_left_list_split()->call_deferred(SNAME("add_child"), members_section);
- members_section->set_v_size_flags(SIZE_EXPAND_FILL);
-
- CheckButton *tool_script_check = memnew(CheckButton);
- tool_script_check->set_text(TTR("Make Tool:"));
- members_section->add_child(tool_script_check);
- tool_script_check->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_tool_script));
-
- /// Members ///
-
- members = memnew(Tree);
- 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_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), CONNECT_DEFERRED);
- members->connect("gui_input", callable_mp(this, &VisualScriptEditor::_members_gui_input));
- 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);
- members->set_drag_forwarding(this);
-
- member_popup = memnew(PopupMenu);
- add_child(member_popup);
- member_popup->connect("id_pressed", callable_mp(this, &VisualScriptEditor::_member_option));
-
- function_name_edit = memnew(AcceptDialog);
- function_name_edit->set_title(TTR("Rename Function"));
- function_name_box = memnew(LineEdit);
- function_name_edit->add_child(function_name_box);
- function_name_box->connect("gui_input", callable_mp(this, &VisualScriptEditor::_fn_name_box_input));
- function_name_edit->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_on_fn_name_box_confirmed));
- function_name_box->set_expand_to_text_length_enabled(true);
- add_child(function_name_edit);
-
- /// Actual Graph ///
-
- graph = memnew(GraphEdit);
- add_child(graph);
- graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- 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));
- graph->connect("copy_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_copy));
- graph->connect("paste_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_paste));
- graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete));
- graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate));
- graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input));
- 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));
-
- status_bar = memnew(HBoxContainer);
- add_child(status_bar);
- status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
- status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE));
-
- toggle_scripts_button = memnew(Button);
- toggle_scripts_button->set_flat(true);
- toggle_scripts_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_scripts_pressed));
- status_bar->add_child(toggle_scripts_button);
-
- /// Add Buttons to Top Bar/Zoom bar.
- HBoxContainer *graph_hbc = graph->get_zoom_hbox();
-
- Label *base_lbl = memnew(Label);
- base_lbl->set_text(TTR("Change Base Type:") + " ");
- graph_hbc->add_child(base_lbl);
-
- base_type_select = memnew(Button);
- base_type_select->connect("pressed", callable_mp(this, &VisualScriptEditor::_change_base_type));
- graph_hbc->add_child(base_type_select);
-
- Button *add_nds = memnew(Button);
- add_nds->set_text(TTR("Add Nodes..."));
- graph_hbc->add_child(add_nds);
- add_nds->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_node_dialog));
-
- Button *fn_btn = memnew(Button);
- fn_btn->set_text(TTR("Add Function..."));
- graph_hbc->add_child(fn_btn);
- fn_btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function_dialog));
-
- // Add Function Dialog.
- VBoxContainer *function_vb = memnew(VBoxContainer);
- function_vb->set_v_size_flags(SIZE_EXPAND_FILL);
- function_vb->set_custom_minimum_size(Size2(450, 300) * EDSCALE);
-
- HBoxContainer *func_name_hbox = memnew(HBoxContainer);
- function_vb->add_child(func_name_hbox);
-
- Label *func_name_label = memnew(Label);
- func_name_label->set_text(TTR("Name:"));
- func_name_hbox->add_child(func_name_label);
-
- func_name_box = memnew(LineEdit);
- func_name_box->set_h_size_flags(SIZE_EXPAND_FILL);
- func_name_box->set_placeholder(TTR("function_name"));
- func_name_box->set_text("");
- func_name_box->connect("focus_entered", callable_mp(this, &VisualScriptEditor::_deselect_input_names));
- func_name_hbox->add_child(func_name_box);
-
- // Add minor setting for function if needed, here!
-
- function_vb->add_child(memnew(HSeparator));
-
- Button *add_input_button = memnew(Button);
- add_input_button->set_h_size_flags(SIZE_EXPAND_FILL);
- add_input_button->set_text(TTR("Add Input"));
- add_input_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_func_input));
- function_vb->add_child(add_input_button);
-
- func_input_scroll = memnew(ScrollContainer);
- func_input_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
- function_vb->add_child(func_input_scroll);
-
- func_input_vbox = memnew(VBoxContainer);
- func_input_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
- func_input_scroll->add_child(func_input_vbox);
-
- function_create_dialog = memnew(ConfirmationDialog);
- function_create_dialog->set_title(TTR("Create Function"));
- function_create_dialog->add_child(function_vb);
- 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);
-
- select_func_text = memnew(Label);
- select_func_text->set_text(TTR("Select or create a function to edit its graph."));
- select_func_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- select_func_text->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
- select_func_text->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(select_func_text);
-
- hint_text = memnew(Label);
- hint_text->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -100);
- hint_text->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- hint_text->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- hint_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- hint_text->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
- graph->add_child(hint_text);
-
- hint_text_timer = memnew(Timer);
- hint_text_timer->set_wait_time(4);
- hint_text_timer->connect("timeout", callable_mp(this, &VisualScriptEditor::_hide_timer));
- add_child(hint_text_timer);
-
- // Allowed casts (connections).
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- graph->add_valid_connection_type(Variant::NIL, i);
- graph->add_valid_connection_type(i, Variant::NIL);
- for (int j = 0; j < Variant::VARIANT_MAX; j++) {
- if (Variant::can_convert(Variant::Type(i), Variant::Type(j))) {
- graph->add_valid_connection_type(i, j);
- }
- }
-
- graph->add_valid_right_disconnect_type(i);
- }
-
- graph->add_valid_left_disconnect_type(TYPE_SEQUENCE);
-
- graph->connect("connection_request", callable_mp(this, &VisualScriptEditor::_graph_connected));
- graph->connect("disconnection_request", callable_mp(this, &VisualScriptEditor::_graph_disconnected));
- graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
-
- edit_signal_dialog = memnew(AcceptDialog);
- edit_signal_dialog->set_ok_button_text(TTR("Close"));
- add_child(edit_signal_dialog);
-
- signal_editor = memnew(VisualScriptEditorSignalEdit);
- edit_signal_edit = memnew(EditorInspector);
- edit_signal_dialog->add_child(edit_signal_edit);
-
- edit_signal_edit->edit(signal_editor);
-
- edit_variable_dialog = memnew(AcceptDialog);
- edit_variable_dialog->set_ok_button_text(TTR("Close"));
- add_child(edit_variable_dialog);
-
- variable_editor = memnew(VisualScriptEditorVariableEdit);
- edit_variable_edit = memnew(EditorInspector);
- edit_variable_dialog->add_child(edit_variable_edit);
-
- edit_variable_edit->edit(variable_editor);
-
- select_base_type = memnew(CreateDialog);
- select_base_type->set_base_type("Object"); // Anything goes.
- select_base_type->connect("create", callable_mp(this, &VisualScriptEditor::_change_base_type_callback));
- add_child(select_base_type);
-
- undo_redo = EditorNode::get_singleton()->get_undo_redo();
-
- set_process_input(true);
-
- default_value_edit = memnew(CustomPropertyEditor);
- add_child(default_value_edit);
- default_value_edit->connect("variant_changed", callable_mp(this, &VisualScriptEditor::_default_value_changed));
-
- new_connect_node_select = memnew(VisualScriptPropertySelector);
- add_child(new_connect_node_select);
- new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node));
- new_connect_node_select->get_cancel_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
-
- new_virtual_method_select = memnew(VisualScriptPropertySelector);
- add_child(new_virtual_method_select);
- new_virtual_method_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_new_virtual_method));
-
- popup_menu = memnew(PopupMenu);
- add_child(popup_menu);
- popup_menu->add_item(TTR("Add Node"), EDIT_ADD_NODE);
- popup_menu->add_separator();
- popup_menu->add_item(TTR("Cut"), EDIT_CUT_NODES);
- popup_menu->add_item(TTR("Copy"), EDIT_COPY_NODES);
- popup_menu->add_item(TTR("Paste"), EDIT_PASTE_NODES);
- popup_menu->add_item(TTR("Delete"), EDIT_DELETE_NODES);
- popup_menu->add_item(TTR("Duplicate"), EDIT_DUPLICATE_NODES);
- popup_menu->add_item(TTR("Clear Copy Buffer"), EDIT_CLEAR_COPY_BUFFER);
- popup_menu->connect("id_pressed", callable_mp(this, &VisualScriptEditor::_menu_option));
-
- base_type_map.insert("String", Variant::STRING);
- base_type_map.insert("Vector2", Variant::VECTOR2);
- base_type_map.insert("Vector2i", Variant::VECTOR2I);
- base_type_map.insert("Rect2", Variant::RECT2);
- 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);
- base_type_map.insert("Callable", Variant::CALLABLE);
- base_type_map.insert("Dictionary", Variant::DICTIONARY);
- base_type_map.insert("Array", Variant::ARRAY);
- base_type_map.insert("PackedByteArray", Variant::PACKED_BYTE_ARRAY);
- base_type_map.insert("PackedInt32Array", Variant::PACKED_INT32_ARRAY);
- base_type_map.insert("PackedFloat32Array", Variant::PACKED_FLOAT32_ARRAY);
- base_type_map.insert("PackedInt64Array", Variant::PACKED_INT64_ARRAY);
- base_type_map.insert("PackedFloat64Array", Variant::PACKED_FLOAT64_ARRAY);
- base_type_map.insert("PackedStringArray", Variant::PACKED_STRING_ARRAY);
- base_type_map.insert("PackedVector2Array", Variant::PACKED_VECTOR2_ARRAY);
- base_type_map.insert("PackedVector3Array", Variant::PACKED_VECTOR3_ARRAY);
- base_type_map.insert("PackedColorArray", Variant::PACKED_COLOR_ARRAY);
-}
-
-VisualScriptEditor::~VisualScriptEditor() {
- undo_redo->clear_history(); // Avoid crashes.
- memdelete(signal_editor);
- memdelete(variable_editor);
-}
-
-static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) {
- if (Object::cast_to<VisualScript>(*p_resource)) {
- return memnew(VisualScriptEditor);
- }
-
- return nullptr;
-}
-
-VisualScriptEditor::Clipboard *VisualScriptEditor::clipboard = nullptr;
-
-void VisualScriptEditor::free_clipboard() {
- if (clipboard) {
- memdelete(clipboard);
- }
-}
-
-static void register_editor_callback() {
- ScriptEditor::register_create_script_editor_function(create_editor);
-
- ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), Key::F9);
- ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KeyModifierMask::CMD + Key::F);
- ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KeyModifierMask::CMD + Key::G);
- ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KeyModifierMask::CMD + Key::R);
- ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KeyModifierMask::CMD + Key::E);
-}
-
-void VisualScriptEditor::register_editor() {
- // Too early to register stuff here, request a callback.
- EditorNode::add_plugin_init_callback(register_editor_callback);
-}
-
-void VisualScriptEditor::validate() {
-}
-
-// VisualScriptCustomNodes
-
-Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &p_name) {
- Ref<VisualScriptCustomNode> node;
- node.instantiate();
- node->set_script(singleton->custom_nodes[p_name]);
- return node;
-}
-
-VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr;
-HashMap<String, Ref<RefCounted>> VisualScriptCustomNodes::custom_nodes;
-
-VisualScriptCustomNodes::VisualScriptCustomNodes() {
- singleton = this;
-}
-
-VisualScriptCustomNodes::~VisualScriptCustomNodes() {
- custom_nodes.clear();
-}
-
-void VisualScriptCustomNodes::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
- String node_name = "custom/" + p_category + "/" + p_name;
- custom_nodes.insert(node_name, p_script);
- VisualScriptLanguage::singleton->add_register_func(node_name, &VisualScriptCustomNodes::create_node_custom);
- emit_signal(SNAME("custom_nodes_updated"));
-}
-
-void VisualScriptCustomNodes::remove_custom_node(const String &p_name, const String &p_category) {
- String node_name = "custom/" + p_category + "/" + p_name;
- custom_nodes.erase(node_name);
- VisualScriptLanguage::singleton->remove_register_func(node_name);
- emit_signal(SNAME("custom_nodes_updated"));
-}
-
-void VisualScriptCustomNodes::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &VisualScriptCustomNodes::add_custom_node);
- ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &VisualScriptCustomNodes::remove_custom_node);
- ADD_SIGNAL(MethodInfo("custom_nodes_updated"));
-}
-
-#endif
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
deleted file mode 100644
index a6df7bba73..0000000000
--- a/modules/visual_script/editor/visual_script_editor.h
+++ /dev/null
@@ -1,377 +0,0 @@
-/*************************************************************************/
-/* visual_script_editor.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 VISUAL_SCRIPT_EDITOR_H
-#define VISUAL_SCRIPT_EDITOR_H
-
-#include "../visual_script.h"
-#include "editor/create_dialog.h"
-#include "editor/plugins/script_editor_plugin.h"
-#include "editor/property_editor.h"
-#include "scene/gui/graph_edit.h"
-#include "visual_script_property_selector.h"
-
-class VisualScriptEditorSignalEdit;
-class VisualScriptEditorVariableEdit;
-
-#ifdef TOOLS_ENABLED
-
-// TODO: Maybe this class should be refactored.
-// See https://github.com/godotengine/godot/issues/51913
-class VisualScriptEditor : public ScriptEditorBase {
- GDCLASS(VisualScriptEditor, ScriptEditorBase);
-
- enum {
- TYPE_SEQUENCE = 1000,
- INDEX_BASE_SEQUENCE = 1024
- };
-
- enum {
- EDIT_ADD_NODE,
- EDIT_SEPARATOR, // popup menu separator - ignored
- EDIT_CUT_NODES,
- EDIT_COPY_NODES,
- EDIT_PASTE_NODES,
- EDIT_DELETE_NODES,
- EDIT_DUPLICATE_NODES,
- EDIT_CLEAR_COPY_BUFFER,
-
- EDIT_CREATE_FUNCTION,
- EDIT_TOGGLE_BREAKPOINT,
- EDIT_FIND_NODE_TYPE,
- REFRESH_GRAPH,
- };
-
- enum PortAction {
- CREATE_CALL_SET_GET,
- CREATE_ACTION,
- };
-
- enum MemberAction {
- MEMBER_EDIT,
- MEMBER_REMOVE
- };
-
- enum MemberType {
- MEMBER_FUNCTION,
- MEMBER_VARIABLE,
- MEMBER_SIGNAL
- };
-
- VBoxContainer *members_section = nullptr;
- MenuButton *edit_menu = nullptr;
-
- Ref<VisualScript> script;
-
- Button *base_type_select = nullptr;
-
- LineEdit *func_name_box = nullptr;
- ScrollContainer *func_input_scroll = nullptr;
- VBoxContainer *func_input_vbox = nullptr;
- ConfirmationDialog *function_create_dialog = nullptr;
-
- GraphEdit *graph = nullptr;
- HBoxContainer *status_bar = nullptr;
- Button *toggle_scripts_button = nullptr;
-
- VisualScriptEditorSignalEdit *signal_editor = nullptr;
-
- AcceptDialog *edit_signal_dialog = nullptr;
- EditorInspector *edit_signal_edit = nullptr;
-
- VisualScriptPropertySelector *method_select = nullptr;
- VisualScriptPropertySelector *new_connect_node_select = nullptr;
- VisualScriptPropertySelector *new_virtual_method_select = nullptr;
-
- VisualScriptEditorVariableEdit *variable_editor = nullptr;
-
- AcceptDialog *edit_variable_dialog = nullptr;
- EditorInspector *edit_variable_edit = nullptr;
-
- CustomPropertyEditor *default_value_edit = nullptr;
-
- UndoRedo *undo_redo = nullptr;
-
- Tree *members = nullptr;
- AcceptDialog *function_name_edit = nullptr;
- LineEdit *function_name_box = nullptr;
-
- Label *hint_text = nullptr;
- Timer *hint_text_timer = nullptr;
-
- Label *select_func_text = nullptr;
-
- bool updating_graph = false;
-
- void _show_hint(const String &p_hint);
- void _hide_timer();
-
- CreateDialog *select_base_type = nullptr;
-
- struct VirtualInMenu {
- String name;
- Variant::Type ret;
- bool ret_variant;
- Vector<Pair<Variant::Type, String>> args;
- };
-
- HashMap<StringName, Color> node_colors;
- HashMap<StringName, Ref<StyleBox>> node_styles;
- HashMap<StringName, Variant::Type> base_type_map;
-
- void _update_graph_connections();
- void _update_graph(int p_only_id = -1);
-
- bool updating_members = false;
-
- void _update_members();
- String _sanitized_variant_text(const StringName &property_name);
-
- StringName selected;
-
- String _validate_name(const String &p_name) const;
-
- struct Clipboard {
- HashMap<int, Ref<VisualScriptNode>> nodes;
- HashMap<int, Vector2> nodes_positions;
-
- RBSet<VisualScript::SequenceConnection> sequence_connections;
- RBSet<VisualScript::DataConnection> data_connections;
- };
-
- static Clipboard *clipboard;
-
- PopupMenu *popup_menu = nullptr;
- PopupMenu *member_popup = nullptr;
- MemberType member_type;
- String member_name;
-
- PortAction port_action;
- int port_action_node = 0;
- int port_action_output = 0;
- Vector2 port_action_pos;
- int port_action_new_node = 0;
-
- bool saved_pos_dirty = false;
-
- Vector2 mouse_up_position;
-
- void _port_action_menu(int p_option);
-
- void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id);
-
- NodePath drop_path;
- Node *drop_node = nullptr;
- Vector2 drop_position;
- void _selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting = true);
- void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id);
-
- void _cancel_connect_node();
- int _create_new_node_from_name(const String &p_text, const Vector2 &p_point);
- void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
-
- int error_line = -1;
-
- void _node_selected(Node *p_node);
- void _center_on_node(int p_id);
-
- void _node_filter_changed(const String &p_text);
- void _change_base_type_callback();
- void _change_base_type();
- void _toggle_tool_script();
- void _member_selected();
- void _member_edited();
-
- void _begin_node_move();
- 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 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);
- void _graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot);
- void _graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot);
- void _graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos);
-
- void _node_ports_changed(int p_id);
- void _node_create();
-
- void _update_available_nodes();
-
- 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);
- void _add_output_port(int p_id);
- void _remove_input_port(int p_id, int p_port);
- void _remove_output_port(int p_id, int p_port);
- void _change_port_type(int p_select, int p_id, int p_port, bool is_input);
- void _update_node_size(int p_id);
- void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input);
-
- Vector2 _get_pos_in_graph(Vector2 p_point) const;
- Vector2 _get_available_pos(bool p_centered = true, Vector2 p_pos = Vector2()) const;
-
- bool node_has_sequence_connections(int p_id);
-
- void _generic_search(Vector2 pos = Vector2(), bool node_centered = false);
-
- virtual void input(const Ref<InputEvent> &p_event) override;
- void _graph_gui_input(const Ref<InputEvent> &p_event);
- void _members_gui_input(const Ref<InputEvent> &p_event);
- void _fn_name_box_input(const Ref<InputEvent> &p_event);
- void _on_fn_name_box_confirmed();
- void _rename_function(const String &p_name, const String &p_new_name);
-
- void _create_function_dialog();
- void _create_function();
- void _add_func_input();
- void _remove_func_input(Node *p_node);
- void _deselect_input_names();
- void _add_node_dialog();
- void _node_item_selected();
- void _node_item_unselected();
-
- void _on_nodes_copy();
- void _on_nodes_paste();
- void _on_nodes_delete();
- void _on_nodes_duplicate();
-
- Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
-
- int editing_id = 0;
- int editing_input = 0;
-
- bool can_swap = false;
- int data_disconnect_node = 0;
- int data_disconnect_port = 0;
-
- void _default_value_changed();
- void _default_value_edited(Node *p_button, int p_id, int p_input_port);
-
- void _menu_option(int p_what);
-
- void _graph_ofs_changed(const Vector2 &p_ofs);
- void _comment_node_resized(const Vector2 &p_new_size, int p_node);
-
- 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, RBSet<int> &p_visited_nodes);
-
- void _member_rmb_selected(const Vector2 &p_pos, MouseButton p_button);
- void _member_option(int p_option);
-
- void _toggle_scripts_pressed();
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
- virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
-
- virtual void apply_code() 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;
- virtual String get_name() override;
- virtual Ref<Texture2D> get_theme_icon() override;
- virtual bool is_unsaved() override;
- virtual Variant get_edit_state() override;
- virtual void set_edit_state(const Variant &p_state) override;
- virtual void goto_line(int p_line, bool p_with_error = false) override;
- virtual void set_executing_line(int p_line) override;
- virtual void clear_executing_line() override;
- virtual void trim_trailing_whitespace() override;
- virtual void insert_final_newline() override;
- virtual void convert_indent_to_spaces() override;
- virtual void convert_indent_to_tabs() override;
- virtual void ensure_focus() override;
- virtual void tag_saved_version() override;
- virtual void reload(bool p_soft) override;
- virtual Array get_breakpoints() override;
- virtual void set_breakpoint(int p_line, bool p_enable) override{};
- virtual void clear_breakpoints() override{};
- virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
- virtual void update_settings() override;
- virtual bool show_members_overview() override;
- virtual void set_debugger_active(bool p_active) override;
- virtual void set_tooltip_request_func(const Callable &p_toolip_callback) override;
- virtual Control *get_edit_menu() override;
- virtual void clear_edit_menu() override;
- virtual void set_find_replace_bar(FindReplaceBar *p_bar) override { p_bar->hide(); }; // Not needed here.
- virtual bool can_lose_focus_on_node_selection() override { return false; }
- virtual void validate() override;
-
- virtual Control *get_base_editor() const override;
-
- static void register_editor();
-
- static void free_clipboard();
-
- void update_toggle_scripts_button() override;
-
- VisualScriptEditor();
- ~VisualScriptEditor();
-};
-
-// Singleton
-class VisualScriptCustomNodes : public Object {
- GDCLASS(VisualScriptCustomNodes, Object);
-
- friend class VisualScriptLanguage;
-
-protected:
- static void _bind_methods();
- static VisualScriptCustomNodes *singleton;
-
- static HashMap<String, Ref<RefCounted>> custom_nodes;
- static Ref<VisualScriptNode> create_node_custom(const String &p_name);
-
-public:
- static VisualScriptCustomNodes *get_singleton() { return singleton; }
-
- void add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script);
- void remove_custom_node(const String &p_name, const String &p_category);
-
- VisualScriptCustomNodes();
- ~VisualScriptCustomNodes();
-};
-
-#endif
-
-#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
deleted file mode 100644
index f98ce5bb26..0000000000
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ /dev/null
@@ -1,1276 +0,0 @@
-/*************************************************************************/
-/* visual_script_property_selector.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 "visual_script_property_selector.h"
-
-#include "../visual_script.h"
-#include "../visual_script_builtin_funcs.h"
-#include "../visual_script_flow_control.h"
-#include "../visual_script_func_nodes.h"
-#include "../visual_script_nodes.h"
-#include "core/os/keyboard.h"
-#include "editor/doc_tools.h"
-#include "editor/editor_feature_profile.h"
-#include "editor/editor_scale.h"
-#include "scene/main/node.h"
-#include "scene/main/window.h"
-
-void VisualScriptPropertySelector::_update_icons() {
- search_box->set_right_icon(results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
- search_box->set_clear_button_enabled(true);
- search_box->add_theme_icon_override("right_icon", results_tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
-
- search_visual_script_nodes->set_icon(results_tree->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
- search_classes->set_icon(results_tree->get_theme_icon(SNAME("Object"), SNAME("EditorIcons")));
- search_methods->set_icon(results_tree->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
- search_operators->set_icon(results_tree->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- search_signals->set_icon(results_tree->get_theme_icon(SNAME("MemberSignal"), SNAME("EditorIcons")));
- search_constants->set_icon(results_tree->get_theme_icon(SNAME("MemberConstant"), SNAME("EditorIcons")));
- search_properties->set_icon(results_tree->get_theme_icon(SNAME("MemberProperty"), SNAME("EditorIcons")));
- search_theme_items->set_icon(results_tree->get_theme_icon(SNAME("MemberTheme"), SNAME("EditorIcons")));
-
- case_sensitive_button->set_icon(results_tree->get_theme_icon(SNAME("MatchCase"), SNAME("EditorIcons")));
- hierarchy_button->set_icon(results_tree->get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
-}
-
-void VisualScriptPropertySelector::_sbox_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: {
- results_tree->gui_input(k);
- search_box->accept_event();
- } break;
- default:
- break;
- }
- }
-}
-
-void VisualScriptPropertySelector::_update_results_i(int p_int) {
- _update_results();
-}
-
-void VisualScriptPropertySelector::_update_results_s(String p_string) {
- _update_results();
-}
-
-void VisualScriptPropertySelector::_update_results_search_all() {
- if (search_classes->is_pressed()) {
- scope_combo->select(COMBO_ALL);
- }
- _update_results();
-}
-
-void VisualScriptPropertySelector::_update_results() {
- _update_icons();
- search_runner = Ref<SearchRunner>(memnew(SearchRunner(this, results_tree)));
- set_process(true);
-}
-
-void VisualScriptPropertySelector::_confirmed() {
- TreeItem *ti = results_tree->get_selected();
- if (!ti) {
- return;
- }
- emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), connecting);
- set_visible(false);
-}
-
-void VisualScriptPropertySelector::_item_selected() {
- help_bit->set_text(results_tree->get_selected()->get_meta("description", "No description available"));
-}
-
-void VisualScriptPropertySelector::_hide_requested() {
- _cancel_pressed(); // From AcceptDialog.
-}
-
-void VisualScriptPropertySelector::_notification(int p_what) {
- switch (p_what) {
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- _update_icons();
- } break;
-
- case NOTIFICATION_ENTER_TREE: {
- connect("confirmed", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
- } break;
-
- case NOTIFICATION_PROCESS: {
- // Update background search.
- if (search_runner.is_valid()) {
- if (search_runner->work()) {
- // Search done.
- get_ok_button()->set_disabled(!results_tree->get_selected());
-
- search_runner = Ref<SearchRunner>();
- set_process(false);
- }
- } else {
- // if one is valid
- set_process(false);
- }
- } break;
- }
-}
-
-void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const bool p_virtuals_only, const bool p_connecting, bool clear_text) {
- set_title(TTR("Select method from base type"));
- base_type = p_base;
- base_script = "";
- type = Variant::NIL;
- connecting = p_connecting;
-
- if (clear_text) {
- if (p_virtuals_only) {
- search_box->set_text("._"); // show all _methods
- search_box->set_caret_column(2);
- } else {
- search_box->set_text("."); // show all methods
- search_box->set_caret_column(1);
- }
- }
-
- search_visual_script_nodes->set_pressed(false);
- search_classes->set_pressed(false);
- search_methods->set_pressed(true);
- search_operators->set_pressed(false);
- search_signals->set_pressed(false);
- search_constants->set_pressed(false);
- search_properties->set_pressed(false);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_BASE);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
-
- _update_results();
-}
-
-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.trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
- type = Variant::NIL;
- connecting = p_connecting;
-
- if (clear_text) {
- if (p_virtuals_only) {
- search_box->set_text("_");
- } else {
- search_box->set_text(" ");
- }
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(false);
- search_classes->set_pressed(false);
- search_methods->set_pressed(true);
- search_operators->set_pressed(false);
- search_signals->set_pressed(true);
- search_constants->set_pressed(false);
- search_properties->set_pressed(true);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_RELATED);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
- _update_results();
-}
-
-void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const bool p_connecting, bool clear_text) {
- set_title(TTR("Select from script"));
- ERR_FAIL_COND(p_script.is_null());
-
- base_type = p_script->get_instance_base_type();
- 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;
-
- if (clear_text) {
- search_box->set_text("");
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(false);
- search_classes->set_pressed(true);
- search_methods->set_pressed(true);
- search_operators->set_pressed(true);
- search_signals->set_pressed(true);
- search_constants->set_pressed(true);
- search_properties->set_pressed(true);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_BASE);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
- _update_results();
-}
-
-void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const bool p_connecting, bool clear_text) {
- set_title(TTR("Select from basic type"));
- ERR_FAIL_COND(p_type == Variant::NIL);
- base_type = Variant::get_type_name(p_type);
- base_script = "";
- type = p_type;
- connecting = p_connecting;
-
- if (clear_text) {
- search_box->set_text(" ");
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(false);
- search_classes->set_pressed(false);
- search_methods->set_pressed(true);
- search_operators->set_pressed(true);
- search_signals->set_pressed(false);
- search_constants->set_pressed(true);
- search_properties->set_pressed(true);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_BASE);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
-
- _update_results();
-}
-
-void VisualScriptPropertySelector::select_from_action(const String &p_type, const bool p_connecting, bool clear_text) {
- set_title(TTR("Select from action"));
- base_type = p_type;
- base_script = "";
- type = Variant::NIL;
- connecting = p_connecting;
-
- if (clear_text) {
- search_box->set_text("");
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(true);
- search_classes->set_pressed(false);
- search_methods->set_pressed(false);
- search_operators->set_pressed(false);
- search_signals->set_pressed(false);
- search_constants->set_pressed(false);
- search_properties->set_pressed(false);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_RELATED);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
- _update_results();
-}
-
-void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const bool p_connecting, bool clear_text) {
- set_title(TTR("Select from instance"));
- base_type = p_instance->get_class();
-
- const Ref<Script> &p_script = p_instance->get_script();
- if (p_script == nullptr) {
- base_script = "";
- } else {
- base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
- }
-
- type = Variant::NIL;
- connecting = p_connecting;
-
- if (clear_text) {
- search_box->set_text(" ");
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(false);
- search_classes->set_pressed(false);
- search_methods->set_pressed(true);
- search_operators->set_pressed(false);
- search_signals->set_pressed(true);
- search_constants->set_pressed(true);
- search_properties->set_pressed(true);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_BASE);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
- _update_results();
-}
-
-void VisualScriptPropertySelector::select_from_visual_script(const Ref<Script> &p_script, bool clear_text) {
- set_title(TTR("Select from visual script"));
- base_type = p_script->get_instance_base_type();
- if (p_script == nullptr) {
- base_script = "";
- } else {
- base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
- }
- type = Variant::NIL;
- connecting = false;
-
- if (clear_text) {
- search_box->set_text(" ");
- }
- search_box->select_all();
-
- search_visual_script_nodes->set_pressed(true);
- search_classes->set_pressed(false);
- search_methods->set_pressed(true);
- search_operators->set_pressed(false);
- search_signals->set_pressed(true);
- search_constants->set_pressed(true);
- search_properties->set_pressed(true);
- search_theme_items->set_pressed(false);
-
- scope_combo->select(COMBO_BASE);
-
- results_tree->clear();
- show_window(.5f);
- search_box->grab_focus();
- _update_results();
-}
-
-void VisualScriptPropertySelector::show_window(float p_screen_ratio) {
- popup_centered_ratio(p_screen_ratio);
-}
-
-void VisualScriptPropertySelector::_bind_methods() {
- ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "category"), PropertyInfo(Variant::BOOL, "connecting")));
-}
-
-VisualScriptPropertySelector::VisualScriptPropertySelector() {
- vbox = memnew(VBoxContainer);
- add_child(vbox);
-
- HBoxContainer *hbox = memnew(HBoxContainer);
- hbox->set_alignment(hbox->ALIGNMENT_CENTER);
- vbox->add_child(hbox);
-
- case_sensitive_button = memnew(Button);
- case_sensitive_button->set_flat(true);
- case_sensitive_button->set_tooltip(TTR("Case Sensitive"));
- case_sensitive_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- case_sensitive_button->set_toggle_mode(true);
- case_sensitive_button->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(case_sensitive_button);
-
- hierarchy_button = memnew(Button);
- hierarchy_button->set_flat(true);
- hierarchy_button->set_tooltip(TTR("Show Hierarchy"));
- hierarchy_button->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- hierarchy_button->set_toggle_mode(true);
- hierarchy_button->set_pressed(true);
- hierarchy_button->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(hierarchy_button);
-
- hbox->add_child(memnew(VSeparator));
-
- search_visual_script_nodes = memnew(Button);
- search_visual_script_nodes->set_flat(true);
- search_visual_script_nodes->set_tooltip(TTR("Search Visual Script Nodes"));
- search_visual_script_nodes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_visual_script_nodes->set_toggle_mode(true);
- search_visual_script_nodes->set_pressed(true);
- search_visual_script_nodes->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_visual_script_nodes);
-
- search_classes = memnew(Button);
- search_classes->set_flat(true);
- search_classes->set_tooltip(TTR("Search Classes"));
- search_classes->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results_search_all));
- search_classes->set_toggle_mode(true);
- search_classes->set_pressed(true);
- search_classes->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_classes);
-
- search_operators = memnew(Button);
- search_operators->set_flat(true);
- search_operators->set_tooltip(TTR("Search Operators"));
- search_operators->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_operators->set_toggle_mode(true);
- search_operators->set_pressed(true);
- search_operators->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_operators);
-
- hbox->add_child(memnew(VSeparator));
-
- search_methods = memnew(Button);
- search_methods->set_flat(true);
- search_methods->set_tooltip(TTR("Search Methods"));
- search_methods->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_methods->set_toggle_mode(true);
- search_methods->set_pressed(true);
- search_methods->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_methods);
-
- search_signals = memnew(Button);
- search_signals->set_flat(true);
- search_signals->set_tooltip(TTR("Search Signals"));
- search_signals->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_signals->set_toggle_mode(true);
- search_signals->set_pressed(true);
- search_signals->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_signals);
-
- search_constants = memnew(Button);
- search_constants->set_flat(true);
- search_constants->set_tooltip(TTR("Search Constants"));
- search_constants->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_constants->set_toggle_mode(true);
- search_constants->set_pressed(true);
- search_constants->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_constants);
-
- search_properties = memnew(Button);
- search_properties->set_flat(true);
- search_properties->set_tooltip(TTR("Search Properties"));
- search_properties->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_properties->set_toggle_mode(true);
- search_properties->set_pressed(true);
- search_properties->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_properties);
-
- search_theme_items = memnew(Button);
- search_theme_items->set_flat(true);
- search_theme_items->set_tooltip(TTR("Search Theme Items"));
- search_theme_items->connect("pressed", callable_mp(this, &VisualScriptPropertySelector::_update_results));
- search_theme_items->set_toggle_mode(true);
- search_theme_items->set_pressed(true);
- search_theme_items->set_focus_mode(Control::FOCUS_NONE);
- hbox->add_child(search_theme_items);
-
- scope_combo = memnew(OptionButton);
- scope_combo->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
- scope_combo->set_tooltip(TTR("Select the search limits"));
- scope_combo->set_stretch_ratio(0); // Fixed width.
- scope_combo->add_item(TTR("Search Related"), SCOPE_RELATED);
- scope_combo->add_separator();
- scope_combo->add_item(TTR("Search Base"), SCOPE_BASE);
- scope_combo->add_item(TTR("Search Inheriters"), SCOPE_INHERITERS);
- scope_combo->add_item(TTR("Search Unrelated"), SCOPE_UNRELATED);
- scope_combo->add_item(TTR("Search All"), SCOPE_ALL);
- scope_combo->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_update_results_i));
- hbox->add_child(scope_combo);
-
- search_box = memnew(LineEdit);
- search_box->set_tooltip(TTR("Enter \" \" to show all filtered options\nEnter \".\" to show all filtered methods, operators and constructors\nUse CTRL_KEY to drop property setters"));
- search_box->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
- search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- search_box->connect("text_changed", callable_mp(this, &VisualScriptPropertySelector::_update_results_s));
- search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
- register_text_enter(search_box);
- vbox->add_child(search_box);
-
- results_tree = memnew(Tree);
- results_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- results_tree->set_hide_root(true);
- results_tree->set_hide_folding(false);
- results_tree->set_columns(2);
- results_tree->set_column_title(0, TTR("Name"));
- results_tree->set_column_clip_content(0, true);
- results_tree->set_column_title(1, TTR("Member Type"));
- results_tree->set_column_expand(1, false);
- results_tree->set_column_custom_minimum_width(1, 150 * EDSCALE);
- results_tree->set_column_clip_content(1, true);
- results_tree->set_custom_minimum_size(Size2(0, 100) * EDSCALE);
- results_tree->set_select_mode(Tree::SELECT_ROW);
- results_tree->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
- 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);
- 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));
- set_ok_button_text(TTR("Open"));
- get_ok_button()->set_disabled(true);
- set_hide_on_ok(false);
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
- Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
- if (profile.is_null()) {
- return false;
- }
-
- StringName class_name = p_class;
- while (class_name != StringName()) {
- if (!ClassDB::class_exists(class_name)) {
- return false;
- }
-
- if (profile->is_class_disabled(class_name)) {
- return true;
- }
- class_name = ClassDB::get_parent_class(class_name);
- }
-
- return false;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_is_class_disabled_by_scope(const StringName &p_class) {
- bool is_base_script = false;
- if (p_class == selector_ui->base_script) {
- is_base_script = true;
- }
- bool is_base = false;
- if (selector_ui->base_type == p_class) {
- is_base = true;
- }
- bool is_parent = false;
- if ((ClassDB::is_parent_class(selector_ui->base_type, p_class)) && !is_base) {
- is_parent = true;
- }
-
- bool is_inheriter = false;
- List<StringName> inheriters;
- ClassDB::get_inheriters_from_class(selector_ui->base_type, &inheriters);
- if (inheriters.find(p_class)) {
- is_inheriter = true;
- }
-
- if (scope_flags & SCOPE_BASE) {
- if (is_base_script || is_base || is_parent) {
- return false;
- }
- }
- if (scope_flags & SCOPE_INHERITERS) {
- if (is_base_script || is_base || is_inheriter) {
- return false;
- }
- }
- // if (scope_flags & SCOPE_RELATED) {
- // /* code */
- // }
- if (scope_flags & SCOPE_UNRELATED) {
- if (!is_base_script && !is_base && !is_inheriter) {
- return false;
- }
- }
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_slice() {
- bool phase_done = false;
- switch (phase) {
- case PHASE_INIT:
- phase_done = _phase_init();
- break;
- case PHASE_MATCH_CLASSES_INIT:
- phase_done = _phase_match_classes_init();
- break;
- case PHASE_NODE_CLASSES_INIT:
- phase_done = _phase_node_classes_init();
- break;
- case PHASE_NODE_CLASSES_BUILD:
- phase_done = _phase_node_classes_build();
- break;
- case PHASE_MATCH_CLASSES:
- phase_done = _phase_match_classes();
- break;
- case PHASE_CLASS_ITEMS_INIT:
- phase_done = _phase_class_items_init();
- break;
- case PHASE_CLASS_ITEMS:
- phase_done = _phase_class_items();
- break;
- case PHASE_MEMBER_ITEMS_INIT:
- phase_done = _phase_member_items_init();
- break;
- case PHASE_MEMBER_ITEMS:
- phase_done = _phase_member_items();
- break;
- case PHASE_SELECT_MATCH:
- phase_done = _phase_select_match();
- break;
- case PHASE_MAX:
- return true;
- default:
- WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
- return true;
- };
-
- if (phase_done) {
- phase++;
- }
- return false;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_init() {
- search_flags = 0; // selector_ui->filter_combo->get_selected_id();
- if (selector_ui->search_visual_script_nodes->is_pressed()) {
- search_flags |= SEARCH_VISUAL_SCRIPT_NODES;
- }
- if (selector_ui->search_classes->is_pressed()) {
- search_flags |= SEARCH_CLASSES;
- }
- // if (selector_ui->search_constructors->is_pressed()) {
- search_flags |= SEARCH_CONSTRUCTORS;
- // }
- if (selector_ui->search_methods->is_pressed()) {
- search_flags |= SEARCH_METHODS;
- }
- if (selector_ui->search_operators->is_pressed()) {
- search_flags |= SEARCH_OPERATORS;
- }
- if (selector_ui->search_signals->is_pressed()) {
- search_flags |= SEARCH_SIGNALS;
- }
- if (selector_ui->search_constants->is_pressed()) {
- search_flags |= SEARCH_CONSTANTS;
- }
- if (selector_ui->search_properties->is_pressed()) {
- search_flags |= SEARCH_PROPERTIES;
- }
- if (selector_ui->search_theme_items->is_pressed()) {
- search_flags |= SEARCH_THEME_ITEMS;
- }
- if (selector_ui->case_sensitive_button->is_pressed()) {
- search_flags |= SEARCH_CASE_SENSITIVE;
- }
- if (selector_ui->hierarchy_button->is_pressed()) {
- search_flags |= SEARCH_SHOW_HIERARCHY;
- }
- scope_flags = selector_ui->scope_combo->get_selected_id();
-
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes_init() {
- combined_docs = EditorHelp::get_doc_data()->class_list;
- matches.clear();
- matched_item = nullptr;
- match_highest_score = 0;
-
- if (
- (selector_ui->base_script.unquote() != "") &&
- (selector_ui->base_script.unquote() != ".") &&
- !combined_docs.has(selector_ui->base_script)) {
- String file_path = "res://" + selector_ui->base_script.unquote(); // EditorHelp::get_doc_data().name to filepath
- Ref<Script> script;
- script = ResourceLoader::load(file_path);
- if (!script.is_null()) {
- DocData::ClassDoc class_doc = DocData::ClassDoc();
-
- class_doc.name = selector_ui->base_script;
-
- class_doc.inherits = script->get_instance_base_type();
- class_doc.brief_description = ".vs files not supported by EditorHelp::get_doc_data()";
- class_doc.description = "";
-
- Object *obj = ObjectDB::get_instance(script->get_instance_id());
- if (Object::cast_to<Script>(obj)) {
- List<MethodInfo> methods;
- Object::cast_to<Script>(obj)->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
- class_doc.methods.push_back(_get_method_doc(M->get()));
- }
-
- List<MethodInfo> signals;
- Object::cast_to<Script>(obj)->get_script_signal_list(&signals);
- for (List<MethodInfo>::Element *S = signals.front(); S; S = S->next()) {
- class_doc.signals.push_back(_get_method_doc(S->get()));
- }
-
- List<PropertyInfo> properties;
- Object::cast_to<Script>(obj)->get_script_property_list(&properties);
- for (List<PropertyInfo>::Element *P = properties.front(); P; P = P->next()) {
- DocData::PropertyDoc pd = DocData::PropertyDoc();
- pd.name = P->get().name;
- pd.type = Variant::get_type_name(P->get().type);
- class_doc.properties.push_back(pd);
- }
- }
- combined_docs.insert(class_doc.name, class_doc);
- }
- }
- iterator_doc = combined_docs.begin();
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_init() {
- VisualScriptLanguage::singleton->get_registered_node_names(&vs_nodes);
- _add_class_doc("functions", "", "");
- _add_class_doc("operators", "", "");
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_build() {
- if (vs_nodes.is_empty()) {
- return true;
- }
- String registered_node_name = vs_nodes[0];
- vs_nodes.pop_front();
-
- Vector<String> path = registered_node_name.split("/");
- if (path[0] == "constants") {
- _add_class_doc(registered_node_name, "", "constants");
- } else if (path[0] == "custom") {
- _add_class_doc(registered_node_name, "", "custom");
- } else if (path[0] == "data") {
- _add_class_doc(registered_node_name, "", "data");
- } else if (path[0] == "flow_control") {
- _add_class_doc(registered_node_name, "", "flow_control");
- } else if (path[0] == "functions") {
- if (path[1] == "built_in") {
- _add_class_doc(registered_node_name, "functions", "built_in");
- } else if (path[1] == "by_type") {
- // No action is required.
- // Using function references from ClassDB to remove confusion for users.
- } else if (path[1] == "constructors") {
- _add_class_doc(registered_node_name, "", "constructors");
- } else if (path[1] == "deconstruct") {
- _add_class_doc(registered_node_name, "", "deconstruct");
- } else if (path[1] == "wait") {
- _add_class_doc(registered_node_name, "functions", "yield");
- } else {
- _add_class_doc(registered_node_name, "functions", "");
- }
- } else if (path[0] == "index") {
- _add_class_doc(registered_node_name, "", "index");
- } else if (path[0] == "operators") {
- if (path[1] == "bitwise") {
- _add_class_doc(registered_node_name, "operators", "bitwise");
- } else if (path[1] == "compare") {
- _add_class_doc(registered_node_name, "operators", "compare");
- } else if (path[1] == "logic") {
- _add_class_doc(registered_node_name, "operators", "logic");
- } else if (path[1] == "math") {
- _add_class_doc(registered_node_name, "operators", "math");
- } else {
- _add_class_doc(registered_node_name, "operators", "");
- }
- }
- return false;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
- 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)) {
- if (class_doc.inherits == "VisualScriptCustomNode") {
- class_doc.script_path = "res://" + class_doc.name.unquote();
- Ref<Script> script = ResourceLoader::load(class_doc.script_path);
- Ref<VisualScriptCustomNode> vsn;
- vsn.instantiate();
- vsn->set_script(script);
- class_doc.name = vsn->get_caption();
- if (combined_docs.has(vsn->get_category())) {
- class_doc.inherits = vsn->get_category();
- } else if (combined_docs.has("VisualScriptNode/" + vsn->get_category())) {
- class_doc.inherits = "VisualScriptNode/" + vsn->get_category();
- } else if (combined_docs.has("VisualScriptCustomNode/" + vsn->get_category())) {
- class_doc.inherits = "VisualScriptCustomNode/" + vsn->get_category();
- } else {
- class_doc.inherits = "";
- }
- class_doc.category = "VisualScriptCustomNode/" + vsn->get_category();
- class_doc.brief_description = "";
- class_doc.constructors.clear();
- class_doc.methods.clear();
- class_doc.operators.clear();
- class_doc.signals.clear();
- class_doc.constants.clear();
- class_doc.enums.clear();
- class_doc.properties.clear();
- class_doc.theme_properties.clear();
- }
-
- matches[class_doc.name] = ClassMatch();
- ClassMatch &match = matches[class_doc.name];
-
- match.category = class_doc.category;
- match.doc = &class_doc;
- // Match class name.
- if (search_flags & SEARCH_CLASSES || _match_visual_script(class_doc)) {
- if (term == "") {
- match.name = !_match_is_hidden(class_doc);
- } else {
- match.name = _match_string(term, class_doc.name);
- }
- // match.name = term == "" || _match_string(term, class_doc.name);
- }
-
- // Match members if the term is long enough.
- if (term.length() >= 0) {
- if (search_flags & SEARCH_CONSTRUCTORS) {
- for (int i = 0; i < class_doc.constructors.size(); i++) {
- String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.constructors[i].name : class_doc.constructors[i].name.to_lower();
- if (method_name.find(term) > -1 ||
- term == " " ||
- (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
- (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
- (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
- match.constructors.push_back(const_cast<DocData::MethodDoc *>(&class_doc.constructors[i]));
- }
- }
- }
- if (search_flags & SEARCH_METHODS) {
- for (int i = 0; i < class_doc.methods.size(); i++) {
- String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
- if (method_name.find(term) > -1 ||
- term == " " ||
- (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
- (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
- (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
- match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
- }
- }
- }
- if (search_flags & SEARCH_OPERATORS) {
- for (int i = 0; i < class_doc.operators.size(); i++) {
- String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? class_doc.operators[i].name : class_doc.operators[i].name.to_lower();
- if (method_name.find(term) > -1 ||
- term == " " ||
- (term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
- (term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
- (term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
- match.operators.push_back(const_cast<DocData::MethodDoc *>(&class_doc.operators[i]));
- }
- }
- }
- if (search_flags & SEARCH_SIGNALS) {
- for (int i = 0; i < class_doc.signals.size(); i++) {
- if (_match_string(term, class_doc.signals[i].name) ||
- term == " ") {
- match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
- }
- }
- }
- if (search_flags & SEARCH_CONSTANTS) {
- for (int i = 0; i < class_doc.constants.size(); i++) {
- if (_match_string(term, class_doc.constants[i].name) ||
- term == " ") {
- match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
- }
- }
- }
- if (search_flags & SEARCH_PROPERTIES) {
- for (int i = 0; i < class_doc.properties.size(); i++) {
- if (_match_string(term, class_doc.properties[i].name) ||
- term == " " ||
- _match_string(term, class_doc.properties[i].getter) ||
- _match_string(term, class_doc.properties[i].setter)) {
- match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
- }
- }
- }
- if (search_flags & SEARCH_THEME_ITEMS) {
- for (int i = 0; i < class_doc.theme_properties.size(); i++) {
- if (_match_string(term, class_doc.theme_properties[i].name) ||
- term == " ") {
- match.theme_properties.push_back(const_cast<DocData::ThemeItemDoc *>(&class_doc.theme_properties[i]));
- }
- }
- }
- }
- }
-
- ++iterator_doc;
- return !iterator_doc;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_class_items_init() {
- results_tree->clear();
- iterator_match = matches.begin();
-
- root_item = results_tree->create_item();
- class_items.clear();
-
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
- if (!iterator_match) {
- return true;
- }
-
- ClassMatch &match = iterator_match->value;
-
- if (search_flags & SEARCH_SHOW_HIERARCHY) {
- if (match.required()) {
- _create_class_hierarchy(match);
- }
- } else {
- if (match.name) {
- _create_class_item(root_item, match.doc, true);
- }
- }
-
- ++iterator_match;
- return !iterator_match;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_member_items_init() {
- iterator_match = matches.begin();
-
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
- if (!iterator_match) {
- return true;
- }
-
- ClassMatch &match = iterator_match->value;
-
- TreeItem *parent = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
- bool constructor_created = false;
- for (int i = 0; i < match.methods.size(); i++) {
- String text = match.methods[i]->name;
- if (!constructor_created) {
- if (match.doc->name == match.methods[i]->name) {
- text += " " + TTR("(constructors)");
- constructor_created = true;
- }
- } else {
- if (match.doc->name == match.methods[i]->name) {
- continue;
- }
- }
- _create_method_item(parent, match.doc, text, match.methods[i]);
- }
- for (int i = 0; i < match.signals.size(); i++) {
- _create_signal_item(parent, match.doc, match.signals[i]);
- }
- for (int i = 0; i < match.constants.size(); i++) {
- _create_constant_item(parent, match.doc, match.constants[i]);
- }
- for (int i = 0; i < match.properties.size(); i++) {
- _create_property_item(parent, match.doc, match.properties[i]);
- }
- for (int i = 0; i < match.theme_properties.size(); i++) {
- _create_theme_property_item(parent, match.doc, match.theme_properties[i]);
- }
-
- ++iterator_match;
- return !iterator_match;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_phase_select_match() {
- if (matched_item) {
- matched_item->select(0);
- }
- return true;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_match_string(const String &p_term, const String &p_string) const {
- if (search_flags & SEARCH_CASE_SENSITIVE) {
- return p_string.find(p_term) > -1;
- } else {
- return p_string.findn(p_term) > -1;
- }
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_match_visual_script(DocData::ClassDoc &class_doc) {
- if (class_doc.category.ends_with("_class")) {
- if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_CLASSES) {
- if (matches.has(class_doc.inherits)) {
- return true;
- }
- }
- return false;
- }
- if (class_doc.category.begins_with("VisualScript") && search_flags & SEARCH_VISUAL_SCRIPT_NODES) {
- return true;
- }
- if (class_doc.name.begins_with("operators") && search_flags & SEARCH_OPERATORS) {
- return true;
- }
- if (class_doc.category.begins_with("VisualScriptNode/deconstruct")) {
- if (class_doc.name.find(selector_ui->base_type, 0) > -1) {
- return true;
- }
- }
-
- return false;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::_match_is_hidden(DocData::ClassDoc &class_doc) {
- if (class_doc.category.begins_with("VisualScript")) {
- if (class_doc.name.begins_with("flow_control")) {
- return false;
- } else if (class_doc.name.begins_with("operators")) {
- return !(search_flags & SEARCH_OPERATORS);
- } else if (class_doc.name.begins_with("functions/built_in/print")) {
- return false;
- }
- return true;
- }
- return false;
-}
-
-void VisualScriptPropertySelector::SearchRunner::_match_item(TreeItem *p_item, const String &p_text) {
- float inverse_length = 1.f / float(p_text.length());
-
- // Favor types where search term is a substring close to the start of the type.
- float w = 0.5f;
- int pos = p_text.findn(term);
- float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w);
-
- // Favor shorter items: they resemble the search term more.
- w = 0.1f;
- score *= (1 - w) + w * (term.length() * inverse_length);
-
- if (match_highest_score == 0 || score > match_highest_score) {
- matched_item = p_item;
- match_highest_score = score;
- }
-}
-
-void VisualScriptPropertySelector::SearchRunner::_add_class_doc(String class_name, String inherits, String category) {
- DocData::ClassDoc class_doc = DocData::ClassDoc();
- class_doc.name = class_name;
- class_doc.inherits = inherits;
- class_doc.category = "VisualScriptNode/" + category;
- class_doc.brief_description = category;
- combined_docs.insert(class_doc.name, class_doc);
-}
-
-DocData::MethodDoc VisualScriptPropertySelector::SearchRunner::_get_method_doc(MethodInfo method_info) {
- DocData::MethodDoc method_doc = DocData::MethodDoc();
- method_doc.name = method_info.name;
- method_doc.return_type = Variant::get_type_name(method_info.return_val.type);
- method_doc.description = "No description available";
- for (List<PropertyInfo>::Element *P = method_info.arguments.front(); P; P = P->next()) {
- DocData::ArgumentDoc argument_doc = DocData::ArgumentDoc();
- argument_doc.name = P->get().name;
- argument_doc.type = Variant::get_type_name(P->get().type);
- method_doc.arguments.push_back(argument_doc);
- }
- return method_doc;
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_hierarchy(const ClassMatch &p_match) {
- if (class_items.has(p_match.doc->name)) {
- return class_items[p_match.doc->name];
- }
-
- // Ensure parent nodes are created first.
- TreeItem *parent = root_item;
- if (p_match.doc->inherits != "") {
- if (class_items.has(p_match.doc->inherits)) {
- parent = class_items[p_match.doc->inherits];
- } else if (matches.has(p_match.doc->inherits)) {
- ClassMatch &base_match = matches[p_match.doc->inherits];
- parent = _create_class_hierarchy(base_match);
- }
- }
-
- TreeItem *class_item = _create_class_item(parent, p_match.doc, !p_match.name);
- class_items[p_match.doc->name] = class_item;
- return class_item;
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
- Ref<Texture2D> icon = empty_icon;
- String text_0 = p_doc->name;
- String text_1 = "Class";
-
- String what = "Class";
- String details = p_doc->name;
- if (p_doc->category.begins_with("VisualScriptCustomNode/")) {
- Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
- text_0 = path[path.size() - 1];
- text_1 = "VisualScriptCustomNode";
- what = "VisualScriptCustomNode";
- details = "CustomNode";
- } else if (p_doc->category.begins_with("VisualScriptNode/")) {
- Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
- text_0 = path[path.size() - 1];
- if (p_doc->category.begins_with("VisualScriptNode/deconstruct")) {
- text_0 = "deconstruct " + text_0;
- }
- text_1 = "VisualScriptNode";
- what = "VisualScriptNode";
- details = p_doc->name;
-
- if (path.size() == 1) {
- if (path[0] == "functions" || path[0] == "operators") {
- text_1 = "VisualScript";
- p_gray = true;
- what = "no_result";
- details = "";
- }
- }
-
- } else {
- if (p_doc->name.is_quoted()) {
- text_0 = p_doc->name.unquote().get_file();
- if (ui_service->has_theme_icon(p_doc->inherits, "EditorIcons")) {
- icon = ui_service->get_theme_icon(p_doc->inherits, "EditorIcons");
- }
- } else if (ui_service->has_theme_icon(p_doc->name, "EditorIcons")) {
- icon = ui_service->get_theme_icon(p_doc->name, "EditorIcons");
- } else if (ClassDB::class_exists(p_doc->name) && ClassDB::is_parent_class(p_doc->name, "Object")) {
- icon = ui_service->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
- }
- }
- String tooltip = p_doc->brief_description.strip_edges();
-
- TreeItem *item = results_tree->create_item(p_parent);
- item->set_icon(0, icon);
- item->set_text(0, text_0);
- item->set_text(1, TTR(text_1));
- item->set_tooltip(0, tooltip);
- item->set_tooltip(1, tooltip);
- item->set_metadata(0, details);
- item->set_metadata(1, what);
- if (p_gray) {
- item->set_custom_color(0, disabled_color);
- item->set_custom_color(1, disabled_color);
- }
-
- _match_item(item, p_doc->name);
-
- return item;
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
- String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
- for (int i = 0; i < p_doc->arguments.size(); i++) {
- const DocData::ArgumentDoc &arg = p_doc->arguments[i];
- tooltip += arg.type + " " + arg.name;
- if (arg.default_value != "") {
- tooltip += " = " + arg.default_value;
- }
- if (i < p_doc->arguments.size() - 1) {
- tooltip += ", ";
- }
- }
- tooltip += ")";
- return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->description);
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
- String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
- for (int i = 0; i < p_doc->arguments.size(); i++) {
- const DocData::ArgumentDoc &arg = p_doc->arguments[i];
- tooltip += arg.type + " " + arg.name;
- if (arg.default_value != "") {
- tooltip += " = " + arg.default_value;
- }
- if (i < p_doc->arguments.size() - 1) {
- tooltip += ", ";
- }
- }
- tooltip += ")";
- return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->description);
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
- String tooltip = p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->description);
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
- String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
- tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
- tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
- return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->description);
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) {
- String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", tooltip, p_doc->description);
-}
-
-TreeItem *VisualScriptPropertySelector::SearchRunner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description) {
- Ref<Texture2D> icon;
- String text;
- if (search_flags & SEARCH_SHOW_HIERARCHY) {
- icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
- text = p_text;
- } else {
- icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
- text = p_class_name + "." + p_text;
- }
-
- TreeItem *item = results_tree->create_item(p_parent);
- item->set_icon(0, icon);
- item->set_text(0, text);
- item->set_text(1, TTRGET(p_type));
- item->set_tooltip(0, p_tooltip);
- item->set_tooltip(1, p_tooltip);
- item->set_metadata(0, p_class_name + ":" + p_name);
- item->set_metadata(1, "class_" + p_metatype);
- item->set_meta("description", p_description);
-
- _match_item(item, p_name);
-
- return item;
-}
-
-bool VisualScriptPropertySelector::SearchRunner::work(uint64_t slot) {
- // Return true when the search has been completed, otherwise false.
- const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
- while (!_slice()) {
- if (OS::get_singleton()->get_ticks_usec() > until) {
- return false;
- }
- }
- return true;
-}
-
-VisualScriptPropertySelector::SearchRunner::SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree) :
- selector_ui(p_selector_ui),
- ui_service(p_selector_ui->vbox),
- results_tree(p_results_tree),
- term(p_selector_ui->search_box->get_text()),
- empty_icon(ui_service->get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons"))),
- disabled_color(ui_service->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))) {
-}
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
deleted file mode 100644
index 41f8eea735..0000000000
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/*************************************************************************/
-/* visual_script_property_selector.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 VISUAL_SCRIPT_PROPERTY_SELECTOR_H
-#define VISUAL_SCRIPT_PROPERTY_SELECTOR_H
-
-#include "../visual_script.h"
-#include "editor/editor_help.h"
-#include "editor/property_editor.h"
-#include "scene/gui/rich_text_label.h"
-
-class VisualScriptPropertySelector : public ConfirmationDialog {
- GDCLASS(VisualScriptPropertySelector, ConfirmationDialog);
-
- enum SearchFlags {
- SEARCH_CLASSES = 1 << 0,
- SEARCH_CONSTRUCTORS = 1 << 1,
- SEARCH_METHODS = 1 << 2,
- SEARCH_OPERATORS = 1 << 3,
- SEARCH_SIGNALS = 1 << 4,
- SEARCH_CONSTANTS = 1 << 5,
- SEARCH_PROPERTIES = 1 << 6,
- SEARCH_THEME_ITEMS = 1 << 7,
- SEARCH_VISUAL_SCRIPT_NODES = 1 << 8,
- SEARCH_ALL = SEARCH_CLASSES | SEARCH_CONSTRUCTORS | SEARCH_METHODS | SEARCH_OPERATORS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
- SEARCH_CASE_SENSITIVE = 1 << 29,
- SEARCH_SHOW_HIERARCHY = 1 << 30,
- };
-
- enum ScopeFlags {
- SCOPE_BASE = 1 << 0,
- SCOPE_INHERITERS = 1 << 1,
- SCOPE_UNRELATED = 1 << 2,
- SCOPE_RELATED = SCOPE_BASE | SCOPE_INHERITERS,
- SCOPE_ALL = SCOPE_BASE | SCOPE_INHERITERS | SCOPE_UNRELATED
- };
-
- enum ScopeCombo {
- COMBO_RELATED,
- COMBO_SEPARATOR,
- COMBO_BASE,
- COMBO_INHERITERS,
- COMBO_UNRELATED,
- COMBO_ALL,
- };
-
- LineEdit *search_box = nullptr;
-
- Button *case_sensitive_button = nullptr;
- Button *hierarchy_button = nullptr;
-
- Button *search_visual_script_nodes = nullptr;
- Button *search_classes = nullptr;
- Button *search_operators = nullptr;
-
- Button *search_methods = nullptr;
- Button *search_signals = nullptr;
- Button *search_constants = nullptr;
- Button *search_properties = nullptr;
- Button *search_theme_items = nullptr;
-
- OptionButton *scope_combo = nullptr;
- Tree *results_tree = nullptr;
-
- class SearchRunner;
- Ref<SearchRunner> search_runner;
-
- void _update_icons();
-
- void _sbox_input(const Ref<InputEvent> &p_ie);
- void _update_results_i(int p_int);
- void _update_results_s(String p_string);
- void _update_results_search_all();
- void _update_results();
-
- void _confirmed();
- void _item_selected();
- void _hide_requested();
-
- EditorHelpBit *help_bit = nullptr;
-
- bool properties = false;
- bool visual_script_generic = false;
- bool connecting = false;
- String selected;
- Variant::Type type;
- String base_type;
- String base_script;
- ObjectID script;
- Object *instance = nullptr;
- bool virtuals_only = false;
- VBoxContainer *vbox = nullptr;
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void select_method_from_base_type(const String &p_base, const bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_base_type(const String &p_base, const String &p_base_script = "", bool p_virtuals_only = false, const bool p_connecting = true, bool clear_text = true);
- void select_from_script(const Ref<Script> &p_script, const bool p_connecting = true, bool clear_text = true);
- void select_from_basic_type(Variant::Type p_type, const bool p_connecting = true, bool clear_text = true);
- void select_from_action(const String &p_type, const bool p_connecting = true, bool clear_text = true);
- void select_from_instance(Object *p_instance, const bool p_connecting = true, bool clear_text = true);
- void select_from_visual_script(const Ref<Script> &p_script, bool clear_text = true);
-
- void show_window(float p_screen_ratio);
-
- VisualScriptPropertySelector();
-};
-
-class VisualScriptPropertySelector::SearchRunner : public RefCounted {
- enum Phase {
- PHASE_INIT,
- PHASE_MATCH_CLASSES_INIT,
- PHASE_NODE_CLASSES_INIT,
- PHASE_NODE_CLASSES_BUILD,
- PHASE_MATCH_CLASSES,
- PHASE_CLASS_ITEMS_INIT,
- PHASE_CLASS_ITEMS,
- PHASE_MEMBER_ITEMS_INIT,
- PHASE_MEMBER_ITEMS,
- PHASE_SELECT_MATCH,
- PHASE_MAX
- };
- int phase = 0;
-
- struct ClassMatch {
- DocData::ClassDoc *doc;
- bool name = false;
- String category = "";
- Vector<DocData::MethodDoc *> constructors;
- Vector<DocData::MethodDoc *> methods;
- Vector<DocData::MethodDoc *> operators;
- Vector<DocData::MethodDoc *> signals;
- Vector<DocData::ConstantDoc *> constants;
- Vector<DocData::PropertyDoc *> properties;
- Vector<DocData::ThemeItemDoc *> theme_properties;
-
- bool required() {
- return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
- }
- };
-
- VisualScriptPropertySelector *selector_ui = nullptr;
- Control *ui_service = nullptr;
- Tree *results_tree = nullptr;
- String term;
- int search_flags = 0;
- int scope_flags = 0;
-
- Ref<Texture2D> empty_icon;
- Color disabled_color;
-
- HashMap<String, DocData::ClassDoc>::Iterator iterator_doc;
- HashMap<String, ClassMatch> matches;
- HashMap<String, ClassMatch>::Iterator iterator_match;
- TreeItem *root_item = nullptr;
- HashMap<String, TreeItem *> class_items;
- TreeItem *matched_item = nullptr;
- float match_highest_score = 0;
-
- HashMap<String, DocData::ClassDoc> combined_docs;
- List<String> vs_nodes;
-
- bool _is_class_disabled_by_feature_profile(const StringName &p_class);
- bool _is_class_disabled_by_scope(const StringName &p_class);
-
- bool _slice();
- bool _phase_init();
- bool _phase_match_classes_init();
- bool _phase_node_classes_init();
- bool _phase_node_classes_build();
- bool _phase_match_classes();
- bool _phase_class_items_init();
- bool _phase_class_items();
- bool _phase_member_items_init();
- bool _phase_member_items();
- bool _phase_select_match();
-
- bool _match_string(const String &p_term, const String &p_string) const;
- bool _match_visual_script(DocData::ClassDoc &class_doc);
- bool _match_is_hidden(DocData::ClassDoc &class_doc);
- void _match_item(TreeItem *p_item, const String &p_text);
- void _add_class_doc(String class_name, String inherits, String category);
- DocData::MethodDoc _get_method_doc(MethodInfo method_info);
- TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
- TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
- TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
- TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
- TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
- TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
- TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
- TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_description);
-
-public:
- bool work(uint64_t slot = 100000);
-
- SearchRunner(VisualScriptPropertySelector *p_selector_ui, Tree *p_results_tree);
-};
-
-#endif // VISUAL_SCRIPT_PROPERTY_SELECTOR_H
diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg
deleted file mode 100644
index bc698247c9..0000000000
--- a/modules/visual_script/icons/VisualScript.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
diff --git a/modules/visual_script/icons/VisualScriptInternal.svg b/modules/visual_script/icons/VisualScriptInternal.svg
deleted file mode 100644
index 8ab39ad929..0000000000
--- a/modules/visual_script/icons/VisualScriptInternal.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><circle cx="3" cy="3.000024" fill="#e0e0e0" r="0"/><path d="m11 10a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0"/><path d="m3 10v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4z" fill="#e0e0e0"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2z" fill="none" stroke="#e0e0e0"/></svg>
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
deleted file mode 100644
index 04a7442d0a..0000000000
--- a/modules/visual_script/register_types.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*************************************************************************/
-/* 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 "core/config/engine.h"
-#include "core/io/resource_loader.h"
-#include "visual_script.h"
-#include "visual_script_builtin_funcs.h"
-#include "visual_script_expression.h"
-#include "visual_script_flow_control.h"
-#include "visual_script_func_nodes.h"
-#include "visual_script_nodes.h"
-#include "visual_script_yield_nodes.h"
-
-VisualScriptLanguage *visual_script_language = nullptr;
-
-#ifdef TOOLS_ENABLED
-#include "editor/visual_script_editor.h"
-static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr;
-#endif
-
-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
- 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 uninitialize_visual_script_module(ModuleInitializationLevel p_level) {
- if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) {
- unregister_visual_script_nodes();
-
- ScriptServer::unregister_language(visual_script_language);
-
- if (visual_script_language) {
- memdelete(visual_script_language);
- }
- }
-
-#ifdef TOOLS_ENABLED
- if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
- VisualScriptEditor::free_clipboard();
- if (vs_custom_nodes_singleton) {
- memdelete(vs_custom_nodes_singleton);
- }
- }
-#endif
-}
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
deleted file mode 100644
index 4215a979e0..0000000000
--- a/modules/visual_script/visual_script.cpp
+++ /dev/null
@@ -1,2502 +0,0 @@
-/*************************************************************************/
-/* visual_script.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 "visual_script.h"
-
-#include "core/config/project_settings.h"
-#include "core/core_string_names.h"
-#include "core/os/os.h"
-#include "scene/main/node.h"
-#include "visual_script_nodes.h"
-
-// Used by editor, this is not really saved.
-void VisualScriptNode::set_breakpoint(bool p_breakpoint) {
- breakpoint = p_breakpoint;
-}
-
-bool VisualScriptNode::is_breakpoint() const {
- return breakpoint;
-}
-
-void VisualScriptNode::ports_changed_notify() {
- emit_signal(SNAME("ports_changed"));
-}
-
-void VisualScriptNode::set_default_input_value(int p_port, const Variant &p_value) {
- ERR_FAIL_INDEX(p_port, default_input_values.size());
-
- default_input_values[p_port] = p_value;
-
-#ifdef TOOLS_ENABLED
- if (script_used.is_valid()) {
- script_used->set_edited(true);
- }
-#endif
-}
-
-Variant VisualScriptNode::get_default_input_value(int p_port) const {
- ERR_FAIL_INDEX_V(p_port, default_input_values.size(), Variant());
- return default_input_values[p_port];
-}
-
-void VisualScriptNode::_set_default_input_values(Array p_values) {
- default_input_values = p_values;
-}
-
-void VisualScriptNode::validate_input_default_values() {
- default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize
-
- // Actually validate on save.
- for (int i = 0; i < get_input_value_port_count(); i++) {
- Variant::Type expected = get_input_value_port_info(i).type;
-
- if (expected == Variant::NIL || expected == default_input_values[i].get_type()) {
- continue;
- } else {
- // Not the same, reconvert.
- Callable::CallError ce;
- Variant existing = default_input_values[i];
- const Variant *existingp = &existing;
- Variant::construct(expected, default_input_values[i], &existingp, 1, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- //could not convert? force..
- Variant::construct(expected, default_input_values[i], nullptr, 0, ce);
- }
- }
- }
-}
-
-Array VisualScriptNode::_get_default_input_values() const {
- // Validate on save, since on load there is little info about this.
- Array values = default_input_values;
- values.resize(get_input_value_port_count());
-
- return values;
-}
-
-String VisualScriptNode::get_text() const {
- return "";
-}
-
-void VisualScriptNode::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_visual_script"), &VisualScriptNode::get_visual_script);
- ClassDB::bind_method(D_METHOD("set_default_input_value", "port_idx", "value"), &VisualScriptNode::set_default_input_value);
- ClassDB::bind_method(D_METHOD("get_default_input_value", "port_idx"), &VisualScriptNode::get_default_input_value);
- ClassDB::bind_method(D_METHOD("ports_changed_notify"), &VisualScriptNode::ports_changed_notify);
- ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualScriptNode::_set_default_input_values);
- ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualScriptNode::_get_default_input_values);
-
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values");
- ADD_SIGNAL(MethodInfo("ports_changed"));
-}
-
-VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- ERR_FAIL_COND_V(get_output_value_port_count() <= p_output, TypeGuess());
-
- PropertyInfo pinfo = get_output_value_port_info(p_output);
-
- TypeGuess tg;
-
- tg.type = pinfo.type;
- if (pinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- tg.gdclass = pinfo.hint_string;
- }
-
- return tg;
-}
-
-Ref<VisualScript> VisualScriptNode::get_visual_script() const {
- return script_used;
-}
-
-VisualScriptNode::VisualScriptNode() {
-}
-
-////////////////
-
-/////////////////////
-
-VisualScriptNodeInstance::VisualScriptNodeInstance() {
-}
-
-VisualScriptNodeInstance::~VisualScriptNodeInstance() {
- if (sequence_outputs) {
- memdelete_arr(sequence_outputs);
- }
-
- if (input_ports) {
- memdelete_arr(input_ports);
- }
-
- if (output_ports) {
- memdelete_arr(output_ports);
- }
-}
-
-void VisualScript::add_function(const StringName &p_name, int p_func_node_id) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!String(p_name).is_valid_identifier());
- ERR_FAIL_COND(functions.has(p_name));
- ERR_FAIL_COND(variables.has(p_name));
- ERR_FAIL_COND(custom_signals.has(p_name));
-
- functions[p_name] = Function();
- functions[p_name].func_id = p_func_node_id;
-}
-
-bool VisualScript::has_function(const StringName &p_name) const {
- return functions.has(p_name);
-}
-
-void VisualScript::remove_function(const StringName &p_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_name));
-
- // Let the editor handle the node removal.
- functions.erase(p_name);
-}
-
-void VisualScript::rename_function(const StringName &p_name, const StringName &p_new_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_name));
- if (p_new_name == p_name) {
- return;
- }
-
- ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
-
- ERR_FAIL_COND(functions.has(p_new_name));
- ERR_FAIL_COND(variables.has(p_new_name));
- ERR_FAIL_COND(custom_signals.has(p_new_name));
-
- functions[p_new_name] = functions[p_name];
- functions.erase(p_name);
-}
-
-void VisualScript::set_scroll(const Vector2 &p_scroll) {
- scroll = p_scroll;
-}
-
-Vector2 VisualScript::get_scroll() const {
- return scroll;
-}
-
-void VisualScript::get_function_list(List<StringName> *r_functions) const {
- for (const KeyValue<StringName, Function> &E : functions) {
- r_functions->push_back(E.key);
- }
-}
-
-int VisualScript::get_function_node_id(const StringName &p_name) const {
- ERR_FAIL_COND_V(!functions.has(p_name), -1);
-
- return functions[p_name].func_id;
-}
-
-void VisualScript::_node_ports_changed(int p_id) {
- Ref<VisualScriptNode> vsn = nodes[p_id].node;
-
- vsn->validate_input_default_values();
-
- // Must revalidate all the functions.
-
- {
- List<SequenceConnection> to_remove;
-
- 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.to_node == p_id && !vsn->has_input_sequence_port()) {
- to_remove.push_back(E);
- }
- }
-
- while (to_remove.size()) {
- sequence_connections.erase(to_remove.front()->get());
- to_remove.pop_front();
- }
- }
-
- {
- List<DataConnection> to_remove;
-
- 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.to_node == p_id && E.to_port >= vsn->get_input_value_port_count()) {
- to_remove.push_back(E);
- }
- }
-
- while (to_remove.size()) {
- data_connections.erase(to_remove.front()->get());
- to_remove.pop_front();
- }
- }
-
-#ifdef TOOLS_ENABLED
- set_edited(true); // Something changed, let's set as edited.
- emit_signal(SNAME("node_ports_changed"), p_id);
-#endif
-}
-
-void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script.
- ERR_FAIL_COND(p_node.is_null());
-
- NodeData nd;
- nd.node = p_node;
- nd.pos = p_pos;
-
- Ref<VisualScriptNode> vsn = p_node;
- 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.
-
- nodes[p_id] = nd;
-}
-
-void VisualScript::remove_node(int p_id) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!nodes.has(p_id));
- {
- List<SequenceConnection> to_remove;
-
- for (const SequenceConnection &E : sequence_connections) {
- if (E.from_node == p_id || E.to_node == p_id) {
- to_remove.push_back(E);
- }
- }
-
- while (to_remove.size()) {
- sequence_connections.erase(to_remove.front()->get());
- to_remove.pop_front();
- }
- }
-
- {
- List<DataConnection> to_remove;
-
- for (const DataConnection &E : data_connections) {
- if (E.from_node == p_id || E.to_node == p_id) {
- to_remove.push_back(E);
- }
- }
-
- while (to_remove.size()) {
- data_connections.erase(to_remove.front()->get());
- to_remove.pop_front();
- }
- }
-
- nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
- nodes[p_id].node->script_used.unref();
-
- nodes.erase(p_id);
-}
-
-bool VisualScript::has_node(int p_id) const {
- return nodes.has(p_id);
-}
-
-Ref<VisualScriptNode> VisualScript::get_node(int p_id) const {
- ERR_FAIL_COND_V(!nodes.has(p_id), Ref<VisualScriptNode>());
-
- return nodes[p_id].node;
-}
-
-void VisualScript::set_node_position(int p_id, const Point2 &p_pos) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!nodes.has(p_id));
- nodes[p_id].pos = p_pos;
-}
-
-Point2 VisualScript::get_node_position(int p_id) const {
- ERR_FAIL_COND_V(!nodes.has(p_id), Point2());
- return nodes[p_id].pos;
-}
-
-void VisualScript::get_node_list(List<int> *r_nodes) const {
- 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) {
- ERR_FAIL_COND(instances.size());
-
- SequenceConnection sc;
- sc.from_node = p_from_node;
- sc.from_output = p_from_output;
- sc.to_node = p_to_node;
- ERR_FAIL_COND(sequence_connections.has(sc));
-
- sequence_connections.insert(sc);
-}
-
-void VisualScript::sequence_disconnect(int p_from_node, int p_from_output, int p_to_node) {
- SequenceConnection sc;
- sc.from_node = p_from_node;
- sc.from_output = p_from_output;
- sc.to_node = p_to_node;
- ERR_FAIL_COND(!sequence_connections.has(sc));
-
- sequence_connections.erase(sc);
-}
-
-bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const {
- SequenceConnection sc;
- sc.from_node = p_from_node;
- sc.from_output = p_from_output;
- sc.to_node = p_to_node;
-
- return sequence_connections.has(sc);
-}
-
-void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const {
- for (const SequenceConnection &E : sequence_connections) {
- r_connection->push_back(E);
- }
-}
-
-void VisualScript::data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
- ERR_FAIL_COND(instances.size());
-
- DataConnection dc;
- dc.from_node = p_from_node;
- dc.from_port = p_from_port;
- dc.to_node = p_to_node;
- dc.to_port = p_to_port;
-
- ERR_FAIL_COND(data_connections.has(dc));
-
- data_connections.insert(dc);
-}
-
-void VisualScript::data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
- DataConnection dc;
- dc.from_node = p_from_node;
- dc.from_port = p_from_port;
- dc.to_node = p_to_node;
- dc.to_port = p_to_port;
-
- ERR_FAIL_COND(!data_connections.has(dc));
-
- data_connections.erase(dc);
-}
-
-bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
- DataConnection dc;
- dc.from_node = p_from_node;
- dc.from_port = p_from_port;
- dc.to_node = p_to_node;
- dc.to_port = p_to_port;
-
- return data_connections.has(dc);
-}
-
-bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
- for (const DataConnection &E : data_connections) {
- if (E.to_node == p_node && E.to_port == p_port) {
- return true;
- }
- }
- return false;
-}
-
-bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const {
- 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;
- }
- }
- return false;
-}
-
-void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const {
- for (const DataConnection &E : data_connections) {
- r_connection->push_back(E);
- }
-}
-
-void VisualScript::set_tool_enabled(bool p_enabled) {
- is_tool_script = p_enabled;
-}
-
-void VisualScript::add_variable(const StringName &p_name, const Variant &p_default_value, bool p_export) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!String(p_name).is_valid_identifier());
- ERR_FAIL_COND(variables.has(p_name));
-
- Variable v;
- v.default_value = p_default_value;
- v.info.type = p_default_value.get_type();
- v.info.name = p_name;
- v.info.hint = PROPERTY_HINT_NONE;
- v._export = p_export;
-
- variables[p_name] = v;
-
-#ifdef TOOLS_ENABLED
- _update_placeholders();
-#endif
-}
-
-bool VisualScript::has_variable(const StringName &p_name) const {
- return variables.has(p_name);
-}
-
-void VisualScript::remove_variable(const StringName &p_name) {
- ERR_FAIL_COND(!variables.has(p_name));
- variables.erase(p_name);
-
-#ifdef TOOLS_ENABLED
- _update_placeholders();
-#endif
-}
-
-void VisualScript::set_variable_default_value(const StringName &p_name, const Variant &p_value) {
- ERR_FAIL_COND(!variables.has(p_name));
-
- variables[p_name].default_value = p_value;
-
-#ifdef TOOLS_ENABLED
- _update_placeholders();
-#endif
-}
-
-Variant VisualScript::get_variable_default_value(const StringName &p_name) const {
- ERR_FAIL_COND_V(!variables.has(p_name), Variant());
- return variables[p_name].default_value;
-}
-
-void VisualScript::set_variable_info(const StringName &p_name, const PropertyInfo &p_info) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!variables.has(p_name));
- variables[p_name].info = p_info;
- variables[p_name].info.name = p_name;
-
-#ifdef TOOLS_ENABLED
- _update_placeholders();
-#endif
-}
-
-PropertyInfo VisualScript::get_variable_info(const StringName &p_name) const {
- ERR_FAIL_COND_V(!variables.has(p_name), PropertyInfo());
- return variables[p_name].info;
-}
-
-void VisualScript::set_variable_export(const StringName &p_name, bool p_export) {
- ERR_FAIL_COND(!variables.has(p_name));
-
- variables[p_name]._export = p_export;
-#ifdef TOOLS_ENABLED
- _update_placeholders();
-#endif
-}
-
-bool VisualScript::get_variable_export(const StringName &p_name) const {
- ERR_FAIL_COND_V(!variables.has(p_name), false);
- return variables[p_name]._export;
-}
-
-void VisualScript::_set_variable_info(const StringName &p_name, const Dictionary &p_info) {
- PropertyInfo pinfo;
- if (p_info.has("type")) {
- pinfo.type = Variant::Type(int(p_info["type"]));
- }
- if (p_info.has("name")) {
- pinfo.name = p_info["name"];
- }
- if (p_info.has("hint")) {
- pinfo.hint = PropertyHint(int(p_info["hint"]));
- }
- if (p_info.has("hint_string")) {
- pinfo.hint_string = p_info["hint_string"];
- }
- if (p_info.has("usage")) {
- pinfo.usage = p_info["usage"];
- }
-
- set_variable_info(p_name, pinfo);
-}
-
-Dictionary VisualScript::_get_variable_info(const StringName &p_name) const {
- PropertyInfo pinfo = get_variable_info(p_name);
- Dictionary d;
- d["type"] = pinfo.type;
- d["name"] = pinfo.name;
- d["hint"] = pinfo.hint;
- d["hint_string"] = pinfo.hint_string;
- d["usage"] = pinfo.usage;
-
- return d;
-}
-
-void VisualScript::get_variable_list(List<StringName> *r_variables) const {
- for (const KeyValue<StringName, Variable> &E : variables) {
- r_variables->push_back(E.key);
- }
-}
-
-void VisualScript::set_instance_base_type(const StringName &p_type) {
- ERR_FAIL_COND(instances.size());
- base_type = p_type;
-}
-
-void VisualScript::rename_variable(const StringName &p_name, const StringName &p_new_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!variables.has(p_name));
- if (p_new_name == p_name) {
- return;
- }
-
- ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
-
- ERR_FAIL_COND(functions.has(p_new_name));
- ERR_FAIL_COND(variables.has(p_new_name));
- ERR_FAIL_COND(custom_signals.has(p_new_name));
-
- variables[p_new_name] = variables[p_name];
- variables.erase(p_name);
- List<int> ids;
- get_node_list(&ids);
- for (int &E : ids) {
- Ref<VisualScriptVariableGet> nodeget = get_node(E);
- if (nodeget.is_valid()) {
- if (nodeget->get_variable() == p_name) {
- nodeget->set_variable(p_new_name);
- }
- } else {
- Ref<VisualScriptVariableSet> nodeset = get_node(E);
- if (nodeset.is_valid()) {
- if (nodeset->get_variable() == p_name) {
- nodeset->set_variable(p_new_name);
- }
- }
- }
- }
-}
-
-void VisualScript::add_custom_signal(const StringName &p_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!String(p_name).is_valid_identifier());
- ERR_FAIL_COND(custom_signals.has(p_name));
-
- custom_signals[p_name] = Vector<Argument>();
-}
-
-bool VisualScript::has_custom_signal(const StringName &p_name) const {
- return custom_signals.has(p_name);
-}
-
-void VisualScript::custom_signal_add_argument(const StringName &p_func, Variant::Type p_type, const String &p_name, int p_index) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_func));
- Argument arg;
- arg.type = p_type;
- arg.name = p_name;
- if (p_index < 0) {
- custom_signals[p_func].push_back(arg);
- } else {
- custom_signals[p_func].insert(0, arg);
- }
-}
-
-void VisualScript::custom_signal_set_argument_type(const StringName &p_func, int p_argidx, Variant::Type p_type) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_func));
- ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func].write[p_argidx].type = p_type;
-}
-
-Variant::Type VisualScript::custom_signal_get_argument_type(const StringName &p_func, int p_argidx) const {
- ERR_FAIL_COND_V(!custom_signals.has(p_func), Variant::NIL);
- ERR_FAIL_INDEX_V(p_argidx, custom_signals[p_func].size(), Variant::NIL);
- return custom_signals[p_func][p_argidx].type;
-}
-
-void VisualScript::custom_signal_set_argument_name(const StringName &p_func, int p_argidx, const String &p_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_func));
- ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func].write[p_argidx].name = p_name;
-}
-
-String VisualScript::custom_signal_get_argument_name(const StringName &p_func, int p_argidx) const {
- ERR_FAIL_COND_V(!custom_signals.has(p_func), String());
- ERR_FAIL_INDEX_V(p_argidx, custom_signals[p_func].size(), String());
- return custom_signals[p_func][p_argidx].name;
-}
-
-void VisualScript::custom_signal_remove_argument(const StringName &p_func, int p_argidx) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_func));
- ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func].remove_at(p_argidx);
-}
-
-int VisualScript::custom_signal_get_argument_count(const StringName &p_func) const {
- ERR_FAIL_COND_V(!custom_signals.has(p_func), 0);
- return custom_signals[p_func].size();
-}
-
-void VisualScript::custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_func));
- ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- ERR_FAIL_INDEX(p_with_argidx, custom_signals[p_func].size());
-
- SWAP(custom_signals[p_func].write[p_argidx], custom_signals[p_func].write[p_with_argidx]);
-}
-
-void VisualScript::remove_custom_signal(const StringName &p_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_name));
- custom_signals.erase(p_name);
-}
-
-void VisualScript::rename_custom_signal(const StringName &p_name, const StringName &p_new_name) {
- ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!custom_signals.has(p_name));
- if (p_new_name == p_name) {
- return;
- }
-
- ERR_FAIL_COND(!String(p_new_name).is_valid_identifier());
-
- ERR_FAIL_COND(functions.has(p_new_name));
- ERR_FAIL_COND(variables.has(p_new_name));
- ERR_FAIL_COND(custom_signals.has(p_new_name));
-
- custom_signals[p_new_name] = custom_signals[p_name];
- custom_signals.erase(p_name);
-}
-
-void VisualScript::get_custom_signal_list(List<StringName> *r_custom_signals) const {
- for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
- r_custom_signals->push_back(E.key);
- }
-
- r_custom_signals->sort_custom<StringName::AlphCompare>();
-}
-
-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.
-
- int max = -1;
- for (const KeyValue<int, NodeData> &E : nodes) {
- if (E.key > max) {
- max = E.key;
- }
- }
- return (max + 1);
-}
-
-/////////////////////////////////
-
-bool VisualScript::can_instantiate() const {
- return true; // ScriptServer::is_scripting_enabled();
-}
-
-StringName VisualScript::get_instance_base_type() const {
- return base_type;
-}
-
-Ref<Script> VisualScript::get_base_script() const {
- return Ref<Script>(); // No inheritance in visual script.
-}
-
-#ifdef TOOLS_ENABLED
-void VisualScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
- placeholders.erase(p_placeholder);
-}
-
-void VisualScript::_update_placeholders() {
- if (placeholders.size() == 0) {
- return; // No bother if no placeholders.
- }
- List<PropertyInfo> pinfo;
- HashMap<StringName, Variant> values;
-
- for (const KeyValue<StringName, Variable> &E : variables) {
- if (!variables[E.key]._export) {
- continue;
- }
-
- PropertyInfo p = variables[E.key].info;
- p.name = String(E.key);
- pinfo.push_back(p);
- values[p.name] = variables[E.key].default_value;
- }
-
- for (PlaceHolderScriptInstance *E : placeholders) {
- E->update(pinfo, values);
- }
-}
-
-#endif
-
-ScriptInstance *VisualScript::instance_create(Object *p_this) {
-#ifdef TOOLS_ENABLED
-
- if (!ScriptServer::is_scripting_enabled() && !is_tool_script) {
- PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(VisualScriptLanguage::singleton, Ref<Script>((Script *)this), p_this));
- placeholders.insert(sins);
-
- List<PropertyInfo> pinfo;
- HashMap<StringName, Variant> values;
-
- for (const KeyValue<StringName, Variable> &E : variables) {
- if (!variables[E.key]._export) {
- continue;
- }
-
- PropertyInfo p = variables[E.key].info;
- p.name = String(E.key);
- pinfo.push_back(p);
- values[p.name] = variables[E.key].default_value;
- }
- sins->update(pinfo, values);
-
- return sins;
- }
-#endif
-
- VisualScriptInstance *instance = memnew(VisualScriptInstance);
- instance->create(Ref<VisualScript>(this), p_this);
-
- {
- MutexLock lock(VisualScriptLanguage::singleton->lock);
-
- instances[p_this] = instance;
- }
-
- return instance;
-}
-
-bool VisualScript::instance_has(const Object *p_this) const {
- return instances.has((Object *)p_this);
-}
-
-bool VisualScript::has_source_code() const {
- return false;
-}
-
-String VisualScript::get_source_code() const {
- return String();
-}
-
-void VisualScript::set_source_code(const String &p_code) {
-}
-
-Error VisualScript::reload(bool p_keep_state) {
- return OK;
-}
-
-bool VisualScript::is_tool() const {
- return is_tool_script;
-}
-
-bool VisualScript::is_valid() const {
- return true; // Always valid.
-}
-
-ScriptLanguage *VisualScript::get_language() const {
- return VisualScriptLanguage::singleton;
-}
-
-bool VisualScript::has_script_signal(const StringName &p_signal) const {
- return custom_signals.has(p_signal);
-}
-
-void VisualScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
- MethodInfo mi;
- mi.name = E.key;
- for (int i = 0; i < E.value.size(); i++) {
- PropertyInfo arg;
- arg.type = E.value[i].type;
- arg.name = E.value[i].name;
- mi.arguments.push_back(arg);
- }
-
- r_signals->push_back(mi);
- }
-}
-
-bool VisualScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
- if (!variables.has(p_property)) {
- return false;
- }
-
- r_value = variables[p_property].default_value;
- return true;
-}
-
-void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
- for (const KeyValue<StringName, Function> &E : functions) {
- MethodInfo mi;
- 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;
- arg.name = func->get_argument_name(i);
- arg.type = func->get_argument_type(i);
- mi.arguments.push_back(arg);
- }
-
- p_list->push_back(mi);
- }
- }
- }
-}
-
-bool VisualScript::has_method(const StringName &p_method) const {
- return functions.has(p_method);
-}
-
-MethodInfo VisualScript::get_method_info(const StringName &p_method) const {
- const Function funct = functions[p_method];
- if (funct.func_id == -1) {
- return MethodInfo();
- }
-
- MethodInfo mi;
- mi.name = p_method;
- if (funct.func_id >= 0) {
- Ref<VisualScriptFunction> func = nodes[funct.func_id].node;
- if (func.is_valid()) {
- for (int i = 0; i < func->get_argument_count(); i++) {
- PropertyInfo arg;
- arg.name = func->get_argument_name(i);
- arg.type = func->get_argument_type(i);
- mi.arguments.push_back(arg);
- }
-
- if (!func->is_sequenced()) {
- mi.flags |= METHOD_FLAG_CONST;
- }
- }
- }
-
- return mi;
-}
-
-void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
- List<StringName> vars;
- 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);
- }
-}
-
-int VisualScript::get_member_line(const StringName &p_member) const {
- return functions[p_member].func_id; // will be -1 if not found
-}
-
-#ifdef TOOLS_ENABLED
-bool VisualScript::are_subnodes_edited() const {
- for (const KeyValue<int, NodeData> &F : nodes) {
- if (F.value.node->is_edited()) {
- return true;
- }
- }
- return false;
-}
-#endif
-
-const Variant VisualScript::get_rpc_config() const {
- return rpc_functions;
-}
-
-void VisualScript::_set_data(const Dictionary &p_data) {
- Dictionary d = p_data;
- if (d.has("base_type")) {
- base_type = d["base_type"];
- }
-
- variables.clear();
- Array vars = d["variables"];
- for (int i = 0; i < vars.size(); i++) {
- Dictionary v = vars[i];
- StringName name = v["name"];
- add_variable(name);
- _set_variable_info(name, v);
- set_variable_default_value(name, v["default_value"]);
- set_variable_export(name, v.has("export") && bool(v["export"]));
- }
-
- custom_signals.clear();
- Array sigs = d["signals"];
- for (int i = 0; i < sigs.size(); i++) {
- Dictionary cs = sigs[i];
- add_custom_signal(cs["name"]);
-
- Array args = cs["arguments"];
- for (int j = 0; j < args.size(); j += 2) {
- custom_signal_add_argument(cs["name"], Variant::Type(int(args[j + 1])), args[j]);
- }
- }
-
- Array funcs = d["functions"];
- functions.clear();
-
- for (int i = 0; i < funcs.size(); i++) {
- Dictionary func = funcs[i];
- add_function(func["name"], func["function_id"]);
- }
- {
- Array nodes = d["nodes"];
- for (int i = 0; i < nodes.size(); i += 3) {
- add_node(nodes[i], nodes[i + 2], nodes[i + 1]);
- }
-
- Array sequence_connections = d["sequence_connections"];
- for (int j = 0; j < sequence_connections.size(); j += 3) {
- sequence_connect(sequence_connections[j + 0], sequence_connections[j + 1], sequence_connections[j + 2]);
- }
-
- Array data_connections = d["data_connections"];
- for (int j = 0; j < data_connections.size(); j += 4) {
- data_connect(data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]);
- }
- }
- is_tool_script = d["is_tool_script"];
- scroll = d["scroll"];
-
- // Takes all the rpc methods.
- rpc_functions.clear();
- 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;
- }
- }
-}
-
-Dictionary VisualScript::_get_data() const {
- Dictionary d;
- d["base_type"] = base_type;
-
- Array vars;
- 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;
-
- Array sigs;
- for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
- Dictionary cs;
- cs["name"] = E.key;
- Array args;
- for (int i = 0; i < E.value.size(); i++) {
- args.push_back(E.value[i].name);
- args.push_back(E.value[i].type);
- }
- cs["arguments"] = args;
-
- sigs.push_back(cs);
- }
-
- d["signals"] = sigs;
-
- Array funcs;
- for (const KeyValue<StringName, Function> &E : functions) {
- Dictionary func;
- func["name"] = E.key;
- func["function_id"] = E.value.func_id;
- funcs.push_back(func);
- }
- d["functions"] = funcs;
-
- Array nds;
- 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 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 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;
-
- d["is_tool_script"] = is_tool_script;
- d["scroll"] = scroll;
-
- return d;
-}
-
-void VisualScript::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_function", "name", "func_node_id"), &VisualScript::add_function);
- ClassDB::bind_method(D_METHOD("has_function", "name"), &VisualScript::has_function);
- ClassDB::bind_method(D_METHOD("remove_function", "name"), &VisualScript::remove_function);
- ClassDB::bind_method(D_METHOD("rename_function", "name", "new_name"), &VisualScript::rename_function);
- ClassDB::bind_method(D_METHOD("set_scroll", "offset"), &VisualScript::set_scroll);
- ClassDB::bind_method(D_METHOD("get_scroll"), &VisualScript::get_scroll);
-
- ClassDB::bind_method(D_METHOD("add_node", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2()));
- ClassDB::bind_method(D_METHOD("remove_node", "id"), &VisualScript::remove_node);
- ClassDB::bind_method(D_METHOD("get_function_node_id", "name"), &VisualScript::get_function_node_id);
-
- ClassDB::bind_method(D_METHOD("get_node", "id"), &VisualScript::get_node);
- ClassDB::bind_method(D_METHOD("has_node", "id"), &VisualScript::has_node);
- ClassDB::bind_method(D_METHOD("set_node_position", "id", "position"), &VisualScript::set_node_position);
- ClassDB::bind_method(D_METHOD("get_node_position", "id"), &VisualScript::get_node_position);
-
- ClassDB::bind_method(D_METHOD("sequence_connect", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect);
- ClassDB::bind_method(D_METHOD("sequence_disconnect", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect);
- ClassDB::bind_method(D_METHOD("has_sequence_connection", "from_node", "from_output", "to_node"), &VisualScript::has_sequence_connection);
-
- ClassDB::bind_method(D_METHOD("data_connect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_connect);
- ClassDB::bind_method(D_METHOD("data_disconnect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_disconnect);
- ClassDB::bind_method(D_METHOD("has_data_connection", "from_node", "from_port", "to_node", "to_port"), &VisualScript::has_data_connection);
-
- ClassDB::bind_method(D_METHOD("add_variable", "name", "default_value", "export"), &VisualScript::add_variable, DEFVAL(Variant()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("has_variable", "name"), &VisualScript::has_variable);
- ClassDB::bind_method(D_METHOD("remove_variable", "name"), &VisualScript::remove_variable);
- ClassDB::bind_method(D_METHOD("set_variable_default_value", "name", "value"), &VisualScript::set_variable_default_value);
- ClassDB::bind_method(D_METHOD("get_variable_default_value", "name"), &VisualScript::get_variable_default_value);
- ClassDB::bind_method(D_METHOD("set_variable_info", "name", "value"), &VisualScript::_set_variable_info);
- ClassDB::bind_method(D_METHOD("get_variable_info", "name"), &VisualScript::_get_variable_info);
- ClassDB::bind_method(D_METHOD("set_variable_export", "name", "enable"), &VisualScript::set_variable_export);
- ClassDB::bind_method(D_METHOD("get_variable_export", "name"), &VisualScript::get_variable_export);
- ClassDB::bind_method(D_METHOD("rename_variable", "name", "new_name"), &VisualScript::rename_variable);
-
- ClassDB::bind_method(D_METHOD("add_custom_signal", "name"), &VisualScript::add_custom_signal);
- ClassDB::bind_method(D_METHOD("has_custom_signal", "name"), &VisualScript::has_custom_signal);
- ClassDB::bind_method(D_METHOD("custom_signal_add_argument", "name", "type", "argname", "index"), &VisualScript::custom_signal_add_argument, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("custom_signal_set_argument_type", "name", "argidx", "type"), &VisualScript::custom_signal_set_argument_type);
- ClassDB::bind_method(D_METHOD("custom_signal_get_argument_type", "name", "argidx"), &VisualScript::custom_signal_get_argument_type);
- ClassDB::bind_method(D_METHOD("custom_signal_set_argument_name", "name", "argidx", "argname"), &VisualScript::custom_signal_set_argument_name);
- ClassDB::bind_method(D_METHOD("custom_signal_get_argument_name", "name", "argidx"), &VisualScript::custom_signal_get_argument_name);
- ClassDB::bind_method(D_METHOD("custom_signal_remove_argument", "name", "argidx"), &VisualScript::custom_signal_remove_argument);
- ClassDB::bind_method(D_METHOD("custom_signal_get_argument_count", "name"), &VisualScript::custom_signal_get_argument_count);
- ClassDB::bind_method(D_METHOD("custom_signal_swap_argument", "name", "argidx", "withidx"), &VisualScript::custom_signal_swap_argument);
- ClassDB::bind_method(D_METHOD("remove_custom_signal", "name"), &VisualScript::remove_custom_signal);
- ClassDB::bind_method(D_METHOD("rename_custom_signal", "name", "new_name"), &VisualScript::rename_custom_signal);
-
- ClassDB::bind_method(D_METHOD("set_instance_base_type", "type"), &VisualScript::set_instance_base_type);
-
- ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
- ClassDB::bind_method(D_METHOD("_get_data"), &VisualScript::_get_data);
-
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
-
- ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id")));
-}
-
-VisualScript::VisualScript() {
- base_type = "Object";
- is_tool_script = false;
-}
-
-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.
-}
-
-RBSet<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
- List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>);
- get_sequence_connection_list(sc);
- 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);
- }
- }
- memdelete(sc);
- return connected;
-}
-
-VisualScript::~VisualScript() {
- // Remove all nodes and stuff that hold data refs.
- for (const KeyValue<int, NodeData> &E : nodes) {
- remove_node(E.key);
- }
-}
-
-////////////////////////////////////////////
-
-bool VisualScriptInstance::set(const StringName &p_name, const Variant &p_value) {
- HashMap<StringName, Variant>::Iterator E = variables.find(p_name);
- if (!E) {
- return false;
- }
-
- E->value = p_value;
-
- return true;
-}
-
-bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
- HashMap<StringName, Variant>::ConstIterator E = variables.find(p_name);
- if (!E) {
- return false;
- }
-
- r_ret = E->value;
- return true;
-}
-
-void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
- if (!E.value._export) {
- continue;
- }
- PropertyInfo p = E.value.info;
- p.name = String(E.key);
- p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
- p_properties->push_back(p);
- }
-}
-
-Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
- if (!script->variables.has(p_name)) {
- if (r_is_valid) {
- *r_is_valid = false;
- }
- ERR_FAIL_V(Variant::NIL);
- }
-
- if (r_is_valid) {
- *r_is_valid = true;
- }
-
- return script->variables[p_name].info.type;
-}
-
-void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
- for (const KeyValue<StringName, VisualScript::Function> &E : script->functions) {
- MethodInfo mi;
- 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;
- arg.name = vsf->get_argument_name(i);
- arg.type = vsf->get_argument_type(i);
-
- mi.arguments.push_back(arg);
- }
-
- if (!vsf->is_sequenced()) { // Assumed constant if not sequenced.
- mi.flags |= METHOD_FLAG_CONST;
- }
- }
- }
- p_list->push_back(mi);
- }
-}
-
-bool VisualScriptInstance::has_method(const StringName &p_method) const {
- return script->functions.has(p_method);
-}
-
-//#define VSDEBUG(m_text) print_line(m_text)
-#define VSDEBUG(m_text)
-
-void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int p_pass, int *pass_stack, const Variant **input_args, Variant **output_args, Variant *variant_stack, Callable::CallError &r_error, String &error_str, VisualScriptNodeInstance **r_error_node) {
- ERR_FAIL_COND(node->pass_idx == -1);
-
- if (pass_stack[node->pass_idx] == p_pass) {
- return;
- }
-
- pass_stack[node->pass_idx] = p_pass;
-
- if (!node->dependencies.is_empty()) {
- int dc = node->dependencies.size();
- VisualScriptNodeInstance **deps = node->dependencies.ptrw();
-
- for (int i = 0; i < dc; i++) {
- _dependency_step(deps[i], p_pass, pass_stack, input_args, output_args, variant_stack, r_error, error_str, r_error_node);
- if (r_error.error != Callable::CallError::CALL_OK) {
- return;
- }
- }
- }
-
- for (int i = 0; i < node->input_port_count; i++) {
- int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
-
- if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
- // Is a default value (unassigned input port).
- input_args[i] = &default_values[index];
- } else {
- // Regular temporary in stack.
- input_args[i] = &variant_stack[index];
- }
- }
- for (int i = 0; i < node->output_port_count; i++) {
- output_args[i] = &variant_stack[node->output_ports[i]];
- }
-
- Variant *working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr;
-
- node->step(input_args, output_args, VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE, working_mem, r_error, error_str);
- // Ignore return.
- if (r_error.error != Callable::CallError::CALL_OK) {
- *r_error_node = node;
- }
-}
-
-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) {
- HashMap<StringName, Function>::Iterator F = functions.find(p_method);
- ERR_FAIL_COND_V(!F, Variant());
- Function *f = &F->value;
-
- // This call goes separate, so it can be yielded and suspended.
- Variant *variant_stack = (Variant *)p_stack;
- bool *sequence_bits = (bool *)(variant_stack + f->max_stack);
- const Variant **input_args = (const Variant **)(sequence_bits + f->node_count);
- Variant **output_args = (Variant **)(input_args + max_input_args);
- int flow_max = f->flow_stack_size;
- int *flow_stack = flow_max ? (int *)(output_args + max_output_args) : (int *)nullptr;
- int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)nullptr;
-
- String error_str;
-
- VisualScriptNodeInstance *node = p_node;
- bool error = false;
- int current_node_id = f->node;
- Variant return_value;
- Variant *working_mem = nullptr;
-
- int flow_stack_pos = p_flow_stack_pos;
-
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- VisualScriptLanguage::singleton->enter_function(this, &p_method, variant_stack, &working_mem, &current_node_id);
- }
-#endif
-
- while (true) {
- p_pass++; // Increment pass.
- current_node_id = node->get_id();
-
- VSDEBUG("==========AT NODE: " + itos(current_node_id) + " base: " + node->get_base_node()->get_class_name());
- VSDEBUG("AT STACK POS: " + itos(flow_stack_pos));
-
- // Setup working mem.
- working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr;
-
- VSDEBUG("WORKING MEM: " + itos(node->working_mem_idx));
-
- if (current_node_id == f->node) {
- // If function node, set up function arguments from beginning of stack.
-
- for (int i = 0; i < f->argument_count; i++) {
- input_args[i] = &variant_stack[i];
- }
- } else {
- // Run dependencies first.
-
- if (!node->dependencies.is_empty()) {
- int dc = node->dependencies.size();
- VisualScriptNodeInstance **deps = node->dependencies.ptrw();
-
- for (int i = 0; i < dc; i++) {
- _dependency_step(deps[i], p_pass, pass_stack, input_args, output_args, variant_stack, r_error, error_str, &node);
- if (r_error.error != Callable::CallError::CALL_OK) {
- error = true;
- current_node_id = node->id;
- break;
- }
- }
- }
-
- if (!error) {
- // Setup input pointers normally.
- VSDEBUG("INPUT PORTS: " + itos(node->input_port_count));
-
- for (int i = 0; i < node->input_port_count; i++) {
- int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
-
- if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
- // Is a default value (unassigned input port).
- input_args[i] = &default_values[index];
- VSDEBUG("\tPORT " + itos(i) + " DEFAULT VAL");
- } else {
- // Regular temporary in stack.
- input_args[i] = &variant_stack[index];
- VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(index));
- }
- }
- }
- }
-
- if (error) {
- break;
- }
-
- // Setup output pointers.
-
- VSDEBUG("OUTPUT PORTS: " + itos(node->output_port_count));
- for (int i = 0; i < node->output_port_count; i++) {
- output_args[i] = &variant_stack[node->output_ports[i]];
- VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(node->output_ports[i]));
- }
-
- // Do step.
-
- VisualScriptNodeInstance::StartMode start_mode;
- {
- if (p_resuming_yield) {
- start_mode = VisualScriptNodeInstance::START_MODE_RESUME_YIELD;
- p_resuming_yield = false; // Should resume only the first time.
- } else if (flow_stack && (flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT)) {
- // If there is a push bit, it means we are continuing a sequence.
- start_mode = VisualScriptNodeInstance::START_MODE_CONTINUE_SEQUENCE;
- } else {
- start_mode = VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE;
- }
- }
-
- VSDEBUG("STEP - STARTSEQ: " + itos(start_mode));
-
- int ret = node->step(input_args, output_args, start_mode, working_mem, r_error, error_str);
-
- if (r_error.error != Callable::CallError::CALL_OK) {
- // Use error from step.
- error = true;
- break;
- }
-
- if (ret & VisualScriptNodeInstance::STEP_YIELD_BIT) {
- // Yielded!
- if (node->get_working_memory_size() == 0) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("A node yielded without working memory, please read the docs on how to yield properly!");
- error = true;
- break;
-
- } else {
- Ref<VisualScriptFunctionState> state = *working_mem;
- if (!state.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Node yielded, but did not return a function state in the first working memory.");
- error = true;
- break;
- }
-
- // Step 1, capture all state.
- state->instance_id = get_owner_ptr()->get_instance_id();
- state->script_id = get_script()->get_instance_id();
- state->instance = this;
- state->function = p_method;
- state->working_mem_index = node->working_mem_idx;
- state->variant_stack_size = f->max_stack;
- state->node = node;
- state->flow_stack_pos = flow_stack_pos;
- state->stack.resize(p_stack_size);
- state->pass = p_pass;
- memcpy(state->stack.ptrw(), p_stack, p_stack_size);
- // Step 2, run away, return directly.
- r_error.error = Callable::CallError::CALL_OK;
-
-#ifdef DEBUG_ENABLED
- // Will re-enter later, so exiting.
- if (EngineDebugger::is_active()) {
- VisualScriptLanguage::singleton->exit_function();
- }
-#endif
-
- return state;
- }
- }
-
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- // line
- bool do_break = false;
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
- if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
- EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
- }
- if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
- do_break = true;
- }
- }
-
- if (EngineDebugger::get_script_debugger()->is_breakpoint(current_node_id, source)) {
- do_break = true;
- }
-
- if (do_break) {
- VisualScriptLanguage::singleton->debug_break("Breakpoint", true);
- }
-
- EngineDebugger::get_singleton()->line_poll();
- }
-#endif
- int output = ret & VisualScriptNodeInstance::STEP_MASK;
-
- VSDEBUG("STEP RETURN: " + itos(ret));
-
- if (ret & VisualScriptNodeInstance::STEP_EXIT_FUNCTION_BIT) {
- if (node->get_working_memory_size() == 0) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Return value must be assigned to first element of node working memory! Fix your node please.");
- error = true;
- } else {
- // Assign from working memory, first element.
- return_value = *working_mem;
- }
-
- VSDEBUG("EXITING FUNCTION - VALUE " + String(return_value));
- break; // Exit function requested, bye
- }
-
- VisualScriptNodeInstance *next = nullptr; // Next node.
-
- if ((ret == output || ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) && node->sequence_output_count) {
- // 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 = true;
- break;
- }
-
- next = node->sequence_outputs[output];
- VSDEBUG("GOT NEXT NODE - " + (next ? itos(next->get_id()) : "Null"));
- }
-
- if (flow_stack) {
- // Update flow stack pos (may have changed).
- flow_stack[flow_stack_pos] = current_node_id;
-
- // Add stack push bit if requested.
- if (ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) {
- flow_stack[flow_stack_pos] |= VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT;
- sequence_bits[node->sequence_index] = true; // Remember sequence bit.
- VSDEBUG("NEXT SEQ - FLAG BIT");
- } else {
- sequence_bits[node->sequence_index] = false; // Forget sequence bit.
- VSDEBUG("NEXT SEQ - NORMAL");
- }
-
- if (ret & VisualScriptNodeInstance::STEP_FLAG_GO_BACK_BIT) {
- // Go back request.
-
- if (flow_stack_pos > 0) {
- flow_stack_pos--;
- node = instances[flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_MASK];
- VSDEBUG("NEXT IS GO BACK");
- } else {
- VSDEBUG("NEXT IS GO BACK, BUT NO NEXT SO EXIT");
- break; // Simply exit without value or error.
- }
- } else if (next) {
- if (sequence_bits[next->sequence_index]) {
- // What happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front
- // because each node has a working memory, we can't really do a sub-sequence
- // as a result, the sequence will be restarted and the stack will roll back to find where this node
- // started the sequence.
-
- bool found = false;
-
- for (int i = flow_stack_pos; i >= 0; i--) {
- if ((flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK) == next->get_id()) {
- flow_stack_pos = i; // Roll back and remove bit.
- flow_stack[i] = next->get_id();
- sequence_bits[next->sequence_index] = false;
- found = true;
- }
- }
-
- if (!found) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Found sequence bit but not the node in the stack (please report).");
- error = true;
- break;
- }
-
- node = next;
- VSDEBUG("RE-ENTERED A LOOP, RETURNED STACK POS TO - " + itos(flow_stack_pos));
-
- } else {
- // Check for stack overflow.
- if (flow_stack_pos + 1 >= flow_max) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = vformat(RTR("Stack overflow (stack size: %s). Check for infinite recursion in your script."), output);
- error = true;
- break;
- }
-
- node = next;
-
- flow_stack_pos++;
- flow_stack[flow_stack_pos] = node->get_id();
-
- VSDEBUG("INCREASE FLOW STACK");
- }
-
- } else {
- // No next node, try to go back in stack to pushed bit.
-
- bool found = false;
-
- for (int i = flow_stack_pos; i >= 0; i--) {
- VSDEBUG("FS " + itos(i) + " - " + itos(flow_stack[i]));
- if (flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT) {
- node = instances[flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK];
- flow_stack_pos = i;
- found = true;
- break;
- }
- }
-
- if (!found) {
- VSDEBUG("NO NEXT NODE, NO GO BACK, EXITING");
- break; // Done, couldn't find a push stack bit.
- }
-
- VSDEBUG("NO NEXT NODE, GO BACK TO: " + itos(flow_stack_pos));
- }
- } else {
- node = next; // Stackless mode, simply assign next node.
- }
- }
-
- if (error) {
- // Error
- // Function, file, line, error, explanation.
- String err_file = script->get_path();
- String err_func = p_method;
- int err_line = current_node_id; // Not a line but it works as one.
-
- if (node && (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD || error_str.is_empty())) {
- if (!error_str.is_empty()) {
- error_str += " ";
- }
-
- if (r_error.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- int errorarg = r_error.argument;
- error_str += "Cannot convert argument " + itos(errorarg + 1) + " to " + Variant::get_type_name(Variant::Type(r_error.expected)) + ".";
- } else if (r_error.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- error_str += "Expected " + itos(r_error.argument) + " arguments.";
- } else if (r_error.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- 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";
- }
- }
-
- //if (!GDScriptLanguage::get_singleton()->debug_break(err_text,false)) {
- // debugger break did not happen
-
- if (!VisualScriptLanguage::singleton->debug_break(error_str, false)) {
- _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
- }
-
- //}
- } else {
- //return_value=
- }
-
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- VisualScriptLanguage::singleton->exit_function();
- }
-#endif
-
- // Clean up variant stack.
- for (int i = 0; i < f->max_stack; i++) {
- variant_stack[i].~Variant();
- }
-
- return return_value;
-}
-
-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
-
- HashMap<StringName, Function>::Iterator F = functions.find(p_method);
- if (!F) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
-
- VSDEBUG("CALLING: " + String(p_method));
-
- Function *f = &F->value;
-
- int total_stack_size = 0;
-
- total_stack_size += f->max_stack * sizeof(Variant); //variants
- total_stack_size += f->node_count * sizeof(bool);
- total_stack_size += (max_input_args + max_output_args) * sizeof(Variant *); //arguments
- total_stack_size += f->flow_stack_size * sizeof(int); //flow
- total_stack_size += f->pass_stack_size * sizeof(int);
-
- VSDEBUG("STACK SIZE: " + itos(total_stack_size));
- VSDEBUG("STACK VARIANTS: : " + itos(f->max_stack));
- VSDEBUG("SEQBITS: : " + itos(f->node_count));
- VSDEBUG("MAX INPUT: " + itos(max_input_args));
- VSDEBUG("MAX OUTPUT: " + itos(max_output_args));
- VSDEBUG("FLOW STACK SIZE: " + itos(f->flow_stack_size));
- VSDEBUG("PASS STACK SIZE: " + itos(f->pass_stack_size));
-
- void *stack = alloca(total_stack_size);
-
- Variant *variant_stack = (Variant *)stack;
- bool *sequence_bits = (bool *)(variant_stack + f->max_stack);
- const Variant **input_args = (const Variant **)(sequence_bits + f->node_count);
- Variant **output_args = (Variant **)(input_args + max_input_args);
- int flow_max = f->flow_stack_size;
- int *flow_stack = flow_max ? (int *)(output_args + max_output_args) : (int *)nullptr;
- int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)nullptr;
-
- for (int i = 0; i < f->node_count; i++) {
- sequence_bits[i] = false; // All starts as false.
- }
-
- memset(pass_stack, 0, f->pass_stack_size * sizeof(int));
-
- 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->value;
-
- if (flow_stack) {
- flow_stack[0] = node->get_id();
- }
-
- VSDEBUG("ARGUMENTS: " + itos(f->argument_count) = " RECEIVED: " + itos(p_argcount));
-
- if (p_argcount < f->argument_count) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = node->get_input_port_count();
-
- return Variant();
- }
-
- if (p_argcount > f->argument_count) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = node->get_input_port_count();
-
- return Variant();
- }
-
- // Allocate variant stack.
- for (int i = 0; i < f->max_stack; i++) {
- memnew_placement(&variant_stack[i], Variant);
- }
-
- // Allocate function arguments (must be copied for yield to work properly).
- for (int i = 0; i < p_argcount; i++) {
- variant_stack[i] = *p_args[i];
- }
-
- return _call_internal(p_method, stack, total_stack_size, node, 0, 0, false, r_error);
-}
-
-void VisualScriptInstance::notification(int p_notification) {
- // Do nothing as this is called using virtual.
-
- Variant what = p_notification;
- const Variant *whatp = &what;
- Callable::CallError ce;
- callp(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); // Do as call.
-}
-
-String VisualScriptInstance::to_string(bool *r_valid) {
- if (has_method(CoreStringNames::get_singleton()->_to_string)) {
- Callable::CallError ce;
- Variant ret = callp(CoreStringNames::get_singleton()->_to_string, nullptr, 0, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- if (ret.get_type() != Variant::STRING) {
- if (r_valid) {
- *r_valid = false;
- }
- ERR_FAIL_V_MSG(String(), "Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String.");
- }
- if (r_valid) {
- *r_valid = true;
- }
- return ret.operator String();
- }
- }
- if (r_valid) {
- *r_valid = false;
- }
- return String();
-}
-
-Ref<Script> VisualScriptInstance::get_script() const {
- return script;
-}
-
-const Variant VisualScriptInstance::get_rpc_config() const {
- return script->get_rpc_config();
-}
-
-void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) {
- script = p_script;
- owner = p_owner;
- source = p_script->get_path();
-
- max_input_args = 0;
- max_output_args = 0;
-
- // Setup variables.
- {
- for (const KeyValue<StringName, VisualScript::Variable> &E : script->variables) {
- variables[E.key] = E.value.default_value;
- }
- }
-
- // Setup functions from sequence trees.
- {
- 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;
- function.flow_stack_size = 0;
- function.pass_stack_size = 0;
- function.node_count = 0;
-
- 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.key));
- ERR_CONTINUE(function.node < 0);
- }
-
- {
- 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.key));
- }
-
- ERR_CONTINUE(!func_node.is_valid());
-
- function.argument_count = func_node->get_argument_count();
- function.max_stack += function.argument_count;
- function.flow_stack_size = func_node->is_stack_less() ? 0 : func_node->get_stack_size();
- max_input_args = MAX(max_input_args, function.argument_count);
- }
- // Function nodes graphs.
- 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 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.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 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 int &F : node_ids) {
- nd_queue.push_back(F);
- }
- List<int> dc_keys;
- while (!nd_queue.is_empty()) {
- int ky = nd_queue.front()->get();
- for (const KeyValue<int, Pair<int, int>> &F : dc_lut[ky]) {
- VisualScript::DataConnection dc;
- dc.from_node = F.value.first;
- dc.from_port = F.value.second;
- dc.to_node = ky;
- dc.to_port = F.key;
- dataconns.insert(dc);
- nd_queue.push_back(dc.from_node);
- node_ids.insert(dc.from_node);
- }
- dc_keys.clear(); // Necessary as get_key_list does a push_back not a set.
- nd_queue.pop_front();
- }
- }
-
- //Multiple passes are required to set up this complex thing..
- //First create the nodes.
- 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;
- instance->input_port_count = node->get_input_value_port_count();
- instance->input_ports = nullptr;
- instance->output_port_count = node->get_output_value_port_count();
- instance->output_ports = nullptr;
- instance->sequence_output_count = node->get_output_sequence_port_count();
- instance->sequence_index = function.node_count++;
- instance->sequence_outputs = nullptr;
- instance->pass_idx = -1;
-
- if (instance->input_port_count) {
- instance->input_ports = memnew_arr(int, instance->input_port_count);
- for (int i = 0; i < instance->input_port_count; i++) {
- instance->input_ports[i] = -1; // If not assigned, will become default value.
- }
- }
-
- if (instance->output_port_count) {
- instance->output_ports = memnew_arr(int, instance->output_port_count);
- for (int i = 0; i < instance->output_port_count; i++) {
- instance->output_ports[i] = -1; // If not assigned, will output to trash.
- }
- }
-
- if (instance->sequence_output_count) {
- instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance *, instance->sequence_output_count);
- for (int i = 0; i < instance->sequence_output_count; i++) {
- instance->sequence_outputs[i] = nullptr; // If it remains null, flow ends here.
- }
- }
-
- if (Object::cast_to<VisualScriptLocalVar>(node.ptr()) || Object::cast_to<VisualScriptLocalVarSet>(*node)) {
- // Working memory is shared only for this node, for the same variables.
- Ref<VisualScriptLocalVar> vslv = node;
-
- StringName var_name;
-
- if (Object::cast_to<VisualScriptLocalVar>(*node)) {
- var_name = String(Object::cast_to<VisualScriptLocalVar>(*node)->get_var_name()).strip_edges();
- } else {
- var_name = String(Object::cast_to<VisualScriptLocalVarSet>(*node)->get_var_name()).strip_edges();
- }
-
- if (!local_var_indices.has(var_name)) {
- local_var_indices[var_name] = function.max_stack;
- function.max_stack++;
- }
-
- instance->working_mem_idx = local_var_indices[var_name];
-
- } else if (instance->get_working_memory_size()) {
- instance->working_mem_idx = function.max_stack;
- function.max_stack += instance->get_working_memory_size();
- } else {
- instance->working_mem_idx = -1; //no working mem
- }
-
- max_input_args = MAX(max_input_args, instance->input_port_count);
- max_output_args = MAX(max_output_args, instance->output_port_count);
-
- instances[F] = instance;
- }
-
- function.trash_pos = function.max_stack++; // create pos for trash
-
- // Second pass, do data connections.
- 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));
- VisualScriptNodeInstance *to = instances[dc.to_node];
- ERR_CONTINUE(dc.from_port >= from->output_port_count);
- ERR_CONTINUE(dc.to_port >= to->input_port_count);
-
- if (from->output_ports[dc.from_port] == -1) {
- int stack_pos = function.max_stack++;
- from->output_ports[dc.from_port] = stack_pos;
- }
-
- if (from->get_sequence_output_count() == 0 && to->dependencies.find(from) == -1) {
- // If the node we are reading from has no output sequence, we must call step() before reading from it.
- if (from->pass_idx == -1) {
- from->pass_idx = function.pass_stack_size;
- function.pass_stack_size++;
- }
- to->dependencies.push_back(from);
- }
-
- to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; // Read from wherever the stack is.
- }
-
- // Third pass, do sequence connections.
- 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));
- VisualScriptNodeInstance *to = instances[sc.to_node];
- ERR_CONTINUE(sc.from_output >= from->sequence_output_count);
-
- from->sequence_outputs[sc.from_output] = to;
- }
-
- //fourth pass:
- // 1) unassigned input ports to default values
- // 2) connect unassigned output ports to trash
- for (const int &F : node_ids) {
- ERR_CONTINUE(!instances.has(F));
-
- 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++) {
- if (instance->input_ports[i] == -1) {
- // Unassigned, connect to default val.
- instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT;
- default_values.push_back(node->get_default_input_value(i));
- }
- }
-
- // Connect to trash.
- for (int i = 0; i < instance->output_port_count; i++) {
- if (instance->output_ports[i] == -1) {
- instance->output_ports[i] = function.trash_pos; //trash is same for all
- }
- }
- }
-
- functions[E.key] = function;
- }
- }
-}
-
-ScriptLanguage *VisualScriptInstance::get_language() {
- return VisualScriptLanguage::singleton;
-}
-
-VisualScriptInstance::VisualScriptInstance() {
-}
-
-VisualScriptInstance::~VisualScriptInstance() {
- {
- MutexLock lock(VisualScriptLanguage::singleton->lock);
-
- script->instances.erase(owner);
- }
-
- for (const KeyValue<int, VisualScriptNodeInstance *> &E : instances) {
- memdelete(E.value);
- }
-}
-
-/////////////////////////////////////////////
-
-/////////////////////
-
-Variant VisualScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- ERR_FAIL_COND_V(function == StringName(), Variant());
-
-#ifdef DEBUG_ENABLED
-
- ERR_FAIL_COND_V_MSG(instance_id.is_valid() && !ObjectDB::get_instance(instance_id), Variant(), "Resumed after yield, but class instance is gone.");
- ERR_FAIL_COND_V_MSG(script_id.is_valid() && !ObjectDB::get_instance(script_id), Variant(), "Resumed after yield, but script is gone.");
-
-#endif
-
- r_error.error = Callable::CallError::CALL_OK;
-
- Array args;
-
- if (p_argcount == 0) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- return Variant();
- } else if (p_argcount == 1) {
- //noooneee, reserved for me, me and only me.
- } else {
- for (int i = 0; i < p_argcount - 1; i++) {
- args.push_back(*p_args[i]);
- }
- }
-
- Ref<VisualScriptFunctionState> self = *p_args[p_argcount - 1]; //hi, I'm myself, needed this to remain alive.
-
- if (self.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = p_argcount - 1;
- r_error.expected = Variant::OBJECT;
- return Variant();
- }
-
- r_error.error = Callable::CallError::CALL_OK;
-
- Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index;
-
- *working_mem = args; // Arguments go to working mem.
-
- Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error);
- function = StringName(); //invalidate
- return ret;
-}
-
-void VisualScriptFunctionState::connect_to_signal(Object *p_obj, const String &p_signal, Array p_binds) {
- ERR_FAIL_NULL(p_obj);
- Vector<Variant> binds;
- for (int i = 0; i < p_binds.size(); i++) {
- binds.push_back(p_binds[i]);
- }
- binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
-
- 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 {
- return function != StringName();
-}
-
-Variant VisualScriptFunctionState::resume(Array p_args) {
- ERR_FAIL_COND_V(function == StringName(), Variant());
-#ifdef DEBUG_ENABLED
-
- ERR_FAIL_COND_V_MSG(instance_id.is_valid() && !ObjectDB::get_instance(instance_id), Variant(), "Resumed after yield, but class instance is gone.");
- ERR_FAIL_COND_V_MSG(script_id.is_valid() && !ObjectDB::get_instance(script_id), Variant(), "Resumed after yield, but script is gone.");
-
-#endif
-
- Callable::CallError r_error;
- r_error.error = Callable::CallError::CALL_OK;
-
- Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index;
-
- *working_mem = p_args; // Arguments go to working mem.
-
- Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error);
- function = StringName(); //invalidate
- return ret;
-}
-
-void VisualScriptFunctionState::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect_to_signal", "obj", "signals", "args"), &VisualScriptFunctionState::connect_to_signal);
- ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Array()));
- ClassDB::bind_method(D_METHOD("is_valid"), &VisualScriptFunctionState::is_valid);
- ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &VisualScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));
-}
-
-VisualScriptFunctionState::VisualScriptFunctionState() {
-}
-
-VisualScriptFunctionState::~VisualScriptFunctionState() {
- if (function != StringName()) {
- Variant *s = ((Variant *)stack.ptr());
- for (int i = 0; i < variant_stack_size; i++) {
- s[i].~Variant();
- }
- }
-}
-
-///////////////////////////////////////////////
-
-String VisualScriptLanguage::get_name() const {
- return "VisualScript";
-}
-
-/* LANGUAGE FUNCTIONS */
-void VisualScriptLanguage::init() {
-}
-
-String VisualScriptLanguage::get_type() const {
- return "VisualScript";
-}
-
-String VisualScriptLanguage::get_extension() const {
- return "vs";
-}
-
-Error VisualScriptLanguage::execute_file(const String &p_path) {
- return OK;
-}
-
-void VisualScriptLanguage::finish() {
-}
-
-/* EDITOR FUNCTIONS */
-void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
-}
-
-bool VisualScriptLanguage::is_control_flow_keyword(String p_keyword) const {
- return false;
-}
-
-void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
-}
-
-void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
-}
-
-bool VisualScriptLanguage::is_using_templates() {
- return false;
-}
-
-Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
- Ref<VisualScript> script;
- script.instantiate();
- script->set_instance_base_type(p_base_class_name);
- 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, HashSet<int> *r_safe_lines) const {
- return false;
-}
-
-Script *VisualScriptLanguage::create_script() const {
- return memnew(VisualScript);
-}
-
-bool VisualScriptLanguage::has_named_classes() const {
- return false;
-}
-
-bool VisualScriptLanguage::supports_builtin_mode() const {
- return true;
-}
-
-int VisualScriptLanguage::find_function(const String &p_function, const String &p_code) const {
- return -1;
-}
-
-String VisualScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {
- return String();
-}
-
-void VisualScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {
-}
-
-void VisualScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
-}
-
-/* DEBUGGER FUNCTIONS */
-
-bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, const String &p_error) {
- // Break because of parse error.
-
- if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
- _debug_parse_err_node = p_node;
- _debug_parse_err_file = p_file;
- _debug_error = p_error;
- EngineDebugger::get_script_debugger()->debug(this, false, true);
- return true;
- } else {
- return false;
- }
-}
-
-bool VisualScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {
- if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
- _debug_parse_err_node = -1;
- _debug_parse_err_file = "";
- _debug_error = p_error;
- EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, true);
- return true;
- } else {
- return false;
- }
-}
-
-String VisualScriptLanguage::debug_get_error() const {
- return _debug_error;
-}
-
-int VisualScriptLanguage::debug_get_stack_level_count() const {
- if (_debug_parse_err_node >= 0) {
- return 1;
- }
-
- return _debug_call_stack_pos;
-}
-
-int VisualScriptLanguage::debug_get_stack_level_line(int p_level) const {
- if (_debug_parse_err_node >= 0) {
- return _debug_parse_err_node;
- }
-
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, -1);
-
- int l = _debug_call_stack_pos - p_level - 1;
-
- return *(_call_stack[l].current_id);
-}
-
-String VisualScriptLanguage::debug_get_stack_level_function(int p_level) const {
- if (_debug_parse_err_node >= 0) {
- return "";
- }
-
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return *_call_stack[l].function;
-}
-
-String VisualScriptLanguage::debug_get_stack_level_source(int p_level) const {
- if (_debug_parse_err_node >= 0) {
- return _debug_parse_err_file;
- }
-
- ERR_FAIL_INDEX_V(p_level, _debug_call_stack_pos, "");
- int l = _debug_call_stack_pos - p_level - 1;
- return _call_stack[l].instance->get_script_ptr()->get_path();
-}
-
-void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- if (_debug_parse_err_node >= 0) {
- return;
- }
-
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
-
- int l = _debug_call_stack_pos - p_level - 1;
- const StringName *f = _call_stack[l].function;
-
- ERR_FAIL_COND(!_call_stack[l].instance->functions.has(*f));
-
- VisualScriptNodeInstance *node = _call_stack[l].instance->instances[*_call_stack[l].current_id];
- ERR_FAIL_COND(!node);
-
- p_locals->push_back("node_name");
- p_values->push_back(node->get_base_node()->get_text());
-
- for (int i = 0; i < node->input_port_count; i++) {
- String name = node->get_base_node()->get_input_value_port_info(i).name;
- if (name.is_empty()) {
- name = "in_" + itos(i);
- }
-
- p_locals->push_back("input/" + name);
-
- //value is trickier
-
- int in_from = node->input_ports[i];
- int in_value = in_from & VisualScriptNodeInstance::INPUT_MASK;
-
- if (in_from & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
- p_values->push_back(_call_stack[l].instance->default_values[in_value]);
- } else {
- p_values->push_back(_call_stack[l].stack[in_value]);
- }
- }
-
- for (int i = 0; i < node->output_port_count; i++) {
- String name = node->get_base_node()->get_output_value_port_info(i).name;
- if (name.is_empty()) {
- name = "out_" + itos(i);
- }
-
- p_locals->push_back("output/" + name);
-
- //value is trickier
-
- int in_from = node->output_ports[i];
- p_values->push_back(_call_stack[l].stack[in_from]);
- }
-
- for (int i = 0; i < node->get_working_memory_size(); i++) {
- p_locals->push_back("working_mem/mem_" + itos(i));
- p_values->push_back((*_call_stack[l].work_mem)[i]);
- }
-}
-
-void VisualScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- if (_debug_parse_err_node >= 0) {
- return;
- }
-
- ERR_FAIL_INDEX(p_level, _debug_call_stack_pos);
- int l = _debug_call_stack_pos - p_level - 1;
-
- Ref<VisualScript> vs = _call_stack[l].instance->get_script();
- if (vs.is_null()) {
- return;
- }
-
- List<StringName> vars;
- vs->get_variable_list(&vars);
- for (const StringName &E : vars) {
- Variant v;
- if (_call_stack[l].instance->get_variable(E, &v)) {
- p_members->push_back("variables/" + E);
- p_values->push_back(v);
- }
- }
-}
-
-void VisualScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- // No globals are really reachable in gdscript.
-}
-
-String VisualScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {
- return "";
-}
-
-void VisualScriptLanguage::reload_all_scripts() {
-}
-
-void VisualScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
-}
-
-/* LOADER FUNCTIONS */
-
-void VisualScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("vs");
-}
-
-void VisualScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
-}
-
-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() {
-}
-
-void VisualScriptLanguage::profiling_stop() {
-}
-
-int VisualScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) {
- return 0;
-}
-
-int VisualScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) {
- return 0;
-}
-
-VisualScriptLanguage *VisualScriptLanguage::singleton = nullptr;
-
-void VisualScriptLanguage::add_register_func(const String &p_name, VisualScriptNodeRegisterFunc p_func) {
- ERR_FAIL_COND(register_funcs.has(p_name));
- register_funcs[p_name] = p_func;
-}
-
-void VisualScriptLanguage::remove_register_func(const String &p_name) {
- ERR_FAIL_COND(!register_funcs.has(p_name));
- register_funcs.erase(p_name);
-}
-
-Ref<VisualScriptNode> VisualScriptLanguage::create_node_from_name(const String &p_name) {
- ERR_FAIL_COND_V(!register_funcs.has(p_name), Ref<VisualScriptNode>());
-
- return register_funcs[p_name](p_name);
-}
-
-void VisualScriptLanguage::get_registered_node_names(List<String> *r_names) {
- for (const KeyValue<String, VisualScriptNodeRegisterFunc> &E : register_funcs) {
- r_names->push_back(E.key);
- }
-}
-
-VisualScriptLanguage::VisualScriptLanguage() {
- singleton = this;
-
- int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024);
- ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
-
- if (EngineDebugger::is_active()) {
- // Debugging enabled!
- _debug_max_call_stack = dmcs;
- _call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
-
- } else {
- _debug_max_call_stack = 0;
- _call_stack = nullptr;
- }
-}
-
-VisualScriptLanguage::~VisualScriptLanguage() {
- if (_call_stack) {
- memdelete_arr(_call_stack);
- }
- singleton = nullptr;
-}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
deleted file mode 100644
index 14cb14e8d9..0000000000
--- a/modules/visual_script/visual_script.h
+++ /dev/null
@@ -1,627 +0,0 @@
-/*************************************************************************/
-/* visual_script.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 VISUAL_SCRIPT_H
-#define VISUAL_SCRIPT_H
-
-#include "core/debugger/engine_debugger.h"
-#include "core/debugger/script_debugger.h"
-#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;
-class VisualScript;
-
-class VisualScriptNode : public Resource {
- GDCLASS(VisualScriptNode, Resource);
-
- friend class VisualScript;
-
- Ref<VisualScript> script_used;
-
- Array default_input_values;
- bool breakpoint = false;
-
- void _set_default_input_values(Array p_values);
- Array _get_default_input_values() const;
-
- void validate_input_default_values();
-
-protected:
- void ports_changed_notify();
- static void _bind_methods();
-
-public:
- Ref<VisualScript> get_visual_script() const;
-
- virtual int get_output_sequence_port_count() const = 0;
- virtual bool has_input_sequence_port() const = 0;
-
- virtual String get_output_sequence_port_text(int p_port) const = 0;
-
- virtual bool has_mixed_input_and_sequence_ports() const { return false; }
-
- virtual int get_input_value_port_count() const = 0;
- virtual int get_output_value_port_count() const = 0;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const = 0;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const = 0;
-
- void set_default_input_value(int p_port, const Variant &p_value);
- Variant get_default_input_value(int p_port) const;
-
- virtual String get_caption() const = 0;
- virtual String get_text() const;
- virtual String get_category() const = 0;
-
- // Used by editor, this is not really saved.
- void set_breakpoint(bool p_breakpoint);
- bool is_breakpoint() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) = 0;
-
- struct TypeGuess {
- Variant::Type type = Variant::NIL;
- StringName gdclass;
- Ref<Script> script;
- };
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
-
- VisualScriptNode();
-};
-
-class VisualScriptNodeInstance {
- friend class VisualScriptInstance;
- friend class VisualScriptLanguage; // For debugger.
-
- enum { // Input argument addressing.
- INPUT_SHIFT = 1 << 24,
- INPUT_MASK = INPUT_SHIFT - 1,
- INPUT_DEFAULT_VALUE_BIT = INPUT_SHIFT, // from unassigned input port, using default value (edited by user)
- };
-
- int id = 0;
- int sequence_index = 0;
- VisualScriptNodeInstance **sequence_outputs = nullptr;
- int sequence_output_count = 0;
- Vector<VisualScriptNodeInstance *> dependencies;
- int *input_ports = nullptr;
- int input_port_count = 0;
- int *output_ports = nullptr;
- int output_port_count = 0;
- int working_mem_idx = 0;
- int pass_idx = 0;
-
- VisualScriptNode *base = nullptr;
-
-public:
- enum StartMode {
- START_MODE_BEGIN_SEQUENCE,
- START_MODE_CONTINUE_SEQUENCE,
- START_MODE_RESUME_YIELD
- };
-
- enum {
- STEP_SHIFT = 1 << 24,
- STEP_MASK = STEP_SHIFT - 1,
- STEP_FLAG_PUSH_STACK_BIT = STEP_SHIFT, // push bit to stack
- STEP_FLAG_GO_BACK_BIT = STEP_SHIFT << 1, // go back to previous node
- STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, // do not advance past this node
- STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, // return from function
- STEP_YIELD_BIT = STEP_SHIFT << 4, // yield (will find VisualScriptFunctionState state in first working memory)
-
- FLOW_STACK_PUSHED_BIT = 1 << 30, // in flow stack, means bit was pushed (must go back here if end of sequence)
- FLOW_STACK_MASK = FLOW_STACK_PUSHED_BIT - 1
-
- };
-
- _FORCE_INLINE_ int get_input_port_count() const { return input_port_count; }
- _FORCE_INLINE_ int get_output_port_count() const { return output_port_count; }
- _FORCE_INLINE_ int get_sequence_output_count() const { return sequence_output_count; }
-
- _FORCE_INLINE_ int get_id() const { return id; }
-
- virtual int get_working_memory_size() const { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) = 0; // Do a step, return which sequence port to go out.
-
- Ref<VisualScriptNode> get_base_node() { return Ref<VisualScriptNode>(base); }
-
- VisualScriptNodeInstance();
- virtual ~VisualScriptNodeInstance();
-};
-
-class VisualScript : public Script {
- GDCLASS(VisualScript, Script);
-
- RES_BASE_EXTENSION("vs");
-
-public:
- struct SequenceConnection {
- union {
- struct {
- uint64_t from_node : 24;
- uint64_t from_output : 16;
- uint64_t to_node : 24;
- };
- uint64_t id = 0;
- };
-
- bool operator<(const SequenceConnection &p_connection) const {
- return id < p_connection.id;
- }
- };
-
- struct DataConnection {
- union {
- struct {
- uint64_t from_node : 24;
- uint64_t from_port : 8;
- uint64_t to_node : 24;
- uint64_t to_port : 8;
- };
- uint64_t id = 0;
- };
-
- bool operator<(const DataConnection &p_connection) const {
- return id < p_connection.id;
- }
- };
-
-private:
- friend class VisualScriptInstance;
-
- StringName base_type;
- struct Argument {
- String name;
- Variant::Type type = Variant::Type::NIL;
- };
-
- struct NodeData {
- Point2 pos;
- Ref<VisualScriptNode> node;
- };
-
- HashMap<int, NodeData> nodes; // Can be a sparse map.
-
- RBSet<SequenceConnection> sequence_connections;
- RBSet<DataConnection> data_connections;
-
- Vector2 scroll;
-
- struct Function {
- int func_id;
- Function() { func_id = -1; }
- };
-
- struct Variable {
- PropertyInfo info;
- Variant default_value;
- bool _export = false;
- // Add getter & setter options here.
- };
-
- HashMap<StringName, Function> functions;
- HashMap<StringName, Variable> variables;
- HashMap<StringName, Vector<Argument>> custom_signals;
- Dictionary rpc_functions;
-
- HashMap<Object *, VisualScriptInstance *> instances;
-
- bool is_tool_script;
-
-#ifdef TOOLS_ENABLED
- RBSet<PlaceHolderScriptInstance *> placeholders;
- // void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
- virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
- void _update_placeholders();
-#endif
-
- void _set_variable_info(const StringName &p_name, const Dictionary &p_info);
- Dictionary _get_variable_info(const StringName &p_name) const;
-
- void _set_data(const Dictionary &p_data);
- Dictionary _get_data() const;
-
-protected:
- void _node_ports_changed(int p_id);
- static void _bind_methods();
-
-public:
- bool inherits_script(const Ref<Script> &p_script) const override;
-
- void set_scroll(const Vector2 &p_scroll);
- Vector2 get_scroll() const;
-
- void add_function(const StringName &p_name, int p_func_node_id);
- bool has_function(const StringName &p_name) const;
- void remove_function(const StringName &p_name);
- void rename_function(const StringName &p_name, const StringName &p_new_name);
- void get_function_list(List<StringName> *r_functions) const;
- int get_function_node_id(const StringName &p_name) const;
- void set_tool_enabled(bool p_enabled);
-
- void add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2());
- void remove_node(int p_id);
- bool has_node(int p_id) const;
- Ref<VisualScriptNode> get_node(int p_id) const;
- void set_node_position(int p_id, const Point2 &p_pos);
- Point2 get_node_position(int p_id) const;
- void get_node_list(List<int> *r_nodes) const;
-
- void sequence_connect(int p_from_node, int p_from_output, int p_to_node);
- 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;
- 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);
- bool has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
- void get_data_connection_list(List<DataConnection> *r_connection) const;
-
- bool is_input_value_port_connected(int p_node, int p_port) const;
- bool get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const;
-
- void add_variable(const StringName &p_name, const Variant &p_default_value = Variant(), bool p_export = false);
- bool has_variable(const StringName &p_name) const;
- void remove_variable(const StringName &p_name);
- void set_variable_default_value(const StringName &p_name, const Variant &p_value);
- Variant get_variable_default_value(const StringName &p_name) const;
- void set_variable_info(const StringName &p_name, const PropertyInfo &p_info);
- PropertyInfo get_variable_info(const StringName &p_name) const;
- void set_variable_export(const StringName &p_name, bool p_export);
- bool get_variable_export(const StringName &p_name) const;
- void get_variable_list(List<StringName> *r_variables) const;
- void rename_variable(const StringName &p_name, const StringName &p_new_name);
-
- void add_custom_signal(const StringName &p_name);
- bool has_custom_signal(const StringName &p_name) const;
- void custom_signal_add_argument(const StringName &p_func, Variant::Type p_type, const String &p_name, int p_index = -1);
- void custom_signal_set_argument_type(const StringName &p_func, int p_argidx, Variant::Type p_type);
- Variant::Type custom_signal_get_argument_type(const StringName &p_func, int p_argidx) const;
- void custom_signal_set_argument_name(const StringName &p_func, int p_argidx, const String &p_name);
- String custom_signal_get_argument_name(const StringName &p_func, int p_argidx) const;
- void custom_signal_remove_argument(const StringName &p_func, int p_argidx);
- int custom_signal_get_argument_count(const StringName &p_func) const;
- 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);
- 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;
-
- int get_available_id() const;
-
- void set_instance_base_type(const StringName &p_type);
-
- virtual bool can_instantiate() const override;
-
- virtual Ref<Script> get_base_script() const override;
- virtual StringName get_instance_base_type() const override;
- virtual ScriptInstance *instance_create(Object *p_this) override;
- virtual bool instance_has(const Object *p_this) const override;
-
- virtual bool has_source_code() const override;
- virtual String get_source_code() const override;
- virtual void set_source_code(const String &p_code) override;
- virtual Error reload(bool p_keep_state = false) override;
-
-#ifdef TOOLS_ENABLED
- virtual Vector<DocData::ClassDoc> get_documentation() const override {
- Vector<DocData::ClassDoc> docs;
- return docs;
- }
-#endif // TOOLS_ENABLED
-
- virtual bool is_tool() const override;
- virtual bool is_valid() const override;
-
- virtual ScriptLanguage *get_language() const override;
-
- virtual bool has_script_signal(const StringName &p_signal) const override;
- virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override;
-
- virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
-
- virtual bool has_method(const StringName &p_method) const override;
- virtual MethodInfo get_method_info(const StringName &p_method) const override;
-
- virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
-
- virtual int get_member_line(const StringName &p_member) const override;
-
- virtual const Variant get_rpc_config() const override;
-
-#ifdef TOOLS_ENABLED
- virtual bool are_subnodes_edited() const;
-#endif
-
- VisualScript();
- ~VisualScript();
-};
-
-class VisualScriptInstance : public ScriptInstance {
- Object *owner = nullptr;
- Ref<VisualScript> script;
-
- HashMap<StringName, Variant> variables; // Using variable path, not script.
- HashMap<int, VisualScriptNodeInstance *> instances;
-
- struct Function {
- int node = 0;
- int max_stack = 0;
- int trash_pos = 0;
- int flow_stack_size = 0;
- int pass_stack_size = 0;
- int node_count = 0;
- int argument_count = 0;
- };
-
- HashMap<StringName, Function> functions;
-
- Vector<Variant> default_values;
- int max_input_args = 0;
- int max_output_args = 0;
-
- StringName source;
-
- void _dependency_step(VisualScriptNodeInstance *node, int p_pass, int *pass_stack, const Variant **input_args, Variant **output_args, Variant *variant_stack, Callable::CallError &r_error, String &error_str, VisualScriptNodeInstance **r_error_node);
- Variant _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);
-
- friend class VisualScriptFunctionState; // For yield.
- friend class VisualScriptLanguage; // For debugger.
-public:
- virtual bool set(const StringName &p_name, const Variant &p_value);
- virtual bool get(const StringName &p_name, Variant &r_ret) const;
- virtual void get_property_list(List<PropertyInfo> *p_properties) const;
- virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const;
-
- virtual void get_method_list(List<MethodInfo> *p_list) const;
- virtual bool has_method(const StringName &p_method) const;
- virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual void notification(int p_notification);
- String to_string(bool *r_valid);
-
- bool set_variable(const StringName &p_variable, const Variant &p_value) {
- HashMap<StringName, Variant>::Iterator E = variables.find(p_variable);
- if (!E) {
- return false;
- }
-
- E->value = p_value;
- return true;
- }
-
- bool get_variable(const StringName &p_variable, Variant *r_variable) const {
- HashMap<StringName, Variant>::ConstIterator E = variables.find(p_variable);
- if (!E) {
- return false;
- }
-
- *r_variable = E->value;
- return true;
- }
-
- virtual Ref<Script> get_script() const;
-
- _FORCE_INLINE_ VisualScript *get_script_ptr() { return script.ptr(); }
- _FORCE_INLINE_ Object *get_owner_ptr() { return owner; }
-
- void create(const Ref<VisualScript> &p_script, Object *p_owner);
-
- virtual ScriptLanguage *get_language();
-
- virtual const Variant get_rpc_config() const;
-
- VisualScriptInstance();
- ~VisualScriptInstance();
-};
-
-class VisualScriptFunctionState : public RefCounted {
- GDCLASS(VisualScriptFunctionState, RefCounted);
- friend class VisualScriptInstance;
-
- ObjectID instance_id;
- ObjectID script_id;
- VisualScriptInstance *instance = nullptr;
- StringName function;
- Vector<uint8_t> stack;
- int working_mem_index = 0;
- int variant_stack_size = 0;
- VisualScriptNodeInstance *node = nullptr;
- int flow_stack_pos = 0;
- int pass = 0;
-
- Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
-
-protected:
- static void _bind_methods();
-
-public:
- void connect_to_signal(Object *p_obj, const String &p_signal, Array p_binds);
- bool is_valid() const;
- Variant resume(Array p_args);
- VisualScriptFunctionState();
- ~VisualScriptFunctionState();
-};
-
-typedef Ref<VisualScriptNode> (*VisualScriptNodeRegisterFunc)(const String &p_type);
-
-class VisualScriptLanguage : public ScriptLanguage {
- HashMap<String, VisualScriptNodeRegisterFunc> register_funcs;
-
- struct CallLevel {
- Variant *stack = nullptr;
- Variant **work_mem = nullptr;
- const StringName *function = nullptr;
- VisualScriptInstance *instance = nullptr;
- int *current_id = nullptr;
- };
-
- int _debug_parse_err_node = -1;
- String _debug_parse_err_file = "";
- String _debug_error;
- int _debug_call_stack_pos = 0;
- int _debug_max_call_stack;
- CallLevel *_call_stack = nullptr;
-
-public:
- StringName notification = "_notification";
- StringName _get_output_port_unsequenced;
- StringName _step = "_step";
- StringName _subcall = "_subcall";
-
- static VisualScriptLanguage *singleton;
-
- Mutex lock;
-
- bool debug_break(const String &p_error, bool p_allow_continue = true);
- bool debug_break_parse(const String &p_file, int p_node, const String &p_error);
-
- _FORCE_INLINE_ void enter_function(VisualScriptInstance *p_instance, const StringName *p_function, Variant *p_stack, Variant **p_work_mem, int *current_id) {
- if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; // No support for other threads than main for now.
- }
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
- EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() + 1);
- }
-
- if (_debug_call_stack_pos >= _debug_max_call_stack) {
- // Stack overflow.
- _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;
- }
-
- _call_stack[_debug_call_stack_pos].stack = p_stack;
- _call_stack[_debug_call_stack_pos].instance = p_instance;
- _call_stack[_debug_call_stack_pos].function = p_function;
- _call_stack[_debug_call_stack_pos].work_mem = p_work_mem;
- _call_stack[_debug_call_stack_pos].current_id = current_id;
- _debug_call_stack_pos++;
- }
-
- _FORCE_INLINE_ void exit_function() {
- if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; // No support for other threads than main for now.
- }
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
- EngineDebugger::get_script_debugger()->set_depth(EngineDebugger::get_script_debugger()->get_depth() - 1);
- }
-
- if (_debug_call_stack_pos == 0) {
- _debug_error = "Stack underflow (engine bug), please report.";
- EngineDebugger::get_script_debugger()->debug(this);
- return;
- }
-
- _debug_call_stack_pos--;
- }
-
- //////////////////////////////////////
-
- virtual String get_name() const override;
-
- /* LANGUAGE FUNCTIONS */
- virtual void init() override;
- virtual String get_type() const override;
- virtual String get_extension() const override;
- virtual Error execute_file(const String &p_path) override;
- virtual void finish() override;
-
- /* EDITOR FUNCTIONS */
- virtual void get_reserved_words(List<String> *p_words) const override;
- virtual bool is_control_flow_keyword(String p_keyword) const override;
- virtual void get_comment_delimiters(List<String> *p_delimiters) const override;
- 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, 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;
- virtual int find_function(const String &p_function, const String &p_code) const override;
- virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override;
- virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override;
- virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) override;
-
- /* DEBUGGER FUNCTIONS */
-
- virtual String debug_get_error() const override;
- virtual int debug_get_stack_level_count() const override;
- virtual int debug_get_stack_level_line(int p_level) const override;
- virtual String debug_get_stack_level_function(int p_level) const override;
- virtual String debug_get_stack_level_source(int p_level) const override;
- virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
- virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
- virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override;
- virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override;
-
- virtual void reload_all_scripts() override;
- virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
- /* LOADER FUNCTIONS */
-
- 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;
-
- virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override;
- virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override;
-
- void add_register_func(const String &p_name, VisualScriptNodeRegisterFunc p_func);
- void remove_register_func(const String &p_name);
- Ref<VisualScriptNode> create_node_from_name(const String &p_name);
- void get_registered_node_names(List<String> *r_names);
-
- VisualScriptLanguage();
- ~VisualScriptLanguage();
-};
-
-// Aid for registering.
-template <class T>
-static Ref<VisualScriptNode> create_node_generic(const String &p_name) {
- Ref<T> node;
- node.instantiate();
- return node;
-}
-
-#endif // VISUAL_SCRIPT_H
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
deleted file mode 100644
index 44e792869d..0000000000
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ /dev/null
@@ -1,1380 +0,0 @@
-/*************************************************************************/
-/* visual_script_builtin_funcs.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 "visual_script_builtin_funcs.h"
-
-#include "core/io/marshalls.h"
-#include "core/math/math_funcs.h"
-#include "core/object/class_db.h"
-#include "core/object/ref_counted.h"
-#include "core/os/os.h"
-#include "core/variant/variant_parser.h"
-
-const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX] = {
- "sin",
- "cos",
- "tan",
- "sinh",
- "cosh",
- "tanh",
- "asin",
- "acos",
- "atan",
- "atan2",
- "sqrt",
- "fmod",
- "fposmod",
- "floor",
- "ceil",
- "round",
- "abs",
- "sign",
- "pow",
- "log",
- "exp",
- "is_nan",
- "is_inf",
- "ease",
- "step_decimals",
- "snapped",
- "lerp",
- "cubic_interpolate",
- "inverse_lerp",
- "range_lerp",
- "move_toward",
- "randomize",
- "randi",
- "randf",
- "randi_range",
- "randf_range",
- "randfn",
- "seed",
- "rand_seed",
- "deg2rad",
- "rad2deg",
- "linear2db",
- "db2linear",
- "wrapi",
- "wrapf",
- "pingpong",
- "max",
- "min",
- "clamp",
- "nearest_po2",
- "weakref",
- "convert",
- "typeof",
- "type_exists",
- "char",
- "str",
- "print",
- "printerr",
- "printraw",
- "print_verbose",
- "var2str",
- "str2var",
- "var2bytes",
- "bytes2var",
- "smoothstep",
- "posmod",
- "lerp_angle",
- "ord",
-};
-
-VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::find_function(const String &p_string) {
- for (int i = 0; i < FUNC_MAX; i++) {
- if (p_string == func_name[i]) {
- return BuiltinFunc(i);
- }
- }
-
- return FUNC_MAX;
-}
-
-String VisualScriptBuiltinFunc::get_func_name(BuiltinFunc p_func) {
- ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String());
- return func_name[p_func];
-}
-
-int VisualScriptBuiltinFunc::get_output_sequence_port_count() const {
- return has_input_sequence_port() ? 1 : 0;
-}
-
-bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
- switch (func) {
- case MATH_RANDOMIZE:
- case TEXT_PRINT:
- case TEXT_PRINTERR:
- case TEXT_PRINTRAW:
- case TEXT_PRINT_VERBOSE:
- case MATH_SEED:
- return true;
- default:
- return false;
- }
-}
-
-int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
- switch (p_func) {
- case MATH_RANDOMIZE:
- case MATH_RANDI:
- case MATH_RANDF:
- return 0;
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_SQRT:
- case MATH_FLOOR:
- case MATH_CEIL:
- case MATH_ROUND:
- case MATH_ABS:
- case MATH_SIGN:
- case MATH_LOG:
- case MATH_EXP:
- case MATH_ISNAN:
- case MATH_ISINF:
- case MATH_STEP_DECIMALS:
- case MATH_SEED:
- case MATH_RANDSEED:
- case MATH_DEG2RAD:
- case MATH_RAD2DEG:
- case MATH_LINEAR2DB:
- case MATH_DB2LINEAR:
- case LOGIC_NEAREST_PO2:
- case OBJ_WEAKREF:
- case TYPE_OF:
- case TEXT_CHAR:
- case TEXT_ORD:
- case TEXT_STR:
- case TEXT_PRINT:
- case TEXT_PRINTERR:
- case TEXT_PRINTRAW:
- case TEXT_PRINT_VERBOSE:
- case VAR_TO_STR:
- case STR_TO_VAR:
- case TYPE_EXISTS:
- return 1;
- case VAR_TO_BYTES:
- case BYTES_TO_VAR:
- case MATH_ATAN2:
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case MATH_POSMOD:
- case MATH_PINGPONG:
- case MATH_POW:
- case MATH_EASE:
- case MATH_SNAPPED:
- case MATH_RANDI_RANGE:
- case MATH_RANDF_RANGE:
- case MATH_RANDFN:
- case LOGIC_MAX:
- case LOGIC_MIN:
- case TYPE_CONVERT:
- return 2;
- case MATH_LERP:
- case MATH_LERP_ANGLE:
- case MATH_INVERSE_LERP:
- case MATH_SMOOTHSTEP:
- case MATH_MOVE_TOWARD:
- case MATH_WRAP:
- case MATH_WRAPF:
- case LOGIC_CLAMP:
- return 3;
- case MATH_CUBIC_INTERPOLATE:
- case MATH_RANGE_LERP:
- return 5;
- case FUNC_MAX: {
- }
- }
- return 0;
-}
-
-int VisualScriptBuiltinFunc::get_input_value_port_count() const {
- return get_func_argument_count(func);
-}
-
-int VisualScriptBuiltinFunc::get_output_value_port_count() const {
- switch (func) {
- case MATH_RANDOMIZE:
- case TEXT_PRINT:
- case TEXT_PRINTERR:
- case TEXT_PRINTRAW:
- case TEXT_PRINT_VERBOSE:
- case MATH_SEED:
- return 0;
- case MATH_RANDSEED:
- return 2;
- default:
- return 1;
- }
-
- return 1;
-}
-
-String VisualScriptBuiltinFunc::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const {
- switch (func) {
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_SQRT:
- case MATH_FLOOR:
- case MATH_CEIL:
- case MATH_ROUND:
- case MATH_ABS:
- case MATH_SIGN:
- case MATH_LOG:
- case MATH_EXP:
- case MATH_ISNAN:
- case MATH_ISINF: {
- return PropertyInfo(Variant::FLOAT, "s");
- } break;
- case MATH_ATAN2: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "y");
- } else {
- return PropertyInfo(Variant::FLOAT, "x");
- }
- } break;
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case LOGIC_MAX:
- case LOGIC_MIN: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "a");
- } else {
- return PropertyInfo(Variant::FLOAT, "b");
- }
- } break;
- case MATH_POSMOD: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "a");
- } else {
- return PropertyInfo(Variant::INT, "b");
- }
- } break;
- case MATH_POW: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "base");
- } else {
- return PropertyInfo(Variant::FLOAT, "exp");
- }
- } break;
- case MATH_EASE: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "s");
- } else {
- return PropertyInfo(Variant::FLOAT, "curve");
- }
- } break;
- case MATH_STEP_DECIMALS: {
- return PropertyInfo(Variant::FLOAT, "step");
- } break;
- case MATH_SNAPPED: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "s");
- } else {
- return PropertyInfo(Variant::FLOAT, "steps");
- }
- } break;
- case MATH_LERP:
- case MATH_LERP_ANGLE:
- case MATH_INVERSE_LERP:
- case MATH_SMOOTHSTEP: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "from");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "to");
- } else {
- return PropertyInfo(Variant::FLOAT, "weight");
- }
- } break;
- case MATH_CUBIC_INTERPOLATE: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "from");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "to");
- } else if (p_idx == 2) {
- return PropertyInfo(Variant::FLOAT, "pre");
- } else if (p_idx == 3) {
- return PropertyInfo(Variant::FLOAT, "post");
- } else {
- return PropertyInfo(Variant::FLOAT, "weight");
- }
- } break;
- case MATH_RANGE_LERP: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "value");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "istart");
- } else if (p_idx == 2) {
- return PropertyInfo(Variant::FLOAT, "istop");
- } else if (p_idx == 3) {
- return PropertyInfo(Variant::FLOAT, "ostart");
- } else {
- return PropertyInfo(Variant::FLOAT, "ostop");
- }
- } break;
- case MATH_MOVE_TOWARD: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "from");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "to");
- } else {
- return PropertyInfo(Variant::FLOAT, "delta");
- }
- } break;
- case MATH_RANDOMIZE:
- case MATH_RANDI:
- case MATH_RANDF: {
- } break;
- case MATH_RANDI_RANGE: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "from");
- } else {
- return PropertyInfo(Variant::INT, "to");
- }
- } break;
- case MATH_RANDF_RANGE: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "from");
- } else {
- return PropertyInfo(Variant::FLOAT, "to");
- }
- } break;
- case MATH_RANDFN: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "mean");
- } else {
- return PropertyInfo(Variant::FLOAT, "deviation");
- }
- } break;
- case MATH_SEED:
- case MATH_RANDSEED: {
- return PropertyInfo(Variant::INT, "seed");
- } break;
- case MATH_DEG2RAD: {
- return PropertyInfo(Variant::FLOAT, "deg");
- } break;
- case MATH_RAD2DEG: {
- return PropertyInfo(Variant::FLOAT, "rad");
- } break;
- case MATH_LINEAR2DB: {
- return PropertyInfo(Variant::FLOAT, "nrg");
- } break;
- case MATH_DB2LINEAR: {
- return PropertyInfo(Variant::FLOAT, "db");
- } break;
- case MATH_PINGPONG: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "value");
- } else {
- return PropertyInfo(Variant::FLOAT, "length");
- }
- } break;
- case MATH_WRAP: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "value");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::INT, "min");
- } else {
- return PropertyInfo(Variant::INT, "max");
- }
- } break;
- case MATH_WRAPF:
- case LOGIC_CLAMP: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "value");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "min");
- } else {
- return PropertyInfo(Variant::FLOAT, "max");
- }
- } break;
- case LOGIC_NEAREST_PO2: {
- return PropertyInfo(Variant::INT, "value");
- } break;
- case OBJ_WEAKREF: {
- return PropertyInfo(Variant::OBJECT, "source");
- } break;
- case TYPE_CONVERT: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::NIL, "what");
- } else {
- return PropertyInfo(Variant::STRING, "type");
- }
- } break;
- case TYPE_OF: {
- return PropertyInfo(Variant::NIL, "what");
- } break;
- case TYPE_EXISTS: {
- return PropertyInfo(Variant::STRING, "type");
- } break;
- case TEXT_ORD: {
- return PropertyInfo(Variant::STRING, "character");
- } break;
- case TEXT_CHAR: {
- return PropertyInfo(Variant::INT, "ascii");
- } break;
- case TEXT_STR:
- case TEXT_PRINT:
- case TEXT_PRINTERR:
- case TEXT_PRINTRAW:
- case TEXT_PRINT_VERBOSE: {
- return PropertyInfo(Variant::NIL, "value");
- } break;
- case STR_TO_VAR: {
- return PropertyInfo(Variant::STRING, "string");
- } break;
- case VAR_TO_STR:
- case VAR_TO_BYTES: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::NIL, "var");
- } else {
- return PropertyInfo(Variant::BOOL, "full_objects");
- }
-
- } break;
- case BYTES_TO_VAR: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes");
- } else {
- return PropertyInfo(Variant::BOOL, "allow_objects");
- }
- } break;
- case FUNC_MAX: {
- }
- }
-
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) const {
- Variant::Type t = Variant::NIL;
- switch (func) {
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_ATAN2:
- case MATH_SQRT:
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case MATH_FLOOR:
- case MATH_CEIL: {
- t = Variant::FLOAT;
- } break;
- case MATH_POSMOD: {
- t = Variant::INT;
- } break;
- case MATH_ROUND: {
- t = Variant::FLOAT;
- } break;
- case MATH_ABS: {
- t = Variant::NIL;
- } break;
- case MATH_SIGN: {
- t = Variant::NIL;
- } break;
- case MATH_POW:
- case MATH_LOG:
- case MATH_EXP: {
- t = Variant::FLOAT;
- } break;
- case MATH_ISNAN:
- case MATH_ISINF: {
- t = Variant::BOOL;
- } break;
- case MATH_EASE: {
- t = Variant::FLOAT;
- } break;
- case MATH_STEP_DECIMALS: {
- t = Variant::INT;
- } break;
- case MATH_SNAPPED:
- case MATH_LERP:
- case MATH_CUBIC_INTERPOLATE:
- case MATH_LERP_ANGLE:
- case MATH_INVERSE_LERP:
- case MATH_RANGE_LERP:
- case MATH_SMOOTHSTEP:
- case MATH_MOVE_TOWARD:
- case MATH_RANDOMIZE: {
- } break;
- case MATH_RANDI: {
- t = Variant::INT;
- } break;
- case MATH_RANDF:
- case MATH_RANDFN:
- case MATH_RANDF_RANGE: {
- t = Variant::FLOAT;
- } break;
- case MATH_RANDI_RANGE: {
- t = Variant::INT;
- } break;
- case MATH_SEED: {
- } break;
- case MATH_RANDSEED: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "rnd");
- } else {
- return PropertyInfo(Variant::INT, "seed");
- }
- } break;
- case MATH_DEG2RAD:
- case MATH_RAD2DEG:
- case MATH_LINEAR2DB:
- case MATH_WRAPF:
- case MATH_PINGPONG:
- case MATH_DB2LINEAR: {
- t = Variant::FLOAT;
- } break;
- case MATH_WRAP: {
- t = Variant::INT;
- } break;
- case LOGIC_MAX:
- case LOGIC_MIN:
- case LOGIC_CLAMP: {
- } break;
-
- case LOGIC_NEAREST_PO2: {
- t = Variant::NIL;
- } break;
- case OBJ_WEAKREF: {
- t = Variant::OBJECT;
-
- } break;
- case TYPE_CONVERT: {
- } break;
- case TEXT_ORD:
- case TYPE_OF: {
- t = Variant::INT;
-
- } break;
- case TYPE_EXISTS: {
- t = Variant::BOOL;
-
- } break;
- case TEXT_CHAR:
- case TEXT_STR: {
- t = Variant::STRING;
-
- } break;
- case TEXT_PRINT: {
- } break;
- case TEXT_PRINTERR: {
- } break;
- case TEXT_PRINTRAW: {
- } break;
- case TEXT_PRINT_VERBOSE: {
- } break;
- case VAR_TO_STR: {
- t = Variant::STRING;
- } break;
- case STR_TO_VAR: {
- } break;
- case VAR_TO_BYTES: {
- if (p_idx == 0) {
- t = Variant::PACKED_BYTE_ARRAY;
- } else {
- t = Variant::BOOL;
- }
-
- } break;
- case BYTES_TO_VAR: {
- if (p_idx == 1) {
- t = Variant::BOOL;
- }
- } break;
- case FUNC_MAX: {
- }
- }
-
- return PropertyInfo(t, "");
-}
-
-/*
-String VisualScriptBuiltinFunc::get_caption() const {
- return "BuiltinFunc";
-}
-
-*/
-
-String VisualScriptBuiltinFunc::get_caption() const {
- return func_name[func];
-}
-
-void VisualScriptBuiltinFunc::set_func(BuiltinFunc p_which) {
- ERR_FAIL_INDEX(p_which, FUNC_MAX);
- func = p_which;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptBuiltinFunc::BuiltinFunc VisualScriptBuiltinFunc::get_func() {
- return func;
-}
-
-#define VALIDATE_ARG_NUM(m_arg) \
- if (!p_inputs[m_arg]->is_num()) { \
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
- r_error.argument = m_arg; \
- r_error.expected = Variant::FLOAT; \
- return; \
- }
-
-void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Callable::CallError &r_error, String &r_error_str) {
- switch (p_func) {
- case VisualScriptBuiltinFunc::MATH_SIN: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::sin((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_COS: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::cos((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_TAN: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::tan((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SINH: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::sinh((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_COSH: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::cosh((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_TANH: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::tanh((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ASIN: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::asin((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ACOS: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::acos((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ATAN: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::atan((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ATAN2: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SQRT: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::sqrt((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FMOD: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FPOSMOD: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_POSMOD: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::posmod((int64_t)*p_inputs[0], (int64_t)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_FLOOR: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::floor((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_CEIL: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::ceil((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ROUND: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::round((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ABS: {
- if (p_inputs[0]->get_type() == Variant::INT) {
- int64_t i = *p_inputs[0];
- *r_return = ABS(i);
- } else if (p_inputs[0]->get_type() == Variant::FLOAT) {
- real_t r = *p_inputs[0];
- *r_return = Math::abs(r);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- }
- } break;
- case VisualScriptBuiltinFunc::MATH_SIGN: {
- if (p_inputs[0]->get_type() == Variant::INT) {
- int64_t i = *p_inputs[0];
- *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0);
- } else if (p_inputs[0]->get_type() == Variant::FLOAT) {
- real_t r = *p_inputs[0];
- *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- }
- } break;
- case VisualScriptBuiltinFunc::MATH_POW: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LOG: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::log((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_EXP: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::exp((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ISNAN: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::is_nan((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_ISINF: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::is_inf((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_EASE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_STEP_DECIMALS: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::step_decimals((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SNAPPED: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::snapped((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LERP: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_CUBIC_INTERPOLATE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- VALIDATE_ARG_NUM(3);
- VALIDATE_ARG_NUM(4);
- *r_return = Math::cubic_interpolate((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LERP_ANGLE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::lerp_angle((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_INVERSE_LERP: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RANGE_LERP: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- VALIDATE_ARG_NUM(3);
- VALIDATE_ARG_NUM(4);
- *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SMOOTHSTEP: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::smoothstep((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_MOVE_TOWARD: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
- Math::randomize();
-
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDI: {
- *r_return = Math::rand();
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDF: {
- *r_return = Math::randf();
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDI_RANGE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::random((int)*p_inputs[0], (int)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDF_RANGE: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDFN: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::randfn((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_SEED: {
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_inputs[0];
- Math::seed(seed);
-
- } break;
- case VisualScriptBuiltinFunc::MATH_RANDSEED: {
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_inputs[0];
- int ret = Math::rand_from_seed(&seed);
- Array reta;
- reta.push_back(ret);
- reta.push_back(seed);
- *r_return = reta;
-
- } break;
- case VisualScriptBuiltinFunc::MATH_DEG2RAD: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::deg2rad((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_RAD2DEG: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::rad2deg((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_LINEAR2DB: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::linear2db((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_DB2LINEAR: {
- VALIDATE_ARG_NUM(0);
- *r_return = Math::db2linear((double)*p_inputs[0]);
- } break;
- case VisualScriptBuiltinFunc::MATH_PINGPONG: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- *r_return = Math::pingpong((double)*p_inputs[0], (double)*p_inputs[1]);
- } break;
- case VisualScriptBuiltinFunc::MATH_WRAP: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::MATH_WRAPF: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
- case VisualScriptBuiltinFunc::LOGIC_MAX: {
- if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) {
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- *r_return = MAX(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
-
- *r_return = MAX(a, b);
- }
-
- } break;
- case VisualScriptBuiltinFunc::LOGIC_MIN: {
- if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) {
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- *r_return = MIN(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
-
- *r_return = MIN(a, b);
- }
- } break;
- case VisualScriptBuiltinFunc::LOGIC_CLAMP: {
- if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) {
- int64_t a = *p_inputs[0];
- int64_t b = *p_inputs[1];
- int64_t c = *p_inputs[2];
- *r_return = CLAMP(a, b, c);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- real_t a = *p_inputs[0];
- real_t b = *p_inputs[1];
- real_t c = *p_inputs[2];
-
- *r_return = CLAMP(a, b, c);
- }
- } break;
- case VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2: {
- VALIDATE_ARG_NUM(0);
- int64_t num = *p_inputs[0];
- *r_return = next_power_of_2(num);
- } break;
- case VisualScriptBuiltinFunc::OBJ_WEAKREF: {
- if (p_inputs[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
-
- return;
- }
-
- if (p_inputs[0]->is_ref_counted()) {
- Ref<RefCounted> r = *p_inputs[0];
- if (!r.is_valid()) {
- return;
- }
-
- Ref<WeakRef> wref = memnew(WeakRef);
- wref->set_ref(r);
- *r_return = wref;
- } else {
- Object *obj = *p_inputs[0];
- if (!obj) {
- return;
- }
- Ref<WeakRef> wref = memnew(WeakRef);
- wref->set_obj(obj);
- *r_return = wref;
- }
-
- } break;
- case VisualScriptBuiltinFunc::TYPE_CONVERT: {
- VALIDATE_ARG_NUM(1);
- int type = *p_inputs[1];
- if (type < 0 || type >= Variant::VARIANT_MAX) {
- r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- return;
-
- } else {
- Variant::construct(Variant::Type(type), *r_return, p_inputs, 1, r_error);
- }
- } break;
- case VisualScriptBuiltinFunc::TYPE_OF: {
- *r_return = p_inputs[0]->get_type();
-
- } break;
- case VisualScriptBuiltinFunc::TYPE_EXISTS: {
- *r_return = ClassDB::class_exists(*p_inputs[0]);
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_CHAR: {
- char32_t result[2] = { *p_inputs[0], 0 };
-
- *r_return = String(result);
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_ORD: {
- if (p_inputs[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
-
- return;
- }
-
- String str = p_inputs[0]->operator String();
-
- if (str.length() != 1) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- *r_return = "Expected a string of length 1 (a character).";
-
- return;
- }
-
- *r_return = str.get(0);
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_STR: {
- String str = *p_inputs[0];
-
- *r_return = str;
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINT: {
- String str = *p_inputs[0];
- print_line(str);
-
- } break;
-
- case VisualScriptBuiltinFunc::TEXT_PRINTERR: {
- String str = *p_inputs[0];
- print_error(str);
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINTRAW: {
- String str = *p_inputs[0];
- OS::get_singleton()->print("%s", str.utf8().get_data());
-
- } break;
- case VisualScriptBuiltinFunc::TEXT_PRINT_VERBOSE: {
- String str = *p_inputs[0];
- print_verbose(str);
- } break;
- case VisualScriptBuiltinFunc::VAR_TO_STR: {
- String vars;
- VariantWriter::write_to_string(*p_inputs[0], vars);
- *r_return = vars;
- } break;
- case VisualScriptBuiltinFunc::STR_TO_VAR: {
- if (p_inputs[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
-
- return;
- }
-
- VariantParser::StreamString ss;
- ss.s = *p_inputs[0];
-
- String errs;
- int line;
- Error err = VariantParser::parse(&ss, *r_return, errs, line);
-
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- *r_return = "Parse error at line " + itos(line) + ": " + errs;
- return;
- }
-
- } break;
- case VisualScriptBuiltinFunc::VAR_TO_BYTES: {
- if (p_inputs[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- return;
- }
- PackedByteArray barr;
- int len;
- bool full_objects = *p_inputs[1];
- Error err = encode_variant(*p_inputs[0], nullptr, len, full_objects);
- if (err) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
- return;
- }
-
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- encode_variant(*p_inputs[0], w, len, full_objects);
- }
- *r_return = barr;
- } break;
- case VisualScriptBuiltinFunc::BYTES_TO_VAR: {
- if (p_inputs[0]->get_type() != Variant::PACKED_BYTE_ARRAY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- return;
- }
- if (p_inputs[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- return;
- }
-
- PackedByteArray varr = *p_inputs[0];
- bool allow_objects = *p_inputs[1];
- Variant ret;
- {
- const uint8_t *r = varr.ptr();
- Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects);
- if (err != OK) {
- r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- return;
- }
- }
-
- *r_return = ret;
-
- } break;
- default: {
- }
- }
-}
-
-class VisualScriptNodeInstanceBuiltinFunc : public VisualScriptNodeInstance {
-public:
- VisualScriptBuiltinFunc *node = nullptr;
- VisualScriptInstance *instance = nullptr;
-
- VisualScriptBuiltinFunc::BuiltinFunc func;
-
- //virtual int get_working_memory_size() const override { return 0; }
- //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 {
- VisualScriptBuiltinFunc::exec_func(func, p_inputs, p_outputs[0], r_error, r_error_str);
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptBuiltinFunc::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceBuiltinFunc *instance = memnew(VisualScriptNodeInstanceBuiltinFunc);
- instance->node = this;
- instance->instance = p_instance;
- instance->func = func;
- return instance;
-}
-
-void VisualScriptBuiltinFunc::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_func", "which"), &VisualScriptBuiltinFunc::set_func);
- ClassDB::bind_method(D_METHOD("get_func"), &VisualScriptBuiltinFunc::get_func);
-
- String cc;
-
- for (int i = 0; i < FUNC_MAX; i++) {
- if (i > 0) {
- cc += ",";
- }
- cc += func_name[i];
- }
- ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, cc), "set_func", "get_func");
-
- BIND_ENUM_CONSTANT(MATH_SIN);
- BIND_ENUM_CONSTANT(MATH_COS);
- BIND_ENUM_CONSTANT(MATH_TAN);
- BIND_ENUM_CONSTANT(MATH_SINH);
- BIND_ENUM_CONSTANT(MATH_COSH);
- BIND_ENUM_CONSTANT(MATH_TANH);
- BIND_ENUM_CONSTANT(MATH_ASIN);
- BIND_ENUM_CONSTANT(MATH_ACOS);
- BIND_ENUM_CONSTANT(MATH_ATAN);
- BIND_ENUM_CONSTANT(MATH_ATAN2);
- BIND_ENUM_CONSTANT(MATH_SQRT);
- BIND_ENUM_CONSTANT(MATH_FMOD);
- BIND_ENUM_CONSTANT(MATH_FPOSMOD);
- BIND_ENUM_CONSTANT(MATH_FLOOR);
- BIND_ENUM_CONSTANT(MATH_CEIL);
- BIND_ENUM_CONSTANT(MATH_ROUND);
- BIND_ENUM_CONSTANT(MATH_ABS);
- BIND_ENUM_CONSTANT(MATH_SIGN);
- BIND_ENUM_CONSTANT(MATH_POW);
- BIND_ENUM_CONSTANT(MATH_LOG);
- BIND_ENUM_CONSTANT(MATH_EXP);
- BIND_ENUM_CONSTANT(MATH_ISNAN);
- BIND_ENUM_CONSTANT(MATH_ISINF);
- BIND_ENUM_CONSTANT(MATH_EASE);
- BIND_ENUM_CONSTANT(MATH_STEP_DECIMALS);
- BIND_ENUM_CONSTANT(MATH_SNAPPED);
- BIND_ENUM_CONSTANT(MATH_LERP);
- BIND_ENUM_CONSTANT(MATH_CUBIC_INTERPOLATE);
- BIND_ENUM_CONSTANT(MATH_INVERSE_LERP);
- BIND_ENUM_CONSTANT(MATH_RANGE_LERP);
- BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD);
- BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
- BIND_ENUM_CONSTANT(MATH_RANDI);
- BIND_ENUM_CONSTANT(MATH_RANDF);
- BIND_ENUM_CONSTANT(MATH_RANDI_RANGE);
- BIND_ENUM_CONSTANT(MATH_RANDF_RANGE);
- BIND_ENUM_CONSTANT(MATH_RANDFN);
- BIND_ENUM_CONSTANT(MATH_SEED);
- BIND_ENUM_CONSTANT(MATH_RANDSEED);
- BIND_ENUM_CONSTANT(MATH_DEG2RAD);
- BIND_ENUM_CONSTANT(MATH_RAD2DEG);
- BIND_ENUM_CONSTANT(MATH_LINEAR2DB);
- BIND_ENUM_CONSTANT(MATH_DB2LINEAR);
- BIND_ENUM_CONSTANT(MATH_WRAP);
- BIND_ENUM_CONSTANT(MATH_WRAPF);
- BIND_ENUM_CONSTANT(MATH_PINGPONG);
- BIND_ENUM_CONSTANT(LOGIC_MAX);
- BIND_ENUM_CONSTANT(LOGIC_MIN);
- BIND_ENUM_CONSTANT(LOGIC_CLAMP);
- BIND_ENUM_CONSTANT(LOGIC_NEAREST_PO2);
- BIND_ENUM_CONSTANT(OBJ_WEAKREF);
- BIND_ENUM_CONSTANT(TYPE_CONVERT);
- BIND_ENUM_CONSTANT(TYPE_OF);
- BIND_ENUM_CONSTANT(TYPE_EXISTS);
- BIND_ENUM_CONSTANT(TEXT_CHAR);
- BIND_ENUM_CONSTANT(TEXT_STR);
- BIND_ENUM_CONSTANT(TEXT_PRINT);
- BIND_ENUM_CONSTANT(TEXT_PRINTERR);
- BIND_ENUM_CONSTANT(TEXT_PRINTRAW);
- BIND_ENUM_CONSTANT(TEXT_PRINT_VERBOSE);
- BIND_ENUM_CONSTANT(VAR_TO_STR);
- BIND_ENUM_CONSTANT(STR_TO_VAR);
- BIND_ENUM_CONSTANT(VAR_TO_BYTES);
- BIND_ENUM_CONSTANT(BYTES_TO_VAR);
- BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP);
- BIND_ENUM_CONSTANT(MATH_POSMOD);
- BIND_ENUM_CONSTANT(MATH_LERP_ANGLE);
- BIND_ENUM_CONSTANT(TEXT_ORD);
- BIND_ENUM_CONSTANT(FUNC_MAX);
-}
-
-VisualScriptBuiltinFunc::VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func) {
- this->func = func;
-}
-
-VisualScriptBuiltinFunc::VisualScriptBuiltinFunc() {
- func = MATH_SIN;
-}
-
-template <VisualScriptBuiltinFunc::BuiltinFunc func>
-static Ref<VisualScriptNode> create_builtin_func_node(const String &p_name) {
- Ref<VisualScriptBuiltinFunc> node = memnew(VisualScriptBuiltinFunc(func));
- return node;
-}
-
-void register_visual_script_builtin_func_node() {
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/sin", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/cos", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/tan", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TAN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/sinh", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SINH>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/cosh", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_COSH>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/tanh", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_TANH>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/asin", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ASIN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/acos", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ACOS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/atan2", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ATAN2>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/sqrt", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SQRT>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/fmod", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FMOD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/fposmod", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FPOSMOD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/posmod", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POSMOD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/floor", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_FLOOR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/ceil", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CEIL>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/round", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ROUND>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/abs", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ABS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/sign", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SIGN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/pow", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POW>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/log", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LOG>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/exp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EXP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/isnan", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISNAN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/isinf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_ISINF>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/step_decimals", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEP_DECIMALS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/snapped", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SNAPPED>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/cubic_interpolate", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CUBIC_INTERPOLATE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp_angle", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP_ANGLE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI_RANGE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF_RANGE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randfn", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDFN>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/deg2rad", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DEG2RAD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/pingpong", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_PINGPONG>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/max", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/min", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/clamp", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_CLAMP>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref", create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/char", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_CHAR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/ord", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_ORD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/str", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/print", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/printraw", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTRAW>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/print_verbose", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT_VERBOSE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2str", create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_STR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/str2var", create_builtin_func_node<VisualScriptBuiltinFunc::STR_TO_VAR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2bytes", create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_BYTES>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/bytes2var", create_builtin_func_node<VisualScriptBuiltinFunc::BYTES_TO_VAR>);
-}
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
deleted file mode 100644
index 18935b9995..0000000000
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*************************************************************************/
-/* visual_script_builtin_funcs.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 VISUAL_SCRIPT_BUILTIN_FUNCS_H
-#define VISUAL_SCRIPT_BUILTIN_FUNCS_H
-
-#include "visual_script.h"
-
-class VisualScriptBuiltinFunc : public VisualScriptNode {
- GDCLASS(VisualScriptBuiltinFunc, VisualScriptNode);
-
-public:
- enum BuiltinFunc {
- MATH_SIN,
- MATH_COS,
- MATH_TAN,
- MATH_SINH,
- MATH_COSH,
- MATH_TANH,
- MATH_ASIN,
- MATH_ACOS,
- MATH_ATAN,
- MATH_ATAN2,
- MATH_SQRT,
- MATH_FMOD,
- MATH_FPOSMOD,
- MATH_FLOOR,
- MATH_CEIL,
- MATH_ROUND,
- MATH_ABS,
- MATH_SIGN,
- MATH_POW,
- MATH_LOG,
- MATH_EXP,
- MATH_ISNAN,
- MATH_ISINF,
- MATH_EASE,
- MATH_STEP_DECIMALS,
- MATH_SNAPPED,
- MATH_LERP,
- MATH_CUBIC_INTERPOLATE,
- MATH_INVERSE_LERP,
- MATH_RANGE_LERP,
- MATH_MOVE_TOWARD,
- MATH_RANDOMIZE,
- MATH_RANDI,
- MATH_RANDF,
- MATH_RANDI_RANGE,
- MATH_RANDF_RANGE,
- MATH_RANDFN,
- MATH_SEED,
- MATH_RANDSEED,
- MATH_DEG2RAD,
- MATH_RAD2DEG,
- MATH_LINEAR2DB,
- MATH_DB2LINEAR,
- MATH_WRAP,
- MATH_WRAPF,
- MATH_PINGPONG,
- LOGIC_MAX,
- LOGIC_MIN,
- LOGIC_CLAMP,
- LOGIC_NEAREST_PO2,
- OBJ_WEAKREF,
- TYPE_CONVERT,
- TYPE_OF,
- TYPE_EXISTS,
- TEXT_CHAR,
- TEXT_STR,
- TEXT_PRINT,
- TEXT_PRINTERR,
- TEXT_PRINTRAW,
- TEXT_PRINT_VERBOSE,
- VAR_TO_STR,
- STR_TO_VAR,
- VAR_TO_BYTES,
- BYTES_TO_VAR,
- MATH_SMOOTHSTEP,
- MATH_POSMOD,
- MATH_LERP_ANGLE,
- TEXT_ORD,
- FUNC_MAX
- };
-
- static int get_func_argument_count(BuiltinFunc p_func);
- static String get_func_name(BuiltinFunc p_func);
- static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Callable::CallError &r_error, String &r_error_str);
- static BuiltinFunc find_function(const String &p_string);
-
-private:
- static const char *func_name[FUNC_MAX];
- BuiltinFunc func;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- //virtual String get_text() const;
- virtual String get_category() const override { return "functions"; }
-
- void set_func(BuiltinFunc p_which);
- BuiltinFunc get_func();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func);
- VisualScriptBuiltinFunc();
-};
-
-VARIANT_ENUM_CAST(VisualScriptBuiltinFunc::BuiltinFunc)
-
-void register_visual_script_builtin_func_node();
-
-#endif // VISUAL_SCRIPT_BUILTIN_FUNCS_H
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
deleted file mode 100644
index e0f6436094..0000000000
--- a/modules/visual_script/visual_script_expression.cpp
+++ /dev/null
@@ -1,1570 +0,0 @@
-/*************************************************************************/
-/* visual_script_expression.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 "visual_script_expression.h"
-
-bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_value) {
- if (String(p_name) == "expression") {
- expression = p_value;
- expression_dirty = true;
- ports_changed_notify();
- return true;
- }
-
- if (String(p_name) == "out_type") {
- output_type = Variant::Type(int(p_value));
- expression_dirty = true;
- ports_changed_notify();
- return true;
- }
- if (String(p_name) == "sequenced") {
- sequenced = p_value;
- ports_changed_notify();
- return true;
- }
-
- if (String(p_name) == "input_count") {
- int from = inputs.size();
- inputs.resize(int(p_value));
- for (int i = from; i < inputs.size(); i++) {
- inputs.write[i].name = String::chr('a' + i);
- if (from == 0) {
- inputs.write[i].type = output_type;
- } else {
- inputs.write[i].type = inputs[from - 1].type;
- }
- }
- expression_dirty = true;
- ports_changed_notify();
- notify_property_list_changed();
- return true;
- }
-
- if (String(p_name).begins_with("input_")) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int();
- ERR_FAIL_INDEX_V(idx, inputs.size(), false);
-
- String what = String(p_name).get_slice("/", 1);
-
- if (what == "type") {
- inputs.write[idx].type = Variant::Type(int(p_value));
- } else if (what == "name") {
- inputs.write[idx].name = p_value;
- } else {
- return false;
- }
-
- expression_dirty = true;
- ports_changed_notify();
- return true;
- }
-
- return false;
-}
-
-bool VisualScriptExpression::_get(const StringName &p_name, Variant &r_ret) const {
- if (String(p_name) == "expression") {
- r_ret = expression;
- return true;
- }
-
- if (String(p_name) == "out_type") {
- r_ret = output_type;
- return true;
- }
-
- if (String(p_name) == "sequenced") {
- r_ret = sequenced;
- return true;
- }
-
- if (String(p_name) == "input_count") {
- r_ret = inputs.size();
- return true;
- }
-
- if (String(p_name).begins_with("input_")) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int();
- ERR_FAIL_INDEX_V(idx, inputs.size(), false);
-
- String what = String(p_name).get_slice("/", 1);
-
- if (what == "type") {
- r_ret = inputs[idx].type;
- } else if (what == "name") {
- r_ret = inputs[idx].name;
- } else {
- return false;
- }
-
- return true;
- }
-
- return false;
-}
-
-void VisualScriptExpression::_get_property_list(List<PropertyInfo> *p_list) const {
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- p_list->push_back(PropertyInfo(Variant::STRING, "expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, "out_type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1"));
- p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced"));
-
- for (int i = 0; i < inputs.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "input_" + itos(i) + "/type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name"));
- }
-}
-
-int VisualScriptExpression::get_output_sequence_port_count() const {
- return sequenced ? 1 : 0;
-}
-
-bool VisualScriptExpression::has_input_sequence_port() const {
- return sequenced;
-}
-
-String VisualScriptExpression::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-int VisualScriptExpression::get_input_value_port_count() const {
- return inputs.size();
-}
-
-int VisualScriptExpression::get_output_value_port_count() const {
- return 1;
-}
-
-PropertyInfo VisualScriptExpression::get_input_value_port_info(int p_idx) const {
- return PropertyInfo(inputs[p_idx].type, inputs[p_idx].name);
-}
-
-PropertyInfo VisualScriptExpression::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(output_type, "result");
-}
-
-String VisualScriptExpression::get_caption() const {
- return RTR("Expression");
-}
-
-String VisualScriptExpression::get_text() const {
- return expression;
-}
-
-Error VisualScriptExpression::_get_token(Token &r_token) {
- while (true) {
-#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
-
- char32_t cchar = GET_CHAR();
- if (cchar == 0) {
- r_token.type = TK_EOF;
- return OK;
- }
-
- switch (cchar) {
- case 0: {
- r_token.type = TK_EOF;
- return OK;
- } break;
- case '{': {
- r_token.type = TK_CURLY_BRACKET_OPEN;
- return OK;
- };
- case '}': {
- r_token.type = TK_CURLY_BRACKET_CLOSE;
- return OK;
- };
- case '[': {
- r_token.type = TK_BRACKET_OPEN;
- return OK;
- };
- case ']': {
- r_token.type = TK_BRACKET_CLOSE;
- return OK;
- };
- case '(': {
- r_token.type = TK_PARENTHESIS_OPEN;
- return OK;
- };
- case ')': {
- r_token.type = TK_PARENTHESIS_CLOSE;
- return OK;
- };
- case ',': {
- r_token.type = TK_COMMA;
- return OK;
- };
- case ':': {
- r_token.type = TK_COLON;
- return OK;
- };
- case '.': {
- r_token.type = TK_PERIOD;
- return OK;
- };
- case '=': {
- cchar = GET_CHAR();
- if (cchar == '=') {
- r_token.type = TK_OP_EQUAL;
- } else {
- _set_error("Expected '='");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- return OK;
- };
- case '!': {
- if (expression[str_ofs] == '=') {
- r_token.type = TK_OP_NOT_EQUAL;
- str_ofs++;
- } else {
- r_token.type = TK_OP_NOT;
- }
- return OK;
- };
- case '>': {
- if (expression[str_ofs] == '=') {
- r_token.type = TK_OP_GREATER_EQUAL;
- str_ofs++;
- } else if (expression[str_ofs] == '>') {
- r_token.type = TK_OP_SHIFT_RIGHT;
- str_ofs++;
- } else {
- r_token.type = TK_OP_GREATER;
- }
- return OK;
- };
- case '<': {
- if (expression[str_ofs] == '=') {
- r_token.type = TK_OP_LESS_EQUAL;
- str_ofs++;
- } else if (expression[str_ofs] == '<') {
- r_token.type = TK_OP_SHIFT_LEFT;
- str_ofs++;
- } else {
- r_token.type = TK_OP_LESS;
- }
- return OK;
- };
- case '+': {
- r_token.type = TK_OP_ADD;
- return OK;
- };
- case '-': {
- r_token.type = TK_OP_SUB;
- return OK;
- };
- case '/': {
- r_token.type = TK_OP_DIV;
- return OK;
- };
- case '*': {
- r_token.type = TK_OP_MUL;
- return OK;
- };
- case '%': {
- r_token.type = TK_OP_MOD;
- return OK;
- };
- case '&': {
- if (expression[str_ofs] == '&') {
- r_token.type = TK_OP_AND;
- str_ofs++;
- } else {
- r_token.type = TK_OP_BIT_AND;
- }
- return OK;
- };
- case '|': {
- if (expression[str_ofs] == '|') {
- r_token.type = TK_OP_OR;
- str_ofs++;
- } else {
- r_token.type = TK_OP_BIT_OR;
- }
- return OK;
- };
- case '^': {
- r_token.type = TK_OP_BIT_XOR;
-
- return OK;
- };
- case '~': {
- r_token.type = TK_OP_BIT_INVERT;
-
- return OK;
- };
- case '"': {
- String str;
- char32_t prev = 0;
- while (true) {
- char32_t ch = GET_CHAR();
-
- if (ch == 0) {
- _set_error("Unterminated String");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- } else if (ch == '"') {
- break;
- } else if (ch == '\\') {
- //escaped characters...
-
- char32_t next = GET_CHAR();
- if (next == 0) {
- _set_error("Unterminated String");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- char32_t res = 0;
-
- switch (next) {
- case 'b':
- res = 8;
- break;
- case 't':
- res = 9;
- break;
- case 'n':
- res = 10;
- break;
- case 'f':
- res = 12;
- break;
- case 'r':
- res = 13;
- break;
- case 'U':
- case 'u': {
- // Hexadecimal sequence.
- int hex_len = (next == 'U') ? 6 : 4;
- for (int j = 0; j < hex_len; j++) {
- char32_t c = GET_CHAR();
-
- if (c == 0) {
- _set_error("Unterminated String");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- if (!is_hex_digit(c)) {
- _set_error("Malformed hex constant in string");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- char32_t v;
- if (is_digit(c)) {
- v = c - '0';
- } else if (c >= 'a' && c <= 'f') {
- v = c - 'a';
- v += 10;
- } else if (c >= 'A' && c <= 'F') {
- v = c - 'A';
- v += 10;
- } else {
- ERR_PRINT("Bug parsing hex constant.");
- v = 0;
- }
-
- res <<= 4;
- res |= v;
- }
-
- } break;
- default: {
- res = next;
- } break;
- }
-
- // Parse UTF-16 pair.
- if ((res & 0xfffffc00) == 0xd800) {
- if (prev == 0) {
- prev = res;
- continue;
- } else {
- _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- } else if ((res & 0xfffffc00) == 0xdc00) {
- if (prev == 0) {
- _set_error("Invalid UTF-16 sequence in string, unpaired trail surrogate");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- } else {
- res = (prev << 10UL) + res - ((0xd800 << 10UL) + 0xdc00 - 0x10000);
- prev = 0;
- }
- }
- if (prev != 0) {
- _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- str += res;
- } else {
- if (prev != 0) {
- _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- str += ch;
- }
- }
- if (prev != 0) {
- _set_error("Invalid UTF-16 sequence in string, unpaired lead surrogate");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
-
- r_token.type = TK_CONSTANT;
- r_token.value = str;
- return OK;
-
- } break;
- default: {
- if (cchar <= 32) {
- break;
- }
-
- if (is_digit(cchar)) {
- //a number
-
- String num;
-#define READING_SIGN 0
-#define READING_INT 1
-#define READING_DEC 2
-#define READING_EXP 3
-#define READING_DONE 4
- int reading = READING_INT;
-
- char32_t c = cchar;
- bool exp_sign = false;
- bool exp_beg = false;
- bool is_float = false;
-
- while (true) {
- switch (reading) {
- case READING_INT: {
- if (is_digit(c)) {
- //pass
- } else if (c == '.') {
- reading = READING_DEC;
- is_float = true;
- } else if (c == 'e') {
- reading = READING_EXP;
- } else {
- reading = READING_DONE;
- }
-
- } break;
- case READING_DEC: {
- if (is_digit(c)) {
- } else if (c == 'e') {
- reading = READING_EXP;
-
- } else {
- reading = READING_DONE;
- }
-
- } break;
- case READING_EXP: {
- if (is_digit(c)) {
- exp_beg = true;
-
- } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
- if (c == '-') {
- is_float = true;
- }
- exp_sign = true;
-
- } else {
- reading = READING_DONE;
- }
- } break;
- }
-
- if (reading == READING_DONE) {
- break;
- }
- num += String::chr(c);
- c = GET_CHAR();
- }
-
- str_ofs--;
-
- r_token.type = TK_CONSTANT;
-
- if (is_float) {
- r_token.value = num.to_float();
- } else {
- r_token.value = num.to_int();
- }
- return OK;
-
- } else if (is_ascii_char(cchar) || cchar == '_') {
- String id;
- bool first = true;
-
- while (is_ascii_char(cchar) || cchar == '_' || (!first && is_digit(cchar))) {
- id += String::chr(cchar);
- cchar = GET_CHAR();
- first = false;
- }
-
- str_ofs--; //go back one
-
- if (id == "in") {
- r_token.type = TK_OP_IN;
- } else if (id == "null") {
- r_token.type = TK_CONSTANT;
- r_token.value = Variant();
- } else if (id == "true") {
- r_token.type = TK_CONSTANT;
- r_token.value = true;
- } else if (id == "false") {
- r_token.type = TK_CONSTANT;
- r_token.value = false;
- } else if (id == "PI") {
- r_token.type = TK_CONSTANT;
- r_token.value = Math_PI;
- } else if (id == "TAU") {
- r_token.type = TK_CONSTANT;
- r_token.value = Math_TAU;
- } else if (id == "INF") {
- r_token.type = TK_CONSTANT;
- r_token.value = INFINITY;
- } else if (id == "NAN") {
- r_token.type = TK_CONSTANT;
- r_token.value = NAN;
- } else if (id == "not") {
- r_token.type = TK_OP_NOT;
- } else if (id == "or") {
- r_token.type = TK_OP_OR;
- } else if (id == "and") {
- r_token.type = TK_OP_AND;
- } else if (id == "self") {
- r_token.type = TK_SELF;
- } else {
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (id == Variant::get_type_name(Variant::Type(i))) {
- r_token.type = TK_BASIC_TYPE;
- r_token.value = i;
- return OK;
- }
- }
-
- VisualScriptBuiltinFunc::BuiltinFunc bifunc = VisualScriptBuiltinFunc::find_function(id);
- if (bifunc != VisualScriptBuiltinFunc::FUNC_MAX) {
- r_token.type = TK_BUILTIN_FUNC;
- r_token.value = bifunc;
- return OK;
- }
-
- r_token.type = TK_IDENTIFIER;
- r_token.value = id;
- }
-
- return OK;
- } else {
- _set_error("Unexpected character.");
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
- }
- }
- }
- }
-
- r_token.type = TK_ERROR;
- return ERR_PARSE_ERROR;
-}
-
-const char *VisualScriptExpression::token_name[TK_MAX] = {
- "CURLY BRACKET OPEN",
- "CURLY BRACKET CLOSE",
- "BRACKET OPEN",
- "BRACKET CLOSE",
- "PARENTHESIS OPEN",
- "PARENTHESIS CLOSE",
- "IDENTIFIER",
- "BUILTIN FUNC",
- "SELF",
- "CONSTANT",
- "BASIC TYPE",
- "COLON",
- "COMMA",
- "PERIOD",
- "OP IN",
- "OP EQUAL",
- "OP NOT EQUAL",
- "OP LESS",
- "OP LESS EQUAL",
- "OP GREATER",
- "OP GREATER EQUAL",
- "OP AND",
- "OP OR",
- "OP NOT",
- "OP ADD",
- "OP SUB",
- "OP MUL",
- "OP DIV",
- "OP MOD",
- "OP SHIFT LEFT",
- "OP SHIFT RIGHT",
- "OP BIT AND",
- "OP BIT OR",
- "OP BIT XOR",
- "OP BIT INVERT",
- "EOF",
- "ERROR"
-};
-
-VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
- Vector<Expression> expression;
-
- while (true) {
- //keep appending stuff to expression
- ENode *expr = nullptr;
-
- Token tk;
- _get_token(tk);
- if (error_set) {
- return nullptr;
- }
-
- switch (tk.type) {
- case TK_CURLY_BRACKET_OPEN: {
- //a dictionary
- DictionaryNode *dn = alloc_node<DictionaryNode>();
-
- while (true) {
- int cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_CURLY_BRACKET_CLOSE) {
- break;
- }
- str_ofs = cofs; //revert
- //parse an expression
- ENode *expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
- dn->dict.push_back(expr2);
-
- _get_token(tk);
- if (tk.type != TK_COLON) {
- _set_error("Expected ':'");
- return nullptr;
- }
-
- expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
-
- dn->dict.push_back(expr2);
-
- cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_COMMA) {
- //all good
- } else if (tk.type == TK_CURLY_BRACKET_CLOSE) {
- str_ofs = cofs;
- } else {
- _set_error("Expected ',' or '}'");
- }
- }
-
- expr = dn;
- } break;
- case TK_BRACKET_OPEN: {
- //an array
-
- ArrayNode *an = alloc_node<ArrayNode>();
-
- while (true) {
- int cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_BRACKET_CLOSE) {
- break;
- }
- str_ofs = cofs; //revert
- //parse an expression
- ENode *expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
- an->array.push_back(expr2);
-
- cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_COMMA) {
- //all good
- } else if (tk.type == TK_BRACKET_CLOSE) {
- str_ofs = cofs;
- } else {
- _set_error("Expected ',' or ']'");
- }
- }
-
- expr = an;
- } break;
- case TK_PARENTHESIS_OPEN: {
- //a suexpression
- ENode *e = _parse_expression();
- if (error_set) {
- return nullptr;
- }
- _get_token(tk);
- if (tk.type != TK_PARENTHESIS_CLOSE) {
- _set_error("Expected ')'");
- return nullptr;
- }
-
- expr = e;
-
- } break;
- case TK_IDENTIFIER: {
- String what = tk.value;
- int index = -1;
- for (int i = 0; i < inputs.size(); i++) {
- if (what == inputs[i].name) {
- index = i;
- break;
- }
- }
-
- if (index != -1) {
- InputNode *input = alloc_node<InputNode>();
- input->index = index;
- expr = input;
- } else {
- _set_error("Invalid input identifier '" + what + "'. For script variables, use self (locals are for inputs)." + what);
- return nullptr;
- }
- } break;
- case TK_SELF: {
- SelfNode *self = alloc_node<SelfNode>();
- expr = self;
- } break;
- case TK_CONSTANT: {
- ConstantNode *constant = alloc_node<ConstantNode>();
- constant->value = tk.value;
- expr = constant;
- } break;
- case TK_BASIC_TYPE: {
- //constructor..
-
- Variant::Type bt = Variant::Type(int(tk.value));
- _get_token(tk);
- if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '('");
- return nullptr;
- }
-
- ConstructorNode *constructor = alloc_node<ConstructorNode>();
- constructor->data_type = bt;
-
- while (true) {
- int cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_PARENTHESIS_CLOSE) {
- break;
- }
- str_ofs = cofs; //revert
- //parse an expression
- ENode *expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
-
- constructor->arguments.push_back(expr2);
-
- cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_COMMA) {
- //all good
- } else if (tk.type == TK_PARENTHESIS_CLOSE) {
- str_ofs = cofs;
- } else {
- _set_error("Expected ',' or ')'");
- }
- }
-
- expr = constructor;
-
- } break;
- case TK_BUILTIN_FUNC: {
- //builtin function
-
- _get_token(tk);
- if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '('");
- return nullptr;
- }
-
- BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>();
- bifunc->func = VisualScriptBuiltinFunc::BuiltinFunc(int(tk.value));
-
- while (true) {
- int cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_PARENTHESIS_CLOSE) {
- break;
- }
- str_ofs = cofs; //revert
- //parse an expression
- ENode *expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
-
- bifunc->arguments.push_back(expr2);
-
- cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_COMMA) {
- //all good
- } else if (tk.type == TK_PARENTHESIS_CLOSE) {
- str_ofs = cofs;
- } else {
- _set_error("Expected ',' or ')'");
- }
- }
-
- int expected_args = VisualScriptBuiltinFunc::get_func_argument_count(bifunc->func);
- if (bifunc->arguments.size() != expected_args) {
- _set_error("Builtin func '" + VisualScriptBuiltinFunc::get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments.");
- }
-
- expr = bifunc;
-
- } break;
- case TK_OP_SUB: {
- Expression e;
- e.is_op = true;
- e.op = Variant::OP_NEGATE;
- expression.push_back(e);
- continue;
- } break;
- case TK_OP_NOT: {
- Expression e;
- e.is_op = true;
- e.op = Variant::OP_NOT;
- expression.push_back(e);
- continue;
- } break;
-
- default: {
- _set_error("Expected expression.");
- return nullptr;
- } break;
- }
-
- //before going to operators, must check indexing!
-
- while (true) {
- int cofs2 = str_ofs;
- _get_token(tk);
- if (error_set) {
- return nullptr;
- }
-
- bool done = false;
-
- switch (tk.type) {
- case TK_BRACKET_OPEN: {
- //value indexing
-
- IndexNode *index = alloc_node<IndexNode>();
- index->base = expr;
-
- ENode *what = _parse_expression();
- if (!what) {
- return nullptr;
- }
-
- index->index = what;
-
- _get_token(tk);
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']' at end of index.");
- return nullptr;
- }
- expr = index;
-
- } break;
- case TK_PERIOD: {
- //named indexing or function call
- _get_token(tk);
- if (tk.type != TK_IDENTIFIER) {
- _set_error("Expected identifier after '.'");
- return nullptr;
- }
-
- StringName identifier = tk.value;
-
- int cofs = str_ofs;
- _get_token(tk);
- if (tk.type == TK_PARENTHESIS_OPEN) {
- //function call
- CallNode *func_call = alloc_node<CallNode>();
- func_call->method = identifier;
- func_call->base = expr;
-
- while (true) {
- int cofs3 = str_ofs;
- _get_token(tk);
- if (tk.type == TK_PARENTHESIS_CLOSE) {
- break;
- }
- str_ofs = cofs3; //revert
- //parse an expression
- ENode *expr2 = _parse_expression();
- if (!expr2) {
- return nullptr;
- }
-
- func_call->arguments.push_back(expr2);
-
- cofs3 = str_ofs;
- _get_token(tk);
- if (tk.type == TK_COMMA) {
- //all good
- } else if (tk.type == TK_PARENTHESIS_CLOSE) {
- str_ofs = cofs3;
- } else {
- _set_error("Expected ',' or ')'");
- }
- }
-
- expr = func_call;
- } else {
- //named indexing
- str_ofs = cofs;
-
- NamedIndexNode *index = alloc_node<NamedIndexNode>();
- index->base = expr;
- index->name = identifier;
- expr = index;
- }
-
- } break;
- default: {
- str_ofs = cofs2;
- done = true;
- } break;
- }
-
- if (done) {
- break;
- }
- }
-
- //push expression
- {
- Expression e;
- e.is_op = false;
- e.node = expr;
- expression.push_back(e);
- }
-
- //ok finally look for an operator
-
- int cofs = str_ofs;
- _get_token(tk);
- if (error_set) {
- return nullptr;
- }
-
- Variant::Operator op = Variant::OP_MAX;
-
- switch (tk.type) {
- case TK_OP_IN:
- op = Variant::OP_IN;
- break;
- case TK_OP_EQUAL:
- op = Variant::OP_EQUAL;
- break;
- case TK_OP_NOT_EQUAL:
- op = Variant::OP_NOT_EQUAL;
- break;
- case TK_OP_LESS:
- op = Variant::OP_LESS;
- break;
- case TK_OP_LESS_EQUAL:
- op = Variant::OP_LESS_EQUAL;
- break;
- case TK_OP_GREATER:
- op = Variant::OP_GREATER;
- break;
- case TK_OP_GREATER_EQUAL:
- op = Variant::OP_GREATER_EQUAL;
- break;
- case TK_OP_AND:
- op = Variant::OP_AND;
- break;
- case TK_OP_OR:
- op = Variant::OP_OR;
- break;
- case TK_OP_NOT:
- op = Variant::OP_NOT;
- break;
- case TK_OP_ADD:
- op = Variant::OP_ADD;
- break;
- case TK_OP_SUB:
- op = Variant::OP_SUBTRACT;
- break;
- case TK_OP_MUL:
- op = Variant::OP_MULTIPLY;
- break;
- case TK_OP_DIV:
- op = Variant::OP_DIVIDE;
- break;
- case TK_OP_MOD:
- op = Variant::OP_MODULE;
- break;
- case TK_OP_SHIFT_LEFT:
- op = Variant::OP_SHIFT_LEFT;
- break;
- case TK_OP_SHIFT_RIGHT:
- op = Variant::OP_SHIFT_RIGHT;
- break;
- case TK_OP_BIT_AND:
- op = Variant::OP_BIT_AND;
- break;
- case TK_OP_BIT_OR:
- op = Variant::OP_BIT_OR;
- break;
- case TK_OP_BIT_XOR:
- op = Variant::OP_BIT_XOR;
- break;
- case TK_OP_BIT_INVERT:
- op = Variant::OP_BIT_NEGATE;
- break;
- default: {
- };
- }
-
- if (op == Variant::OP_MAX) { //stop appending stuff
- str_ofs = cofs;
- break;
- }
-
- //push operator and go on
- {
- Expression e;
- e.is_op = true;
- e.op = op;
- expression.push_back(e);
- }
- }
-
- /* Reduce the set of expressions and place them in an operator tree, respecting precedence */
-
- while (expression.size() > 1) {
- int next_op = -1;
- int min_priority = 0xFFFFF;
- bool is_unary = false;
-
- for (int i = 0; i < expression.size(); i++) {
- if (!expression[i].is_op) {
- continue;
- }
-
- int priority;
-
- bool unary = false;
-
- switch (expression[i].op) {
- case Variant::OP_BIT_NEGATE:
- priority = 0;
- unary = true;
- break;
- case Variant::OP_NEGATE:
- priority = 1;
- unary = true;
- break;
-
- case Variant::OP_MULTIPLY:
- priority = 2;
- break;
- case Variant::OP_DIVIDE:
- priority = 2;
- break;
- case Variant::OP_MODULE:
- priority = 2;
- break;
-
- case Variant::OP_ADD:
- priority = 3;
- break;
- case Variant::OP_SUBTRACT:
- priority = 3;
- break;
-
- case Variant::OP_SHIFT_LEFT:
- priority = 4;
- break;
- case Variant::OP_SHIFT_RIGHT:
- priority = 4;
- break;
-
- case Variant::OP_BIT_AND:
- priority = 5;
- break;
- case Variant::OP_BIT_XOR:
- priority = 6;
- break;
- case Variant::OP_BIT_OR:
- priority = 7;
- break;
-
- case Variant::OP_LESS:
- priority = 8;
- break;
- case Variant::OP_LESS_EQUAL:
- priority = 8;
- break;
- case Variant::OP_GREATER:
- priority = 8;
- break;
- case Variant::OP_GREATER_EQUAL:
- priority = 8;
- break;
-
- case Variant::OP_EQUAL:
- priority = 8;
- break;
- case Variant::OP_NOT_EQUAL:
- priority = 8;
- break;
-
- case Variant::OP_IN:
- priority = 10;
- break;
-
- case Variant::OP_NOT:
- priority = 11;
- unary = true;
- break;
- case Variant::OP_AND:
- priority = 12;
- break;
- case Variant::OP_OR:
- priority = 13;
- break;
-
- default: {
- _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op));
- return nullptr;
- }
- }
-
- if (priority < min_priority) {
- // < is used for left to right (default)
- // <= is used for right to left
-
- next_op = i;
- min_priority = priority;
- is_unary = unary;
- }
- }
-
- if (next_op == -1) {
- _set_error("Yet another parser bug....");
- ERR_FAIL_V(nullptr);
- }
-
- // OK! create operator..
- if (is_unary) {
- int expr_pos = next_op;
- while (expression[expr_pos].is_op) {
- expr_pos++;
- if (expr_pos == expression.size()) {
- //can happen..
- _set_error("Unexpected end of expression...");
- return nullptr;
- }
- }
-
- //consecutively do unary operators
- for (int i = expr_pos - 1; i >= next_op; i--) {
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = expression[i].op;
- op->nodes[0] = expression[i + 1].node;
- op->nodes[1] = nullptr;
- expression.write[i].is_op = false;
- expression.write[i].node = op;
- expression.remove_at(i + 1);
- }
-
- } else {
- if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = expression[next_op].op;
-
- if (expression[next_op - 1].is_op) {
- _set_error("Parser bug...");
- ERR_FAIL_V(nullptr);
- }
-
- if (expression[next_op + 1].is_op) {
- // this is not invalid and can really appear
- // but it becomes invalid anyway because no binary op
- // can be followed by a unary op in a valid combination,
- // due to how precedence works, unaries will always disappear first
-
- _set_error("Unexpected two consecutive operators.");
- return nullptr;
- }
-
- op->nodes[0] = expression[next_op - 1].node; //expression goes as left
- op->nodes[1] = expression[next_op + 1].node; //next expression goes as right
-
- //replace all 3 nodes by this operator and make it an expression
- expression.write[next_op - 1].node = op;
- expression.remove_at(next_op);
- expression.remove_at(next_op);
- }
- }
-
- return expression[0].node;
-}
-
-bool VisualScriptExpression::_compile_expression() {
- if (!expression_dirty) {
- return error_set;
- }
-
- if (nodes) {
- memdelete(nodes);
- nodes = nullptr;
- root = nullptr;
- }
-
- error_str = String();
- error_set = false;
- str_ofs = 0;
-
- root = _parse_expression();
-
- if (error_set) {
- root = nullptr;
- if (nodes) {
- memdelete(nodes);
- }
- nodes = nullptr;
- return true;
- }
-
- expression_dirty = false;
- return false;
-}
-
-class VisualScriptNodeInstanceExpression : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- VisualScriptExpression *expression = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
- //execute by parsing the tree directly
- virtual bool _execute(const Variant **p_inputs, VisualScriptExpression::ENode *p_node, Variant &r_ret, String &r_error_str, Callable::CallError &ce) {
- switch (p_node->type) {
- case VisualScriptExpression::ENode::TYPE_INPUT: {
- const VisualScriptExpression::InputNode *in = static_cast<const VisualScriptExpression::InputNode *>(p_node);
- r_ret = *p_inputs[in->index];
- } break;
- case VisualScriptExpression::ENode::TYPE_CONSTANT: {
- const VisualScriptExpression::ConstantNode *c = static_cast<const VisualScriptExpression::ConstantNode *>(p_node);
- r_ret = c->value;
-
- } break;
- case VisualScriptExpression::ENode::TYPE_SELF: {
- r_ret = instance->get_owner_ptr();
- } break;
- case VisualScriptExpression::ENode::TYPE_OPERATOR: {
- const VisualScriptExpression::OperatorNode *op = static_cast<const VisualScriptExpression::OperatorNode *>(p_node);
-
- Variant a;
- bool ret = _execute(p_inputs, op->nodes[0], a, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- Variant b;
-
- if (op->nodes[1]) {
- ret = _execute(p_inputs, op->nodes[1], b, r_error_str, ce);
- if (ret) {
- return true;
- }
- }
-
- bool valid = true;
- Variant::evaluate(op->op, a, b, r_ret, valid);
- if (!valid) {
- r_error_str = "Invalid operands to operator " + Variant::get_operator_name(op->op) + ": " + Variant::get_type_name(a.get_type()) + " and " + Variant::get_type_name(b.get_type()) + ".";
- return true;
- }
-
- } break;
- case VisualScriptExpression::ENode::TYPE_INDEX: {
- const VisualScriptExpression::IndexNode *index = static_cast<const VisualScriptExpression::IndexNode *>(p_node);
-
- Variant base;
- bool ret = _execute(p_inputs, index->base, base, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- Variant idx;
-
- ret = _execute(p_inputs, index->index, idx, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- bool valid;
- r_ret = base.get(idx, &valid);
- if (!valid) {
- r_error_str = "Invalid index of type " + Variant::get_type_name(idx.get_type()) + " for base of type " + Variant::get_type_name(base.get_type()) + ".";
- return true;
- }
-
- } break;
- case VisualScriptExpression::ENode::TYPE_NAMED_INDEX: {
- const VisualScriptExpression::NamedIndexNode *index = static_cast<const VisualScriptExpression::NamedIndexNode *>(p_node);
-
- Variant base;
- bool ret = _execute(p_inputs, index->base, base, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- bool valid;
- r_ret = base.get_named(index->name, valid);
- if (!valid) {
- r_error_str = "Invalid index '" + String(index->name) + "' for base of type " + Variant::get_type_name(base.get_type()) + ".";
- return true;
- }
-
- } break;
- case VisualScriptExpression::ENode::TYPE_ARRAY: {
- const VisualScriptExpression::ArrayNode *array = static_cast<const VisualScriptExpression::ArrayNode *>(p_node);
-
- Array arr;
- arr.resize(array->array.size());
- for (int i = 0; i < array->array.size(); i++) {
- Variant value;
- bool ret = _execute(p_inputs, array->array[i], value, r_error_str, ce);
- if (ret) {
- return true;
- }
- arr[i] = value;
- }
-
- r_ret = arr;
-
- } break;
- case VisualScriptExpression::ENode::TYPE_DICTIONARY: {
- const VisualScriptExpression::DictionaryNode *dictionary = static_cast<const VisualScriptExpression::DictionaryNode *>(p_node);
-
- Dictionary d;
- for (int i = 0; i < dictionary->dict.size(); i += 2) {
- Variant key;
- bool ret = _execute(p_inputs, dictionary->dict[i + 0], key, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- Variant value;
- ret = _execute(p_inputs, dictionary->dict[i + 1], value, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- d[key] = value;
- }
-
- r_ret = d;
- } break;
- case VisualScriptExpression::ENode::TYPE_CONSTRUCTOR: {
- const VisualScriptExpression::ConstructorNode *constructor = static_cast<const VisualScriptExpression::ConstructorNode *>(p_node);
-
- Vector<Variant> arr;
- Vector<const Variant *> argp;
- arr.resize(constructor->arguments.size());
- argp.resize(constructor->arguments.size());
-
- for (int i = 0; i < constructor->arguments.size(); i++) {
- Variant value;
- bool ret = _execute(p_inputs, constructor->arguments[i], value, r_error_str, ce);
- if (ret) {
- return true;
- }
- arr.write[i] = value;
- argp.write[i] = &arr[i];
- }
-
- Variant::construct(constructor->data_type, r_ret, (const Variant **)argp.ptr(), argp.size(), ce);
-
- if (ce.error != Callable::CallError::CALL_OK) {
- r_error_str = "Invalid arguments to construct '" + Variant::get_type_name(constructor->data_type) + "'.";
- return true;
- }
-
- } break;
- case VisualScriptExpression::ENode::TYPE_BUILTIN_FUNC: {
- const VisualScriptExpression::BuiltinFuncNode *bifunc = static_cast<const VisualScriptExpression::BuiltinFuncNode *>(p_node);
-
- Vector<Variant> arr;
- Vector<const Variant *> argp;
- arr.resize(bifunc->arguments.size());
- argp.resize(bifunc->arguments.size());
-
- for (int i = 0; i < bifunc->arguments.size(); i++) {
- Variant value;
- bool ret = _execute(p_inputs, bifunc->arguments[i], value, r_error_str, ce);
- if (ret) {
- return true;
- }
- arr.write[i] = value;
- argp.write[i] = &arr[i];
- }
-
- VisualScriptBuiltinFunc::exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str);
-
- if (ce.error != Callable::CallError::CALL_OK) {
- r_error_str = "Builtin Call Failed. " + r_error_str;
- return true;
- }
-
- } break;
- case VisualScriptExpression::ENode::TYPE_CALL: {
- const VisualScriptExpression::CallNode *call = static_cast<const VisualScriptExpression::CallNode *>(p_node);
-
- Variant base;
- bool ret = _execute(p_inputs, call->base, base, r_error_str, ce);
- if (ret) {
- return true;
- }
-
- Vector<Variant> arr;
- Vector<const Variant *> argp;
- arr.resize(call->arguments.size());
- argp.resize(call->arguments.size());
-
- for (int i = 0; i < call->arguments.size(); i++) {
- Variant value;
- bool ret2 = _execute(p_inputs, call->arguments[i], value, r_error_str, ce);
- if (ret2) {
- return true;
- }
- arr.write[i] = value;
- argp.write[i] = &arr[i];
- }
-
- base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
-
- if (ce.error != Callable::CallError::CALL_OK) {
- r_error_str = "On call to '" + String(call->method) + "':";
- return true;
- }
-
- } break;
- }
- return 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 (!expression->root || expression->error_set) {
- r_error_str = expression->error_str;
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
-
- bool error = _execute(p_inputs, expression->root, *p_outputs[0], r_error_str, r_error);
- if (error && r_error.error == Callable::CallError::CALL_OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- }
-
-#ifdef DEBUG_ENABLED
- if (!error && expression->output_type != Variant::NIL && !Variant::can_convert_strict(p_outputs[0]->get_type(), expression->output_type)) {
- r_error_str += "Can't convert expression result from " + Variant::get_type_name(p_outputs[0]->get_type()) + " to " + Variant::get_type_name(expression->output_type) + ".";
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- }
-#endif
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptExpression::instantiate(VisualScriptInstance *p_instance) {
- _compile_expression();
- VisualScriptNodeInstanceExpression *instance = memnew(VisualScriptNodeInstanceExpression);
- instance->instance = p_instance;
- instance->expression = this;
- return instance;
-}
-
-void VisualScriptExpression::reset_state() {
- if (nodes) {
- memdelete(nodes);
- nodes = nullptr;
- root = nullptr;
- }
-
- error_str = String();
- error_set = false;
- str_ofs = 0;
- inputs.clear();
-}
-
-VisualScriptExpression::VisualScriptExpression() {
-}
-
-VisualScriptExpression::~VisualScriptExpression() {
- if (nodes) {
- memdelete(nodes);
- }
-}
-
-void register_visual_script_expression_node() {
- VisualScriptLanguage::singleton->add_register_func("operators/expression", create_node_generic<VisualScriptExpression>);
-}
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
deleted file mode 100644
index 7e10f98f36..0000000000
--- a/modules/visual_script/visual_script_expression.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/*************************************************************************/
-/* visual_script_expression.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 VISUAL_SCRIPT_EXPRESSION_H
-#define VISUAL_SCRIPT_EXPRESSION_H
-
-#include "visual_script.h"
-#include "visual_script_builtin_funcs.h"
-
-class VisualScriptExpression : public VisualScriptNode {
- GDCLASS(VisualScriptExpression, VisualScriptNode);
- friend class VisualScriptNodeInstanceExpression;
-
- struct Input {
- Variant::Type type = Variant::NIL;
- String name;
- };
-
- Vector<Input> inputs;
- Variant::Type output_type = Variant::NIL;
-
- String expression;
-
- bool sequenced = false;
- int str_ofs = 0;
- bool expression_dirty = true;
-
- bool _compile_expression();
-
- enum TokenType {
- TK_CURLY_BRACKET_OPEN,
- TK_CURLY_BRACKET_CLOSE,
- TK_BRACKET_OPEN,
- TK_BRACKET_CLOSE,
- TK_PARENTHESIS_OPEN,
- TK_PARENTHESIS_CLOSE,
- TK_IDENTIFIER,
- TK_BUILTIN_FUNC,
- TK_SELF,
- TK_CONSTANT,
- TK_BASIC_TYPE,
- TK_COLON,
- TK_COMMA,
- TK_PERIOD,
- TK_OP_IN,
- TK_OP_EQUAL,
- TK_OP_NOT_EQUAL,
- TK_OP_LESS,
- TK_OP_LESS_EQUAL,
- TK_OP_GREATER,
- TK_OP_GREATER_EQUAL,
- TK_OP_AND,
- TK_OP_OR,
- TK_OP_NOT,
- TK_OP_ADD,
- TK_OP_SUB,
- TK_OP_MUL,
- TK_OP_DIV,
- TK_OP_MOD,
- TK_OP_SHIFT_LEFT,
- TK_OP_SHIFT_RIGHT,
- TK_OP_BIT_AND,
- TK_OP_BIT_OR,
- TK_OP_BIT_XOR,
- TK_OP_BIT_INVERT,
- TK_EOF,
- TK_ERROR,
- TK_MAX
- };
-
- static const char *token_name[TK_MAX];
- struct Token {
- TokenType type;
- Variant value;
- };
-
- void _set_error(const String &p_err) {
- if (error_set) {
- return;
- }
- error_str = p_err;
- error_set = true;
- }
-
- Error _get_token(Token &r_token);
-
- String error_str;
- bool error_set = true;
-
- struct ENode {
- enum Type {
- TYPE_INPUT,
- TYPE_CONSTANT,
- TYPE_SELF,
- TYPE_OPERATOR,
- TYPE_INDEX,
- TYPE_NAMED_INDEX,
- TYPE_ARRAY,
- TYPE_DICTIONARY,
- TYPE_CONSTRUCTOR,
- TYPE_BUILTIN_FUNC,
- TYPE_CALL
- };
-
- ENode *next = nullptr;
-
- Type type = Type::TYPE_SELF;
-
- virtual ~ENode() {
- if (next) {
- memdelete(next);
- }
- }
- };
-
- struct Expression {
- bool is_op = false;
- union {
- Variant::Operator op;
- ENode *node = nullptr;
- };
- };
-
- ENode *_parse_expression();
-
- struct InputNode : public ENode {
- int index = 0;
- InputNode() {
- type = TYPE_INPUT;
- }
- };
-
- struct ConstantNode : public ENode {
- Variant value;
- ConstantNode() {
- type = TYPE_CONSTANT;
- }
- };
-
- struct OperatorNode : public ENode {
- Variant::Operator op = Variant::Operator::OP_ADD;
-
- ENode *nodes[2] = { nullptr, nullptr };
-
- OperatorNode() {
- type = TYPE_OPERATOR;
- }
- };
-
- struct SelfNode : public ENode {
- SelfNode() {
- type = TYPE_SELF;
- }
- };
-
- struct IndexNode : public ENode {
- ENode *base = nullptr;
- ENode *index = nullptr;
-
- IndexNode() {
- type = TYPE_INDEX;
- }
- };
-
- struct NamedIndexNode : public ENode {
- ENode *base = nullptr;
- StringName name;
-
- NamedIndexNode() {
- type = TYPE_NAMED_INDEX;
- }
- };
-
- struct ConstructorNode : public ENode {
- Variant::Type data_type = Variant::Type::NIL;
- Vector<ENode *> arguments;
-
- ConstructorNode() {
- type = TYPE_CONSTRUCTOR;
- }
- };
-
- struct CallNode : public ENode {
- ENode *base = nullptr;
- StringName method;
- Vector<ENode *> arguments;
-
- CallNode() {
- type = TYPE_CALL;
- }
- };
-
- struct ArrayNode : public ENode {
- Vector<ENode *> array;
- ArrayNode() {
- type = TYPE_ARRAY;
- }
- };
-
- struct DictionaryNode : public ENode {
- Vector<ENode *> dict;
- DictionaryNode() {
- type = TYPE_DICTIONARY;
- }
- };
-
- struct BuiltinFuncNode : public ENode {
- VisualScriptBuiltinFunc::BuiltinFunc func = VisualScriptBuiltinFunc::BuiltinFunc::BYTES_TO_VAR;
- Vector<ENode *> arguments;
- BuiltinFuncNode() {
- type = TYPE_BUILTIN_FUNC;
- }
- };
-
- template <class T>
- T *alloc_node() {
- T *node = memnew(T);
- node->next = nodes;
- nodes = node;
- return node;
- }
-
- ENode *root = nullptr;
- ENode *nodes = nullptr;
-
-protected:
- 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:
- virtual void reset_state() override;
-
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "operators"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptExpression();
- ~VisualScriptExpression();
-};
-
-void register_visual_script_expression_node();
-
-#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
deleted file mode 100644
index 19bbd834cc..0000000000
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ /dev/null
@@ -1,880 +0,0 @@
-/*************************************************************************/
-/* visual_script_flow_control.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 "visual_script_flow_control.h"
-
-#include "core/config/project_settings.h"
-#include "core/io/resource_loader.h"
-#include "core/os/keyboard.h"
-
-//////////////////////////////////////////
-////////////////RETURN////////////////////
-//////////////////////////////////////////
-
-int VisualScriptReturn::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptReturn::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptReturn::get_input_value_port_count() const {
- return with_value ? 1 : 0;
-}
-
-int VisualScriptReturn::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptReturn::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptReturn::get_input_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "result";
- pinfo.type = type;
- return pinfo;
-}
-
-PropertyInfo VisualScriptReturn::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptReturn::get_caption() const {
- return RTR("Return");
-}
-
-String VisualScriptReturn::get_text() const {
- return get_name();
-}
-
-void VisualScriptReturn::set_return_type(Variant::Type p_type) {
- if (type == p_type) {
- return;
- }
- type = p_type;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptReturn::get_return_type() const {
- return type;
-}
-
-void VisualScriptReturn::set_enable_return_value(bool p_enable) {
- if (with_value == p_enable) {
- return;
- }
-
- with_value = p_enable;
- ports_changed_notify();
-}
-
-bool VisualScriptReturn::is_return_value_enabled() const {
- return with_value;
-}
-
-void VisualScriptReturn::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_return_type", "type"), &VisualScriptReturn::set_return_type);
- ClassDB::bind_method(D_METHOD("get_return_type"), &VisualScriptReturn::get_return_type);
- ClassDB::bind_method(D_METHOD("set_enable_return_value", "enable"), &VisualScriptReturn::set_enable_return_value);
- ClassDB::bind_method(D_METHOD("is_return_value_enabled"), &VisualScriptReturn::is_return_value_enabled);
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "return_enabled"), "set_enable_return_value", "is_return_value_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "return_type", PROPERTY_HINT_ENUM, argt), "set_return_type", "get_return_type");
-}
-
-class VisualScriptNodeInstanceReturn : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 (with_value) {
- *p_working_mem = *p_inputs[0];
- return STEP_EXIT_FUNCTION_BIT;
- } else {
- *p_working_mem = Variant();
- return 0;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptReturn::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceReturn *instance = memnew(VisualScriptNodeInstanceReturn);
- instance->node = this;
- instance->instance = p_instance;
- instance->with_value = with_value;
- return instance;
-}
-
-VisualScriptReturn::VisualScriptReturn() {
- with_value = false;
- type = Variant::NIL;
-}
-
-template <bool with_value>
-static Ref<VisualScriptNode> create_return_node(const String &p_name) {
- Ref<VisualScriptReturn> node;
- node.instantiate();
- node->set_enable_return_value(with_value);
- return node;
-}
-
-//////////////////////////////////////////
-////////////////CONDITION/////////////////
-//////////////////////////////////////////
-
-int VisualScriptCondition::get_output_sequence_port_count() const {
- return 3;
-}
-
-bool VisualScriptCondition::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptCondition::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptCondition::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptCondition::get_output_sequence_port_text(int p_port) const {
- if (p_port == 0) {
- return "true";
- } else if (p_port == 1) {
- return "false";
- } else {
- return "done";
- }
-}
-
-PropertyInfo VisualScriptCondition::get_input_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "cond";
- pinfo.type = Variant::BOOL;
- return pinfo;
-}
-
-PropertyInfo VisualScriptCondition::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptCondition::get_caption() const {
- return RTR("Condition");
-}
-
-String VisualScriptCondition::get_text() const {
- return RTR("if (cond) is:");
-}
-
-void VisualScriptCondition::_bind_methods() {
-}
-
-class VisualScriptNodeInstanceCondition : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 (p_start_mode == START_MODE_CONTINUE_SEQUENCE) {
- return 2;
- } else if (p_inputs[0]->operator bool()) {
- return 0 | STEP_FLAG_PUSH_STACK_BIT;
- } else {
- return 1 | STEP_FLAG_PUSH_STACK_BIT;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptCondition::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceCondition *instance = memnew(VisualScriptNodeInstanceCondition);
- instance->node = this;
- instance->instance = p_instance;
- return instance;
-}
-
-VisualScriptCondition::VisualScriptCondition() {
-}
-
-//////////////////////////////////////////
-////////////////WHILE/////////////////
-//////////////////////////////////////////
-
-int VisualScriptWhile::get_output_sequence_port_count() const {
- return 2;
-}
-
-bool VisualScriptWhile::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptWhile::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptWhile::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptWhile::get_output_sequence_port_text(int p_port) const {
- if (p_port == 0) {
- return "repeat";
- } else {
- return "exit";
- }
-}
-
-PropertyInfo VisualScriptWhile::get_input_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "cond";
- pinfo.type = Variant::BOOL;
- return pinfo;
-}
-
-PropertyInfo VisualScriptWhile::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptWhile::get_caption() const {
- return RTR("While");
-}
-
-String VisualScriptWhile::get_text() const {
- return RTR("while (cond):");
-}
-
-void VisualScriptWhile::_bind_methods() {
-}
-
-class VisualScriptNodeInstanceWhile : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 {
- bool keep_going = p_inputs[0]->operator bool();
-
- if (keep_going) {
- return 0 | STEP_FLAG_PUSH_STACK_BIT;
- } else {
- return 1;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptWhile::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceWhile *instance = memnew(VisualScriptNodeInstanceWhile);
- instance->node = this;
- instance->instance = p_instance;
- return instance;
-}
-
-VisualScriptWhile::VisualScriptWhile() {
-}
-
-//////////////////////////////////////////
-////////////////ITERATOR/////////////////
-//////////////////////////////////////////
-
-int VisualScriptIterator::get_output_sequence_port_count() const {
- return 2;
-}
-
-bool VisualScriptIterator::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptIterator::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptIterator::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptIterator::get_output_sequence_port_text(int p_port) const {
- if (p_port == 0) {
- return "each";
- } else {
- return "exit";
- }
-}
-
-PropertyInfo VisualScriptIterator::get_input_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "input";
- pinfo.type = Variant::NIL;
- return pinfo;
-}
-
-PropertyInfo VisualScriptIterator::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "elem";
- pinfo.type = Variant::NIL;
- return pinfo;
-}
-
-String VisualScriptIterator::get_caption() const {
- return RTR("Iterator");
-}
-
-String VisualScriptIterator::get_text() const {
- return RTR("for (elem) in (input):");
-}
-
-void VisualScriptIterator::_bind_methods() {
-}
-
-class VisualScriptNodeInstanceIterator : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 (p_start_mode == START_MODE_BEGIN_SEQUENCE) {
- p_working_mem[0] = *p_inputs[0];
- bool valid;
- bool can_iter = p_inputs[0]->iter_init(p_working_mem[1], valid);
-
- 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());
- return 0;
- }
-
- if (!can_iter) {
- return 1; //nothing to iterate
- }
-
- *p_outputs[0] = p_working_mem[0].iter_get(p_working_mem[1], valid);
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Iterator became invalid");
- return 0;
- }
-
- } else { //continue sequence
-
- bool valid;
- bool can_iter = p_working_mem[0].iter_next(p_working_mem[1], valid);
-
- 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());
- return 0;
- }
-
- if (!can_iter) {
- return 1; //nothing to iterate
- }
-
- *p_outputs[0] = p_working_mem[0].iter_get(p_working_mem[1], valid);
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Iterator became invalid");
- return 0;
- }
- }
-
- return 0 | STEP_FLAG_PUSH_STACK_BIT; //go around
- }
-};
-
-VisualScriptNodeInstance *VisualScriptIterator::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceIterator *instance = memnew(VisualScriptNodeInstanceIterator);
- instance->node = this;
- instance->instance = p_instance;
- return instance;
-}
-
-VisualScriptIterator::VisualScriptIterator() {
-}
-
-//////////////////////////////////////////
-////////////////SEQUENCE/////////////////
-//////////////////////////////////////////
-
-int VisualScriptSequence::get_output_sequence_port_count() const {
- return steps;
-}
-
-bool VisualScriptSequence::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptSequence::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptSequence::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSequence::get_output_sequence_port_text(int p_port) const {
- return itos(p_port + 1);
-}
-
-PropertyInfo VisualScriptSequence::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptSequence::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::INT, "current");
-}
-
-String VisualScriptSequence::get_caption() const {
- return RTR("Sequence");
-}
-
-String VisualScriptSequence::get_text() const {
- return RTR("in order:");
-}
-
-void VisualScriptSequence::set_steps(int p_steps) {
- ERR_FAIL_COND(p_steps < 1);
- if (steps == p_steps) {
- return;
- }
-
- steps = p_steps;
- ports_changed_notify();
-}
-
-int VisualScriptSequence::get_steps() const {
- return steps;
-}
-
-void VisualScriptSequence::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_steps", "steps"), &VisualScriptSequence::set_steps);
- ClassDB::bind_method(D_METHOD("get_steps"), &VisualScriptSequence::get_steps);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "steps", PROPERTY_HINT_RANGE, "1,64,1"), "set_steps", "get_steps");
-}
-
-class VisualScriptNodeInstanceSequence : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 (p_start_mode == START_MODE_BEGIN_SEQUENCE) {
- p_working_mem[0] = 0;
- }
-
- int step = p_working_mem[0];
-
- *p_outputs[0] = step;
-
- if (step + 1 == steps) {
- return step;
- } else {
- p_working_mem[0] = step + 1;
- return step | STEP_FLAG_PUSH_STACK_BIT;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSequence::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSequence *instance = memnew(VisualScriptNodeInstanceSequence);
- instance->node = this;
- instance->instance = p_instance;
- instance->steps = steps;
- return instance;
-}
-
-VisualScriptSequence::VisualScriptSequence() {
- steps = 1;
-}
-
-//////////////////////////////////////////
-////////////////EVENT TYPE FILTER///////////
-//////////////////////////////////////////
-
-int VisualScriptSwitch::get_output_sequence_port_count() const {
- return case_values.size() + 1;
-}
-
-bool VisualScriptSwitch::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptSwitch::get_input_value_port_count() const {
- return case_values.size() + 1;
-}
-
-int VisualScriptSwitch::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptSwitch::get_output_sequence_port_text(int p_port) const {
- if (p_port == case_values.size()) {
- return "done";
- }
-
- return String();
-}
-
-PropertyInfo VisualScriptSwitch::get_input_value_port_info(int p_idx) const {
- if (p_idx < case_values.size()) {
- return PropertyInfo(case_values[p_idx].type, " =");
- } else {
- return PropertyInfo(Variant::NIL, "input");
- }
-}
-
-PropertyInfo VisualScriptSwitch::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptSwitch::get_caption() const {
- return RTR("Switch");
-}
-
-String VisualScriptSwitch::get_text() const {
- return RTR("'input' is:");
-}
-
-class VisualScriptNodeInstanceSwitch : public VisualScriptNodeInstance {
-public:
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return 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 (p_start_mode == START_MODE_CONTINUE_SEQUENCE) {
- return case_count; //exit
- }
-
- for (int i = 0; i < case_count; i++) {
- if (*p_inputs[i] == *p_inputs[case_count]) {
- return i | STEP_FLAG_PUSH_STACK_BIT;
- }
- }
-
- return case_count;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSwitch::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSwitch *instance = memnew(VisualScriptNodeInstanceSwitch);
- instance->instance = p_instance;
- instance->case_count = case_values.size();
- return instance;
-}
-
-bool VisualScriptSwitch::_set(const StringName &p_name, const Variant &p_value) {
- if (String(p_name) == "case_count") {
- case_values.resize(p_value);
- notify_property_list_changed();
- ports_changed_notify();
- return true;
- }
-
- if (String(p_name).begins_with("case/")) {
- int idx = String(p_name).get_slice("/", 1).to_int();
- ERR_FAIL_INDEX_V(idx, case_values.size(), false);
-
- case_values.write[idx].type = Variant::Type(int(p_value));
- notify_property_list_changed();
- ports_changed_notify();
-
- return true;
- }
-
- return false;
-}
-
-bool VisualScriptSwitch::_get(const StringName &p_name, Variant &r_ret) const {
- if (String(p_name) == "case_count") {
- r_ret = case_values.size();
- return true;
- }
-
- if (String(p_name).begins_with("case/")) {
- int idx = String(p_name).get_slice("/", 1).to_int();
- ERR_FAIL_INDEX_V(idx, case_values.size(), false);
-
- r_ret = case_values[idx].type;
- return true;
- }
-
- return false;
-}
-
-void VisualScriptSwitch::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "case_count", PROPERTY_HINT_RANGE, "0,128"));
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < case_values.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "case/" + itos(i), PROPERTY_HINT_ENUM, argt));
- }
-}
-
-void VisualScriptSwitch::reset_state() {
- case_values.clear();
-}
-
-void VisualScriptSwitch::_bind_methods() {
-}
-
-VisualScriptSwitch::VisualScriptSwitch() {
-}
-
-//////////////////////////////////////////
-////////////////TYPE CAST///////////
-//////////////////////////////////////////
-
-int VisualScriptTypeCast::get_output_sequence_port_count() const {
- return 2;
-}
-
-bool VisualScriptTypeCast::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptTypeCast::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptTypeCast::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptTypeCast::get_output_sequence_port_text(int p_port) const {
- return p_port == 0 ? "yes" : "no";
-}
-
-PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "instance");
-}
-
-PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_TYPE_STRING, get_base_type());
-}
-
-String VisualScriptTypeCast::get_caption() const {
- return RTR("Type Cast");
-}
-
-String VisualScriptTypeCast::get_text() const {
- if (!script.is_empty()) {
- return vformat(RTR("Is %s?"), script.get_file());
- } else {
- return vformat(RTR("Is %s?"), base_type);
- }
-}
-
-void VisualScriptTypeCast::set_base_type(const StringName &p_type) {
- if (base_type == p_type) {
- return;
- }
-
- base_type = p_type;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptTypeCast::get_base_type() const {
- return base_type;
-}
-
-void VisualScriptTypeCast::set_base_script(const String &p_path) {
- if (script == p_path) {
- return;
- }
-
- script = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptTypeCast::get_base_script() const {
- return script;
-}
-
-VisualScriptTypeCast::TypeGuess VisualScriptTypeCast::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- TypeGuess tg;
- tg.type = Variant::OBJECT;
- if (!script.is_empty()) {
- tg.script = ResourceLoader::load(script);
- }
- //if (!tg.script.is_valid()) {
- // tg.gdclass = base_type;
- //}
-
- return tg;
-}
-
-class VisualScriptNodeInstanceTypeCast : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- StringName base_type;
- String script;
-
- //virtual int get_working_memory_size() const override { return 0; }
- //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return 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 {
- Object *obj = *p_inputs[0];
-
- *p_outputs[0] = Variant();
-
- if (!obj) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Instance is null";
- return 0;
- }
-
- if (!script.is_empty()) {
- Ref<Script> obj_script = obj->get_script();
- if (!obj_script.is_valid()) {
- return 1; //well, definitely not the script because object we got has no script.
- }
-
- if (!ResourceCache::has(script)) {
- //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 = 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;
- return 1;
- }
-
- while (obj_script.is_valid()) {
- if (cast_script == obj_script) {
- *p_outputs[0] = *p_inputs[0]; //copy
- return 0; // it is the script, yey
- }
-
- obj_script = obj_script->get_base_script();
- }
-
- return 1; //not found sorry
- }
-
- if (ClassDB::is_parent_class(obj->get_class_name(), base_type)) {
- *p_outputs[0] = *p_inputs[0]; //copy
- return 0;
- } else {
- return 1;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptTypeCast::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceTypeCast *instance = memnew(VisualScriptNodeInstanceTypeCast);
- instance->instance = p_instance;
- instance->base_type = base_type;
- instance->script = script;
- return instance;
-}
-
-void VisualScriptTypeCast::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_base_type", "type"), &VisualScriptTypeCast::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptTypeCast::get_base_type);
-
- ClassDB::bind_method(D_METHOD("set_base_script", "path"), &VisualScriptTypeCast::set_base_script);
- ClassDB::bind_method(D_METHOD("get_base_script"), &VisualScriptTypeCast::get_base_script);
-
- List<String> script_extensions;
- for (int i = 0; i > ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
- }
-
- String script_ext_hint;
- for (const String &E : script_extensions) {
- if (!script_ext_hint.is_empty()) {
- script_ext_hint += ",";
- }
- script_ext_hint += "*." + E;
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
-}
-
-VisualScriptTypeCast::VisualScriptTypeCast() {
- base_type = "Object";
-}
-
-void register_visual_script_flow_control_nodes() {
- VisualScriptLanguage::singleton->add_register_func("flow_control/return", create_return_node<false>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/return_with_value", create_return_node<true>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/condition", create_node_generic<VisualScriptCondition>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/while", create_node_generic<VisualScriptWhile>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/iterator", create_node_generic<VisualScriptIterator>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/sequence", create_node_generic<VisualScriptSequence>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/switch", create_node_generic<VisualScriptSwitch>);
- //VisualScriptLanguage::singleton->add_register_func("flow_control/input", create_node_generic<VisualScriptInputFilter>);
- VisualScriptLanguage::singleton->add_register_func("flow_control/type_cast", create_node_generic<VisualScriptTypeCast>);
-}
diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h
deleted file mode 100644
index 7ffdf3df65..0000000000
--- a/modules/visual_script/visual_script_flow_control.h
+++ /dev/null
@@ -1,268 +0,0 @@
-/*************************************************************************/
-/* visual_script_flow_control.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 VISUAL_SCRIPT_FLOW_CONTROL_H
-#define VISUAL_SCRIPT_FLOW_CONTROL_H
-
-#include "visual_script.h"
-
-class VisualScriptReturn : public VisualScriptNode {
- GDCLASS(VisualScriptReturn, VisualScriptNode);
-
- Variant::Type type;
- bool with_value;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- void set_return_type(Variant::Type);
- Variant::Type get_return_type() const;
-
- void set_enable_return_value(bool p_enable);
- bool is_return_value_enabled() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptReturn();
-};
-
-class VisualScriptCondition : public VisualScriptNode {
- GDCLASS(VisualScriptCondition, VisualScriptNode);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptCondition();
-};
-
-class VisualScriptWhile : public VisualScriptNode {
- GDCLASS(VisualScriptWhile, VisualScriptNode);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptWhile();
-};
-
-class VisualScriptIterator : public VisualScriptNode {
- GDCLASS(VisualScriptIterator, VisualScriptNode);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptIterator();
-};
-
-class VisualScriptSequence : public VisualScriptNode {
- GDCLASS(VisualScriptSequence, VisualScriptNode);
-
- int steps;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- void set_steps(int p_steps);
- int get_steps() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptSequence();
-};
-
-class VisualScriptSwitch : public VisualScriptNode {
- GDCLASS(VisualScriptSwitch, VisualScriptNode);
-
- struct Case {
- Variant::Type type;
- Case() { type = Variant::NIL; }
- };
-
- Vector<Case> case_values;
-
- friend class VisualScriptNodeInstanceSwitch;
-
-protected:
- 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;
-
- static void _bind_methods();
-
-public:
- virtual void reset_state() override;
-
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
- virtual bool has_mixed_input_and_sequence_ports() const override { return true; }
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptSwitch();
-};
-
-class VisualScriptTypeCast : public VisualScriptNode {
- GDCLASS(VisualScriptTypeCast, VisualScriptNode);
-
- StringName base_type;
- String script;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- void set_base_type(const StringName &p_type);
- StringName get_base_type() const;
-
- void set_base_script(const String &p_path);
- String get_base_script() const;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptTypeCast();
-};
-
-void register_visual_script_flow_control_nodes();
-
-#endif // VISUAL_SCRIPT_FLOW_CONTROL_H
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
deleted file mode 100644
index b16358ae38..0000000000
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ /dev/null
@@ -1,2444 +0,0 @@
-/*************************************************************************/
-/* visual_script_func_nodes.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 "visual_script_func_nodes.h"
-
-#include "core/config/engine.h"
-#include "core/io/resource_loader.h"
-#include "core/os/os.h"
-#include "core/templates/local_vector.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-#include "visual_script_nodes.h"
-
-//////////////////////////////////////////
-////////////////CALL//////////////////////
-//////////////////////////////////////////
-
-int VisualScriptFunctionCall::get_output_sequence_port_count() const {
- if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function))) {
- return 0;
- } else {
- return 1;
- }
-}
-
-bool VisualScriptFunctionCall::has_input_sequence_port() const {
- return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function)));
-}
-#ifdef TOOLS_ENABLED
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
- if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
- return nullptr;
- }
-
- Ref<Script> scr = p_current_node->get_script();
-
- if (scr.is_valid() && scr == script) {
- return p_current_node;
- }
-
- for (int i = 0; i < p_current_node->get_child_count(); i++) {
- Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
- if (n) {
- return n;
- }
- }
-
- return nullptr;
-}
-
-#endif
-Node *VisualScriptFunctionCall::_get_base_node() const {
-#ifdef TOOLS_ENABLED
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return nullptr;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return nullptr;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return nullptr;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return nullptr;
- }
-
- if (!script_node->has_node(base_path)) {
- return nullptr;
- }
-
- Node *path_to = script_node->get_node(base_path);
-
- return path_to;
-#else
-
- return nullptr;
-#endif
-}
-
-StringName VisualScriptFunctionCall::_get_base_type() const {
- if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- return get_visual_script()->get_instance_base_type();
- } else if (call_mode == CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
- Node *path = _get_base_node();
- if (path) {
- return path->get_class();
- }
- }
-
- return base_type;
-}
-
-int VisualScriptFunctionCall::get_input_value_port_count() const {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- Vector<Variant::Type> types;
- int argc = Variant::get_builtin_method_argument_count(basic_type, function);
- for (int i = 0; i < argc; i++) {
- types.push_back(Variant::get_builtin_method_argument_type(basic_type, function, i));
- }
- return types.size() + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) + 1;
-
- } else {
- MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
- if (mb) {
- int defaulted_args = mb->get_argument_count() < use_default_args ? mb->get_argument_count() : use_default_args;
- return mb->get_argument_count() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args;
- }
-
- int defaulted_args = method_cache.arguments.size() < use_default_args ? method_cache.arguments.size() : use_default_args;
- return method_cache.arguments.size() + (call_mode == CALL_MODE_INSTANCE ? 1 : 0) + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) - defaulted_args;
- }
-}
-
-int VisualScriptFunctionCall::get_output_value_port_count() const {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- bool returns = Variant::has_builtin_method_return_value(basic_type, function);
- return returns ? 1 : 0;
-
- } else {
- int ret;
- MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
- if (mb) {
- ret = mb->has_return() ? 1 : 0;
- } else {
- ret = 1; //it is assumed that script always returns something
- }
-
- if (call_mode == CALL_MODE_INSTANCE) {
- ret++;
- }
-
- return ret;
- }
-}
-
-String VisualScriptFunctionCall::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptFunctionCall::get_input_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_INSTANCE || call_mode == CALL_MODE_BASIC_TYPE) {
- if (p_idx == 0) {
- PropertyInfo pi;
- pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type);
- pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower());
- return pi;
- } else {
- p_idx--;
- }
- }
-
- if (rpc_call_mode >= RPC_RELIABLE_TO_ID) {
- if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "peer_id");
- } else {
- p_idx--;
- }
- }
-
-#ifdef DEBUG_METHODS_ENABLED
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(Variant::get_builtin_method_argument_type(basic_type, function, p_idx), Variant::get_builtin_method_argument_name(basic_type, function, p_idx));
- } else {
- MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
- if (mb) {
- return mb->get_argument_info(p_idx);
- }
-
- if (p_idx >= 0 && p_idx < method_cache.arguments.size()) {
- return method_cache.arguments[p_idx];
- }
-
- return PropertyInfo();
- }
-#else
- return PropertyInfo();
-#endif
-}
-
-PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) const {
-#ifdef DEBUG_METHODS_ENABLED
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(Variant::get_builtin_method_return_type(basic_type, function), "");
- } else {
- if (call_mode == CALL_MODE_INSTANCE) {
- if (p_idx == 0) {
- return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
- } else {
- p_idx--;
- }
- }
-
- PropertyInfo ret;
-
- /*MethodBind *mb = ClassDB::get_method(_get_base_type(),function);
- if (mb) {
- ret = mb->get_argument_info(-1);
- } else {*/
-
- ret = method_cache.return_val;
-
- //}
-
- if (call_mode == CALL_MODE_INSTANCE) {
- ret.name = "return";
- } else {
- ret.name = "";
- }
- return ret;
- }
-#else
- return PropertyInfo();
-#endif
-}
-
-String VisualScriptFunctionCall::get_caption() const {
- return " " + String(function) + "()";
-}
-
-String VisualScriptFunctionCall::get_text() const {
- String text;
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- text = vformat(RTR("On %s"), Variant::get_type_name(basic_type));
- } else if (call_mode == CALL_MODE_INSTANCE) {
- text = vformat(RTR("On %s"), base_type);
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- text = "[" + String(base_path.simplified()) + "]";
- } else if (call_mode == CALL_MODE_SELF) {
- text = RTR("On Self");
- } else if (call_mode == CALL_MODE_SINGLETON) {
- text = String(singleton) + ":" + String(function) + "()";
- }
-
- if (rpc_call_mode) {
- text += " RPC";
- if (rpc_call_mode == RPC_UNRELIABLE || rpc_call_mode == RPC_UNRELIABLE_TO_ID) {
- text += " UNREL";
- }
- }
-
- return text;
-}
-
-void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
- if (basic_type == p_type) {
- return;
- }
- basic_type = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptFunctionCall::get_basic_type() const {
- return basic_type;
-}
-
-void VisualScriptFunctionCall::set_base_type(const StringName &p_type) {
- if (base_type == p_type) {
- return;
- }
-
- base_type = p_type;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptFunctionCall::get_base_type() const {
- return base_type;
-}
-
-void VisualScriptFunctionCall::set_base_script(const String &p_path) {
- if (base_script == p_path) {
- return;
- }
-
- base_script = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptFunctionCall::get_base_script() const {
- return base_script;
-}
-
-void VisualScriptFunctionCall::set_singleton(const StringName &p_type) {
- if (singleton == p_type) {
- return;
- }
-
- singleton = p_type;
- Object *obj = Engine::get_singleton()->get_singleton_object(singleton);
- if (obj) {
- base_type = obj->get_class();
- }
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptFunctionCall::get_singleton() const {
- return singleton;
-}
-
-void VisualScriptFunctionCall::_update_method_cache() {
- StringName type;
- Ref<Script> script;
-
- if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- type = node->get_class();
- base_type = type; //cache, too
- script = node->get_script();
- }
- } else if (call_mode == CALL_MODE_SELF) {
- if (get_visual_script().is_valid()) {
- type = get_visual_script()->get_instance_base_type();
- base_type = type; //cache, too
- script = get_visual_script();
- }
-
- } else if (call_mode == CALL_MODE_SINGLETON) {
- Object *obj = Engine::get_singleton()->get_singleton_object(singleton);
- if (obj) {
- type = obj->get_class();
- script = obj->get_script();
- }
-
- } else if (call_mode == CALL_MODE_INSTANCE) {
- type = base_type;
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(base_script)) {
- script = ResourceCache::get_ref(base_script);
- } else {
- return;
- }
- }
- }
-
- MethodBind *mb = ClassDB::get_method(type, function);
- if (mb) {
- use_default_args = mb->get_default_argument_count();
- method_cache = MethodInfo();
- for (int i = 0; i < mb->get_argument_count(); i++) {
-#ifdef DEBUG_METHODS_ENABLED
- method_cache.arguments.push_back(mb->get_argument_info(i));
-#else
- method_cache.arguments.push_back(PropertyInfo());
-#endif
- }
-
- if (mb->is_const()) {
- method_cache.flags |= METHOD_FLAG_CONST;
- }
-
-#ifdef DEBUG_METHODS_ENABLED
-
- method_cache.return_val = mb->get_return_info();
-#endif
-
- if (mb->is_vararg()) {
- //for vararg just give it 10 arguments (should be enough for most use cases)
- for (int i = 0; i < 10; i++) {
- method_cache.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
- use_default_args++;
- }
- }
- } else if (script.is_valid() && script->has_method(function)) {
- method_cache = script->get_method_info(function);
- use_default_args = method_cache.default_arguments.size();
- }
-}
-
-void VisualScriptFunctionCall::set_function(const StringName &p_type) {
- if (function == p_type) {
- return;
- }
-
- function = p_type;
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- use_default_args = Variant::get_builtin_method_default_arguments(basic_type, function).size();
- } else {
- //update all caches
-
- _update_method_cache();
- }
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptFunctionCall::get_function() const {
- return function;
-}
-
-void VisualScriptFunctionCall::set_base_path(const NodePath &p_type) {
- if (base_path == p_type) {
- return;
- }
-
- base_path = p_type;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-NodePath VisualScriptFunctionCall::get_base_path() const {
- return base_path;
-}
-
-void VisualScriptFunctionCall::set_call_mode(CallMode p_mode) {
- if (call_mode == p_mode) {
- return;
- }
-
- call_mode = p_mode;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptFunctionCall::CallMode VisualScriptFunctionCall::get_call_mode() const {
- return call_mode;
-}
-
-void VisualScriptFunctionCall::set_use_default_args(int p_amount) {
- if (use_default_args == p_amount) {
- return;
- }
-
- use_default_args = p_amount;
- ports_changed_notify();
-}
-
-void VisualScriptFunctionCall::set_rpc_call_mode(VisualScriptFunctionCall::RPCCallMode p_mode) {
- if (rpc_call_mode == p_mode) {
- return;
- }
- rpc_call_mode = p_mode;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-VisualScriptFunctionCall::RPCCallMode VisualScriptFunctionCall::get_rpc_call_mode() const {
- return rpc_call_mode;
-}
-
-int VisualScriptFunctionCall::get_use_default_args() const {
- return use_default_args;
-}
-
-void VisualScriptFunctionCall::set_validate(bool p_amount) {
- validate = p_amount;
-}
-
-bool VisualScriptFunctionCall::get_validate() const {
- return validate;
-}
-
-void VisualScriptFunctionCall::_set_argument_cache(const Dictionary &p_cache) {
- //so everything works in case all else fails
- method_cache = MethodInfo::from_dict(p_cache);
-}
-
-Dictionary VisualScriptFunctionCall::_get_argument_cache() const {
- return method_cache;
-}
-
-void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const {
- if (property.name == "base_type") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
- }
- }
-
- if (property.name == "base_script") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "basic_type") {
- if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "singleton") {
- if (call_mode != CALL_MODE_SINGLETON) {
- property.usage = PROPERTY_USAGE_NONE;
- } else {
- List<Engine::Singleton> names;
- Engine::get_singleton()->get_singletons(&names);
- property.hint = PROPERTY_HINT_ENUM;
- String sl;
- for (const Engine::Singleton &E : names) {
- if (!sl.is_empty()) {
- sl += ",";
- }
- sl += E.name;
- }
- property.hint_string = sl;
- }
- }
-
- if (property.name == "node_path") {
- if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = PROPERTY_USAGE_NONE;
- } else {
- Node *bnode = _get_base_node();
- if (bnode) {
- property.hint_string = bnode->get_path(); //convert to long string
- }
- }
- }
-
- if (property.name == "function") {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- property.hint = PROPERTY_HINT_METHOD_OF_VARIANT_TYPE;
- property.hint_string = Variant::get_type_name(basic_type);
-
- } else if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- property.hint = PROPERTY_HINT_METHOD_OF_SCRIPT;
- property.hint_string = itos(get_visual_script()->get_instance_id());
- } else if (call_mode == CALL_MODE_SINGLETON) {
- Object *obj = Engine::get_singleton()->get_singleton_object(singleton);
- if (obj) {
- property.hint = PROPERTY_HINT_METHOD_OF_INSTANCE;
- property.hint_string = itos(obj->get_instance_id());
- } else {
- property.hint = PROPERTY_HINT_METHOD_OF_BASE_TYPE;
- property.hint_string = base_type; //should be cached
- }
- } else if (call_mode == CALL_MODE_INSTANCE) {
- property.hint = PROPERTY_HINT_METHOD_OF_BASE_TYPE;
- property.hint_string = base_type;
-
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(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());
- }
- }
- }
-
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- property.hint = PROPERTY_HINT_METHOD_OF_INSTANCE;
- property.hint_string = itos(node->get_instance_id());
- } else {
- property.hint = PROPERTY_HINT_METHOD_OF_BASE_TYPE;
- property.hint_string = get_base_type();
- }
- }
- }
-
- if (property.name == "use_default_args") {
- property.hint = PROPERTY_HINT_RANGE;
-
- int mc = 0;
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- mc = Variant::get_builtin_method_default_arguments(basic_type, function).size();
- } else {
- MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
- if (mb) {
- mc = mb->get_default_argument_count();
- }
- }
-
- if (mc == 0) {
- property.usage = PROPERTY_USAGE_NONE; //do not show
- } else {
- property.hint_string = "0," + itos(mc) + ",1";
- }
- }
-
- if (property.name == "rpc_call_mode") {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-}
-
-void VisualScriptFunctionCall::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_base_type", "base_type"), &VisualScriptFunctionCall::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptFunctionCall::get_base_type);
-
- ClassDB::bind_method(D_METHOD("set_base_script", "base_script"), &VisualScriptFunctionCall::set_base_script);
- ClassDB::bind_method(D_METHOD("get_base_script"), &VisualScriptFunctionCall::get_base_script);
-
- ClassDB::bind_method(D_METHOD("set_basic_type", "basic_type"), &VisualScriptFunctionCall::set_basic_type);
- ClassDB::bind_method(D_METHOD("get_basic_type"), &VisualScriptFunctionCall::get_basic_type);
-
- ClassDB::bind_method(D_METHOD("set_singleton", "singleton"), &VisualScriptFunctionCall::set_singleton);
- ClassDB::bind_method(D_METHOD("get_singleton"), &VisualScriptFunctionCall::get_singleton);
-
- ClassDB::bind_method(D_METHOD("set_function", "function"), &VisualScriptFunctionCall::set_function);
- ClassDB::bind_method(D_METHOD("get_function"), &VisualScriptFunctionCall::get_function);
-
- ClassDB::bind_method(D_METHOD("set_call_mode", "mode"), &VisualScriptFunctionCall::set_call_mode);
- ClassDB::bind_method(D_METHOD("get_call_mode"), &VisualScriptFunctionCall::get_call_mode);
-
- ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptFunctionCall::set_base_path);
- ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptFunctionCall::get_base_path);
-
- ClassDB::bind_method(D_METHOD("set_use_default_args", "amount"), &VisualScriptFunctionCall::set_use_default_args);
- ClassDB::bind_method(D_METHOD("get_use_default_args"), &VisualScriptFunctionCall::get_use_default_args);
-
- ClassDB::bind_method(D_METHOD("_set_argument_cache", "argument_cache"), &VisualScriptFunctionCall::_set_argument_cache);
- ClassDB::bind_method(D_METHOD("_get_argument_cache"), &VisualScriptFunctionCall::_get_argument_cache);
-
- ClassDB::bind_method(D_METHOD("set_rpc_call_mode", "mode"), &VisualScriptFunctionCall::set_rpc_call_mode);
- ClassDB::bind_method(D_METHOD("get_rpc_call_mode"), &VisualScriptFunctionCall::get_rpc_call_mode);
-
- ClassDB::bind_method(D_METHOD("set_validate", "enable"), &VisualScriptFunctionCall::set_validate);
- ClassDB::bind_method(D_METHOD("get_validate"), &VisualScriptFunctionCall::get_validate);
-
- String bt;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (i > 0) {
- bt += ",";
- }
-
- bt += Variant::get_type_name(Variant::Type(i));
- }
-
- List<String> script_extensions;
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
- }
-
- String script_ext_hint;
- for (const String &E : script_extensions) {
- if (!script_ext_hint.is_empty()) {
- script_ext_hint += ",";
- }
- script_ext_hint += "*." + E;
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type,Singleton"), "set_call_mode", "get_call_mode");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "singleton"), "set_singleton", "get_singleton");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_argument_cache", "_get_argument_cache");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "function"), "set_function", "get_function"); //when set, if loaded properly, will override argument count.
- ADD_PROPERTY(PropertyInfo(Variant::INT, "use_default_args"), "set_use_default_args", "get_use_default_args");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "validate"), "set_validate", "get_validate");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "rpc_call_mode", PROPERTY_HINT_ENUM, "Disabled,Reliable,Unreliable,ReliableToID,UnreliableToID"), "set_rpc_call_mode", "get_rpc_call_mode"); //when set, if loaded properly, will override argument count.
-
- BIND_ENUM_CONSTANT(CALL_MODE_SELF);
- BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
- BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
- BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
- BIND_ENUM_CONSTANT(CALL_MODE_SINGLETON);
-
- BIND_ENUM_CONSTANT(RPC_DISABLED);
- BIND_ENUM_CONSTANT(RPC_RELIABLE);
- BIND_ENUM_CONSTANT(RPC_UNRELIABLE);
- BIND_ENUM_CONSTANT(RPC_RELIABLE_TO_ID);
- BIND_ENUM_CONSTANT(RPC_UNRELIABLE_TO_ID);
-}
-
-class VisualScriptNodeInstanceFunctionCall : public VisualScriptNodeInstance {
-public:
- VisualScriptFunctionCall::CallMode call_mode;
- NodePath node_path;
- int input_args = 0;
- bool validate = false;
- int returns = 0;
- VisualScriptFunctionCall::RPCCallMode rpc_mode;
- StringName function;
- StringName singleton;
-
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- _FORCE_INLINE_ bool call_rpc(Object *p_base, const Variant **p_args, int p_argcount) {
- if (!p_base) {
- return false;
- }
-
- Node *node = Object::cast_to<Node>(p_base);
- if (!node) {
- return false;
- }
-
- int to_id = 0;
- //bool reliable = true;
-
- if (rpc_mode >= VisualScriptFunctionCall::RPC_RELIABLE_TO_ID) {
- to_id = *p_args[0];
- p_args += 1;
- p_argcount -= 1;
- //if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE_TO_ID) {
- //reliable = false;
- //}
- }
- //else if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE) {
- //reliable = false;
- //}
-
- // TODO reliable?
- node->rpcp(to_id, function, p_args, p_argcount);
-
- return true;
- }
-
- 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) {
- case VisualScriptFunctionCall::CALL_MODE_SELF: {
- Object *object = instance->get_owner_ptr();
-
- if (rpc_mode) {
- call_rpc(object, p_inputs, input_args);
- } else if (returns) {
- *p_outputs[0] = object->callp(function, p_inputs, input_args, r_error);
- } else {
- object->callp(function, p_inputs, input_args, r_error);
- }
- } break;
- case VisualScriptFunctionCall::CALL_MODE_NODE_PATH: {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Base object is not a Node!";
- return 0;
- }
-
- Node *another = node->get_node(node_path);
- if (!another) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Path does not lead Node!";
- return 0;
- }
-
- if (rpc_mode) {
- call_rpc(node, p_inputs, input_args);
- } else if (returns) {
- *p_outputs[0] = another->callp(function, p_inputs, input_args, r_error);
- } else {
- another->callp(function, p_inputs, input_args, r_error);
- }
-
- } break;
- case VisualScriptFunctionCall::CALL_MODE_INSTANCE:
- case VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE: {
- Variant v = *p_inputs[0];
-
- if (rpc_mode) {
- Object *obj = v;
- if (obj) {
- call_rpc(obj, p_inputs + 1, input_args - 1);
- }
- } else if (returns) {
- if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
- if (returns >= 2) {
- v.callp(function, p_inputs + 1, input_args, *p_outputs[1], r_error);
- } else if (returns == 1) {
- Variant ret;
- v.callp(function, p_inputs + 1, input_args, ret, r_error);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid returns count for call_mode == CALL_MODE_INSTANCE";
- return 0;
- }
- } else {
- v.callp(function, p_inputs + 1, input_args, *p_outputs[0], r_error);
- }
- } else {
- Variant ret;
- v.callp(function, p_inputs + 1, input_args, ret, r_error);
- }
-
- if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
- *p_outputs[0] = *p_inputs[0];
- }
-
- } break;
- case VisualScriptFunctionCall::CALL_MODE_SINGLETON: {
- Object *object = Engine::get_singleton()->get_singleton_object(singleton);
- if (!object) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid singleton name: '" + String(singleton) + "'";
- return 0;
- }
-
- if (rpc_mode) {
- call_rpc(object, p_inputs, input_args);
- } else if (returns) {
- *p_outputs[0] = object->callp(function, p_inputs, input_args, r_error);
- } else {
- object->callp(function, p_inputs, input_args, r_error);
- }
- } break;
- }
-
- if (!validate) {
- //ignore call errors if validation is disabled
- r_error.error = Callable::CallError::CALL_OK;
- r_error_str = String();
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptFunctionCall::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceFunctionCall *instance = memnew(VisualScriptNodeInstanceFunctionCall);
- instance->node = this;
- instance->instance = p_instance;
- instance->singleton = singleton;
- instance->function = function;
- instance->call_mode = call_mode;
- instance->returns = get_output_value_port_count();
- instance->node_path = base_path;
- instance->input_args = get_input_value_port_count() - ((call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 1 : 0);
- instance->rpc_mode = rpc_call_mode;
- instance->validate = validate;
- return instance;
-}
-
-VisualScriptFunctionCall::TypeGuess VisualScriptFunctionCall::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- if (p_output == 0 && call_mode == CALL_MODE_INSTANCE) {
- return p_inputs[0];
- }
-
- return VisualScriptNode::guess_output_type(p_inputs, p_output);
-}
-
-VisualScriptFunctionCall::VisualScriptFunctionCall() {
- validate = true;
- call_mode = CALL_MODE_SELF;
- basic_type = Variant::NIL;
- use_default_args = 0;
- base_type = "Object";
- rpc_call_mode = RPC_DISABLED;
-}
-
-template <VisualScriptFunctionCall::CallMode cmode>
-static Ref<VisualScriptNode> create_function_call_node(const String &p_name) {
- Ref<VisualScriptFunctionCall> node;
- node.instantiate();
- node->set_call_mode(cmode);
- return node;
-}
-
-//////////////////////////////////////////
-////////////////SET//////////////////////
-//////////////////////////////////////////
-
-int VisualScriptPropertySet::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptPropertySet::has_input_sequence_port() const {
- return true;
-}
-
-Node *VisualScriptPropertySet::_get_base_node() const {
-#ifdef TOOLS_ENABLED
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return nullptr;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
-
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return nullptr;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return nullptr;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return nullptr;
- }
-
- if (!script_node->has_node(base_path)) {
- return nullptr;
- }
-
- Node *path_to = script_node->get_node(base_path);
-
- return path_to;
-#else
-
- return nullptr;
-#endif
-}
-
-StringName VisualScriptPropertySet::_get_base_type() const {
- if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- return get_visual_script()->get_instance_base_type();
- } else if (call_mode == CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
- Node *path = _get_base_node();
- if (path) {
- return path->get_class();
- }
- }
-
- return base_type;
-}
-
-int VisualScriptPropertySet::get_input_value_port_count() const {
- int pc = (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 2 : 1;
-
- return pc;
-}
-
-int VisualScriptPropertySet::get_output_value_port_count() const {
- return (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 1 : 0;
-}
-
-String VisualScriptPropertySet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-void VisualScriptPropertySet::_adjust_input_index(PropertyInfo &pinfo) const {
- if (index != StringName()) {
- Variant v;
- Callable::CallError ce;
- Variant::construct(pinfo.type, v, nullptr, 0, ce);
- Variant i = v.get(index);
- pinfo.type = i.get_type();
- }
-}
-
-PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_INSTANCE || call_mode == CALL_MODE_BASIC_TYPE) {
- if (p_idx == 0) {
- PropertyInfo pi;
- pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type);
- pi.name = "instance";
- return pi;
- }
- }
-
- List<PropertyInfo> props;
- ClassDB::get_property_list(_get_base_type(), &props, false);
- for (const PropertyInfo &E : props) {
- if (E.name == property) {
- String detail_prop_name = property;
- if (index != StringName()) {
- detail_prop_name += "." + String(index);
- }
- PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, E.hint, E.hint_string);
- _adjust_input_index(pinfo);
- return pinfo;
- }
- }
-
- PropertyInfo pinfo = type_cache;
- _adjust_input_index(pinfo);
- return pinfo;
-}
-
-PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(basic_type, "pass");
- } else if (call_mode == CALL_MODE_INSTANCE) {
- return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
- } else {
- return PropertyInfo();
- }
-}
-
-String VisualScriptPropertySet::get_caption() const {
- static const LocalVector<String> opname = {
- RTR("Set %s"),
- RTR("Add %s"),
- RTR("Subtract %s"),
- RTR("Multiply %s"),
- RTR("Divide %s"),
- RTR("Mod %s"),
- RTR("ShiftLeft %s"),
- RTR("ShiftRight %s"),
- RTR("BitAnd %s"),
- RTR("BitOr %s"),
- RTR("BitXor %s"),
- };
-
- String prop = property;
- if (index != StringName()) {
- prop += "." + String(index);
- }
-
- return vformat(opname[assign_op], prop);
-}
-
-String VisualScriptPropertySet::get_text() const {
- if (!has_input_sequence_port()) {
- return "";
- }
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- return vformat(RTR("On %s"), Variant::get_type_name(basic_type));
- } else if (call_mode == CALL_MODE_INSTANCE) {
- return vformat(RTR("On %s"), base_type);
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- return " [" + String(base_path.simplified()) + "]";
- } else {
- return RTR("On Self");
- }
-}
-
-void VisualScriptPropertySet::_update_base_type() {
- //cache it because this information may not be available on load
- if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- base_type = node->get_class();
- }
- } else if (call_mode == CALL_MODE_SELF) {
- if (get_visual_script().is_valid()) {
- base_type = get_visual_script()->get_instance_base_type();
- }
- }
-}
-
-void VisualScriptPropertySet::set_basic_type(Variant::Type p_type) {
- if (basic_type == p_type) {
- return;
- }
- basic_type = p_type;
-
- notify_property_list_changed();
- _update_base_type();
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptPropertySet::get_basic_type() const {
- return basic_type;
-}
-
-void VisualScriptPropertySet::set_base_type(const StringName &p_type) {
- if (base_type == p_type) {
- return;
- }
-
- base_type = p_type;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertySet::get_base_type() const {
- return base_type;
-}
-
-void VisualScriptPropertySet::set_base_script(const String &p_path) {
- if (base_script == p_path) {
- return;
- }
-
- base_script = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptPropertySet::get_base_script() const {
- return base_script;
-}
-
-void VisualScriptPropertySet::_update_cache() {
- if (!Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop())) {
- return;
- }
-
- if (!Engine::get_singleton()->is_editor_hint()) { //only update cache if editor exists, it's pointless otherwise
- return;
- }
-
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- //not super efficient..
-
- Variant v;
- Callable::CallError ce;
- Variant::construct(basic_type, v, nullptr, 0, ce);
-
- List<PropertyInfo> pinfo;
- v.get_property_list(&pinfo);
-
- for (const PropertyInfo &E : pinfo) {
- if (E.name == property) {
- type_cache = E;
- }
- }
-
- } else {
- StringName type;
- Ref<Script> script;
- Node *node = nullptr;
-
- if (call_mode == CALL_MODE_NODE_PATH) {
- node = _get_base_node();
- if (node) {
- type = node->get_class();
- base_type = type; //cache, too
- script = node->get_script();
- }
- } else if (call_mode == CALL_MODE_SELF) {
- if (get_visual_script().is_valid()) {
- type = get_visual_script()->get_instance_base_type();
- base_type = type; //cache, too
- script = get_visual_script();
- }
- } else if (call_mode == CALL_MODE_INSTANCE) {
- type = base_type;
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(base_script)) {
- script = ResourceCache::get_ref(base_script);
- } else {
- return;
- }
- }
- }
-
- List<PropertyInfo> pinfo;
-
- if (node) {
- node->get_property_list(&pinfo);
- } else {
- ClassDB::get_property_list(type, &pinfo);
- }
-
- if (script.is_valid()) {
- script->get_script_property_list(&pinfo);
- }
-
- for (const PropertyInfo &E : pinfo) {
- if (E.name == property) {
- type_cache = E;
- return;
- }
- }
- }
-}
-
-void VisualScriptPropertySet::set_property(const StringName &p_type) {
- if (property == p_type) {
- return;
- }
-
- property = p_type;
- index = StringName();
- _update_cache();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertySet::get_property() const {
- return property;
-}
-
-void VisualScriptPropertySet::set_base_path(const NodePath &p_type) {
- if (base_path == p_type) {
- return;
- }
-
- base_path = p_type;
- _update_base_type();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-NodePath VisualScriptPropertySet::get_base_path() const {
- return base_path;
-}
-
-void VisualScriptPropertySet::set_call_mode(CallMode p_mode) {
- if (call_mode == p_mode) {
- return;
- }
-
- call_mode = p_mode;
- _update_base_type();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptPropertySet::CallMode VisualScriptPropertySet::get_call_mode() const {
- return call_mode;
-}
-
-void VisualScriptPropertySet::_set_type_cache(const Dictionary &p_type) {
- type_cache = PropertyInfo::from_dict(p_type);
-}
-
-Dictionary VisualScriptPropertySet::_get_type_cache() const {
- return type_cache;
-}
-
-void VisualScriptPropertySet::set_index(const StringName &p_type) {
- if (index == p_type) {
- return;
- }
- index = p_type;
- _update_cache();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertySet::get_index() const {
- return index;
-}
-
-void VisualScriptPropertySet::set_assign_op(AssignOp p_op) {
- ERR_FAIL_INDEX(p_op, ASSIGN_OP_MAX);
- if (assign_op == p_op) {
- return;
- }
-
- assign_op = p_op;
- _update_cache();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptPropertySet::AssignOp VisualScriptPropertySet::get_assign_op() const {
- return assign_op;
-}
-
-void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
- if (property.name == "base_type") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
- }
- }
-
- if (property.name == "base_script") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "basic_type") {
- if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "node_path") {
- if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = PROPERTY_USAGE_NONE;
- } else {
- Node *bnode = _get_base_node();
- if (bnode) {
- property.hint_string = bnode->get_path(); //convert to long string
- }
- }
- }
-
- if (property.name == "property") {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE;
- property.hint_string = Variant::get_type_name(basic_type);
-
- } else if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
- property.hint_string = itos(get_visual_script()->get_instance_id());
- } else if (call_mode == CALL_MODE_INSTANCE) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
- property.hint_string = base_type;
-
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(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());
- }
- }
- }
-
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_INSTANCE;
- property.hint_string = itos(node->get_instance_id());
- } else {
- property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
- property.hint_string = get_base_type();
- }
- }
- }
-
- if (property.name == "index") {
- Callable::CallError ce;
- Variant v;
- Variant::construct(type_cache.type, v, nullptr, 0, ce);
- List<PropertyInfo> plist;
- v.get_property_list(&plist);
- String options = "";
- for (const PropertyInfo &E : plist) {
- options += "," + E.name;
- }
-
- property.hint = PROPERTY_HINT_ENUM;
- property.hint_string = options;
- property.type = Variant::STRING;
- if (options.is_empty()) {
- property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
- }
- }
-}
-
-void VisualScriptPropertySet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_base_type", "base_type"), &VisualScriptPropertySet::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptPropertySet::get_base_type);
-
- ClassDB::bind_method(D_METHOD("set_base_script", "base_script"), &VisualScriptPropertySet::set_base_script);
- ClassDB::bind_method(D_METHOD("get_base_script"), &VisualScriptPropertySet::get_base_script);
-
- ClassDB::bind_method(D_METHOD("set_basic_type", "basic_type"), &VisualScriptPropertySet::set_basic_type);
- ClassDB::bind_method(D_METHOD("get_basic_type"), &VisualScriptPropertySet::get_basic_type);
-
- ClassDB::bind_method(D_METHOD("_set_type_cache", "type_cache"), &VisualScriptPropertySet::_set_type_cache);
- ClassDB::bind_method(D_METHOD("_get_type_cache"), &VisualScriptPropertySet::_get_type_cache);
-
- ClassDB::bind_method(D_METHOD("set_property", "property"), &VisualScriptPropertySet::set_property);
- ClassDB::bind_method(D_METHOD("get_property"), &VisualScriptPropertySet::get_property);
-
- ClassDB::bind_method(D_METHOD("set_call_mode", "mode"), &VisualScriptPropertySet::set_call_mode);
- ClassDB::bind_method(D_METHOD("get_call_mode"), &VisualScriptPropertySet::get_call_mode);
-
- ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptPropertySet::set_base_path);
- ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptPropertySet::get_base_path);
-
- ClassDB::bind_method(D_METHOD("set_index", "index"), &VisualScriptPropertySet::set_index);
- ClassDB::bind_method(D_METHOD("get_index"), &VisualScriptPropertySet::get_index);
-
- ClassDB::bind_method(D_METHOD("set_assign_op", "assign_op"), &VisualScriptPropertySet::set_assign_op);
- ClassDB::bind_method(D_METHOD("get_assign_op"), &VisualScriptPropertySet::get_assign_op);
-
- String bt;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (i > 0) {
- bt += ",";
- }
-
- bt += Variant::get_type_name(Variant::Type(i));
- }
-
- List<String> script_extensions;
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
- }
-
- String script_ext_hint;
- for (const String &E : script_extensions) {
- if (!script_ext_hint.is_empty()) {
- script_ext_hint += ",";
- }
- script_ext_hint += "*." + E;
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "index"), "set_index", "get_index");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "assign_op", PROPERTY_HINT_ENUM, "Assign,Add,Sub,Mul,Div,Mod,ShiftLeft,ShiftRight,BitAnd,BitOr,Bitxor"), "set_assign_op", "get_assign_op");
-
- BIND_ENUM_CONSTANT(CALL_MODE_SELF);
- BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
- BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
- BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
-
- BIND_ENUM_CONSTANT(ASSIGN_OP_NONE);
- BIND_ENUM_CONSTANT(ASSIGN_OP_ADD);
- BIND_ENUM_CONSTANT(ASSIGN_OP_SUB);
- BIND_ENUM_CONSTANT(ASSIGN_OP_MUL);
- BIND_ENUM_CONSTANT(ASSIGN_OP_DIV);
- BIND_ENUM_CONSTANT(ASSIGN_OP_MOD);
- BIND_ENUM_CONSTANT(ASSIGN_OP_SHIFT_LEFT);
- BIND_ENUM_CONSTANT(ASSIGN_OP_SHIFT_RIGHT);
- BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_AND);
- BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_OR);
- BIND_ENUM_CONSTANT(ASSIGN_OP_BIT_XOR);
-}
-
-class VisualScriptNodeInstancePropertySet : public VisualScriptNodeInstance {
-public:
- VisualScriptPropertySet::CallMode call_mode;
- NodePath node_path;
- StringName property;
-
- VisualScriptPropertySet *node = nullptr;
- VisualScriptInstance *instance = nullptr;
- VisualScriptPropertySet::AssignOp assign_op;
- StringName index;
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- _FORCE_INLINE_ void _process_get(Variant &source, const Variant &p_argument, bool &valid) {
- if (index != StringName() && assign_op == VisualScriptPropertySet::ASSIGN_OP_NONE) {
- source.set_named(index, p_argument, valid);
- } else {
- Variant value;
- if (index != StringName()) {
- value = source.get_named(index, valid);
- } else {
- value = source;
- }
-
- switch (assign_op) {
- case VisualScriptPropertySet::ASSIGN_OP_NONE: {
- //should never get here
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_ADD: {
- value = Variant::evaluate(Variant::OP_ADD, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_SUB: {
- value = Variant::evaluate(Variant::OP_SUBTRACT, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_MUL: {
- value = Variant::evaluate(Variant::OP_MULTIPLY, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_DIV: {
- value = Variant::evaluate(Variant::OP_DIVIDE, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_MOD: {
- value = Variant::evaluate(Variant::OP_MODULE, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_SHIFT_LEFT: {
- value = Variant::evaluate(Variant::OP_SHIFT_LEFT, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_SHIFT_RIGHT: {
- value = Variant::evaluate(Variant::OP_SHIFT_RIGHT, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_BIT_AND: {
- value = Variant::evaluate(Variant::OP_BIT_AND, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_BIT_OR: {
- value = Variant::evaluate(Variant::OP_BIT_OR, value, p_argument);
- } break;
- case VisualScriptPropertySet::ASSIGN_OP_BIT_XOR: {
- value = Variant::evaluate(Variant::OP_BIT_XOR, value, p_argument);
- } break;
- default: {
- }
- }
-
- if (index != StringName()) {
- source.set_named(index, value, valid);
- } else {
- source = value;
- }
- }
- }
-
- 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) {
- case VisualScriptPropertySet::CALL_MODE_SELF: {
- Object *object = instance->get_owner_ptr();
-
- bool valid;
-
- if (needs_get) {
- Variant value = object->get(property, &valid);
- _process_get(value, *p_inputs[0], valid);
- object->set(property, value, &valid);
- } else {
- object->set(property, *p_inputs[0], &valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid set value '" + String(*p_inputs[0]) + "' on property '" + String(property) + "' of type " + object->get_class();
- }
- } break;
- case VisualScriptPropertySet::CALL_MODE_NODE_PATH: {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Base object is not a Node!";
- return 0;
- }
-
- Node *another = node->get_node(node_path);
- if (!another) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Path does not lead Node!";
- return 0;
- }
-
- bool valid;
-
- if (needs_get) {
- Variant value = another->get(property, &valid);
- _process_get(value, *p_inputs[0], valid);
- another->set(property, value, &valid);
- } else {
- another->set(property, *p_inputs[0], &valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid set value '" + String(*p_inputs[0]) + "' on property '" + String(property) + "' of type " + another->get_class();
- }
-
- } break;
- case VisualScriptPropertySet::CALL_MODE_INSTANCE:
- case VisualScriptPropertySet::CALL_MODE_BASIC_TYPE: {
- Variant v = *p_inputs[0];
-
- bool valid;
-
- if (needs_get) {
- Variant value = v.get_named(property, valid);
- _process_get(value, *p_inputs[1], valid);
- v.set_named(property, value, valid);
-
- } else {
- v.set_named(property, *p_inputs[1], valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid set value '" + String(*p_inputs[1]) + "' (" + Variant::get_type_name(p_inputs[1]->get_type()) + ") on property '" + String(property) + "' of type " + Variant::get_type_name(v.get_type());
- }
-
- *p_outputs[0] = v;
-
- } break;
- }
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptPropertySet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstancePropertySet *instance = memnew(VisualScriptNodeInstancePropertySet);
- instance->node = this;
- instance->instance = p_instance;
- instance->property = property;
- instance->call_mode = call_mode;
- instance->node_path = base_path;
- instance->assign_op = assign_op;
- instance->index = index;
- instance->needs_get = index != StringName() || assign_op != ASSIGN_OP_NONE;
- return instance;
-}
-
-VisualScriptPropertySet::TypeGuess VisualScriptPropertySet::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- if (p_output == 0 && call_mode == CALL_MODE_INSTANCE) {
- return p_inputs[0];
- }
-
- return VisualScriptNode::guess_output_type(p_inputs, p_output);
-}
-
-VisualScriptPropertySet::VisualScriptPropertySet() {
- assign_op = ASSIGN_OP_NONE;
- call_mode = CALL_MODE_SELF;
- base_type = "Object";
- basic_type = Variant::NIL;
-}
-
-template <VisualScriptPropertySet::CallMode cmode>
-static Ref<VisualScriptNode> create_property_set_node(const String &p_name) {
- Ref<VisualScriptPropertySet> node;
- node.instantiate();
- node->set_call_mode(cmode);
- return node;
-}
-
-//////////////////////////////////////////
-////////////////GET//////////////////////
-//////////////////////////////////////////
-
-int VisualScriptPropertyGet::get_output_sequence_port_count() const {
- return 0; // (call_mode==CALL_MODE_SELF || call_mode==CALL_MODE_NODE_PATH)?0:1;
-}
-
-bool VisualScriptPropertyGet::has_input_sequence_port() const {
- return false; //(call_mode==CALL_MODE_SELF || call_mode==CALL_MODE_NODE_PATH)?false:true;
-}
-
-void VisualScriptPropertyGet::_update_base_type() {
- //cache it because this information may not be available on load
- if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- base_type = node->get_class();
- }
- } else if (call_mode == CALL_MODE_SELF) {
- if (get_visual_script().is_valid()) {
- base_type = get_visual_script()->get_instance_base_type();
- }
- }
-}
-
-Node *VisualScriptPropertyGet::_get_base_node() const {
-#ifdef TOOLS_ENABLED
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return nullptr;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
-
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return nullptr;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return nullptr;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return nullptr;
- }
-
- if (!script_node->has_node(base_path)) {
- return nullptr;
- }
-
- Node *path_to = script_node->get_node(base_path);
-
- return path_to;
-#else
-
- return nullptr;
-#endif
-}
-
-StringName VisualScriptPropertyGet::_get_base_type() const {
- if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- return get_visual_script()->get_instance_base_type();
- } else if (call_mode == CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
- Node *path = _get_base_node();
- if (path) {
- return path->get_class();
- }
- }
-
- return base_type;
-}
-
-int VisualScriptPropertyGet::get_input_value_port_count() const {
- return (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 1 : 0;
-}
-
-int VisualScriptPropertyGet::get_output_value_port_count() const {
- int pc = (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 2 : 1;
-
- return pc;
-}
-
-String VisualScriptPropertyGet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_INSTANCE || call_mode == CALL_MODE_BASIC_TYPE) {
- if (p_idx == 0) {
- PropertyInfo pi;
- pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type);
- pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower());
- return pi;
- }
- }
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_BASIC_TYPE && p_idx == 0) {
- return PropertyInfo(basic_type, "pass");
- } else if (call_mode == CALL_MODE_INSTANCE && p_idx == 0) {
- return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
- } else {
- List<PropertyInfo> props;
- ClassDB::get_property_list(_get_base_type(), &props, false);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (E->get().name == property) {
- PropertyInfo pinfo = PropertyInfo(E->get().type, String(property) + "." + String(index), E->get().hint, E->get().hint_string);
- _adjust_input_index(pinfo);
- return pinfo;
- }
- }
- }
-
- PropertyInfo pinfo = PropertyInfo(type_cache, "value");
- _adjust_input_index(pinfo);
- return pinfo;
-}
-
-String VisualScriptPropertyGet::get_caption() const {
- String prop = property;
- if (index != StringName()) {
- prop += "." + String(index);
- }
-
- return vformat(RTR("Get %s"), prop);
-}
-
-String VisualScriptPropertyGet::get_text() const {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- return vformat(RTR("On %s"), Variant::get_type_name(basic_type));
- } else if (call_mode == CALL_MODE_INSTANCE) {
- return vformat(RTR("On %s"), base_type);
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- return " [" + String(base_path.simplified()) + "]";
- } else {
- return RTR("On Self");
- }
-}
-
-void VisualScriptPropertyGet::set_base_type(const StringName &p_type) {
- if (base_type == p_type) {
- return;
- }
-
- base_type = p_type;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertyGet::get_base_type() const {
- return base_type;
-}
-
-void VisualScriptPropertyGet::set_base_script(const String &p_path) {
- if (base_script == p_path) {
- return;
- }
-
- base_script = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptPropertyGet::get_base_script() const {
- return base_script;
-}
-
-void VisualScriptPropertyGet::_update_cache() {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- //not super efficient..
-
- Variant v;
- Callable::CallError ce;
- Variant::construct(basic_type, v, nullptr, 0, ce);
-
- List<PropertyInfo> pinfo;
- v.get_property_list(&pinfo);
-
- for (const PropertyInfo &E : pinfo) {
- if (E.name == property) {
- type_cache = E.type;
- return;
- }
- }
-
- } else {
- StringName type;
- Ref<Script> script;
- Node *node = nullptr;
-
- if (call_mode == CALL_MODE_NODE_PATH) {
- node = _get_base_node();
- if (node) {
- type = node->get_class();
- base_type = type; //cache, too
- script = node->get_script();
- }
- } else if (call_mode == CALL_MODE_SELF) {
- if (get_visual_script().is_valid()) {
- type = get_visual_script()->get_instance_base_type();
- base_type = type; //cache, too
- script = get_visual_script();
- }
- } else if (call_mode == CALL_MODE_INSTANCE) {
- type = base_type;
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(base_script)) {
- script = ResourceCache::get_ref(base_script);
- } else {
- return;
- }
- }
- }
-
- bool valid = false;
-
- Variant::Type type_ret;
-
- type_ret = ClassDB::get_property_type(base_type, property, &valid);
-
- if (valid) {
- type_cache = type_ret;
- return; //all dandy
- }
-
- if (node) {
- Variant prop = node->get(property, &valid);
- if (valid) {
- type_cache = prop.get_type();
- return; //all dandy again
- }
- }
-
- if (script.is_valid()) {
- type_ret = script->get_static_property_type(property, &valid);
-
- if (valid) {
- type_cache = type_ret;
- return; //all dandy
- }
- }
- }
-}
-
-void VisualScriptPropertyGet::set_property(const StringName &p_type) {
- if (property == p_type) {
- return;
- }
-
- property = p_type;
-
- _update_cache();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertyGet::get_property() const {
- return property;
-}
-
-void VisualScriptPropertyGet::set_base_path(const NodePath &p_type) {
- if (base_path == p_type) {
- return;
- }
-
- base_path = p_type;
- notify_property_list_changed();
- _update_base_type();
- ports_changed_notify();
-}
-
-NodePath VisualScriptPropertyGet::get_base_path() const {
- return base_path;
-}
-
-void VisualScriptPropertyGet::set_call_mode(CallMode p_mode) {
- if (call_mode == p_mode) {
- return;
- }
-
- call_mode = p_mode;
- notify_property_list_changed();
- _update_base_type();
- ports_changed_notify();
-}
-
-VisualScriptPropertyGet::CallMode VisualScriptPropertyGet::get_call_mode() const {
- return call_mode;
-}
-
-void VisualScriptPropertyGet::set_basic_type(Variant::Type p_type) {
- if (basic_type == p_type) {
- return;
- }
- basic_type = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptPropertyGet::get_basic_type() const {
- return basic_type;
-}
-
-void VisualScriptPropertyGet::_set_type_cache(Variant::Type p_type) {
- type_cache = p_type;
-}
-
-Variant::Type VisualScriptPropertyGet::_get_type_cache() const {
- return type_cache;
-}
-
-void VisualScriptPropertyGet::_adjust_input_index(PropertyInfo &pinfo) const {
- if (index != StringName()) {
- Variant v;
- Callable::CallError ce;
- Variant::construct(pinfo.type, v, nullptr, 0, ce);
- Variant i = v.get(index);
- pinfo.type = i.get_type();
- pinfo.name = String(property) + "." + index;
- } else {
- pinfo.name = String(property);
- }
-}
-
-void VisualScriptPropertyGet::set_index(const StringName &p_type) {
- if (index == p_type) {
- return;
- }
- index = p_type;
- _update_cache();
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptPropertyGet::get_index() const {
- return index;
-}
-
-void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
- if (property.name == "base_type") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
- }
- }
-
- if (property.name == "base_script") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "basic_type") {
- if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-
- if (property.name == "node_path") {
- if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = PROPERTY_USAGE_NONE;
- } else {
- Node *bnode = _get_base_node();
- if (bnode) {
- property.hint_string = bnode->get_path(); //convert to long string
- }
- }
- }
-
- if (property.name == "property") {
- if (call_mode == CALL_MODE_BASIC_TYPE) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE;
- property.hint_string = Variant::get_type_name(basic_type);
-
- } else if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
- property.hint_string = itos(get_visual_script()->get_instance_id());
- } else if (call_mode == CALL_MODE_INSTANCE) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
- property.hint_string = base_type;
-
- if (!base_script.is_empty()) {
- if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
- ScriptServer::edit_request_func(base_script); //make sure it's loaded
- }
-
- if (ResourceCache::has(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());
- }
- }
- }
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- Node *node = _get_base_node();
- if (node) {
- property.hint = PROPERTY_HINT_PROPERTY_OF_INSTANCE;
- property.hint_string = itos(node->get_instance_id());
- } else {
- property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
- property.hint_string = get_base_type();
- }
- }
- }
-
- if (property.name == "index") {
- Callable::CallError ce;
- Variant v;
- Variant::construct(type_cache, v, nullptr, 0, ce);
- List<PropertyInfo> plist;
- v.get_property_list(&plist);
- String options = "";
- for (const PropertyInfo &E : plist) {
- options += "," + E.name;
- }
-
- property.hint = PROPERTY_HINT_ENUM;
- property.hint_string = options;
- property.type = Variant::STRING;
- if (options.is_empty()) {
- property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
- }
- }
-}
-
-void VisualScriptPropertyGet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_base_type", "base_type"), &VisualScriptPropertyGet::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptPropertyGet::get_base_type);
-
- ClassDB::bind_method(D_METHOD("set_base_script", "base_script"), &VisualScriptPropertyGet::set_base_script);
- ClassDB::bind_method(D_METHOD("get_base_script"), &VisualScriptPropertyGet::get_base_script);
-
- ClassDB::bind_method(D_METHOD("set_basic_type", "basic_type"), &VisualScriptPropertyGet::set_basic_type);
- ClassDB::bind_method(D_METHOD("get_basic_type"), &VisualScriptPropertyGet::get_basic_type);
-
- ClassDB::bind_method(D_METHOD("_set_type_cache", "type_cache"), &VisualScriptPropertyGet::_set_type_cache);
- ClassDB::bind_method(D_METHOD("_get_type_cache"), &VisualScriptPropertyGet::_get_type_cache);
-
- ClassDB::bind_method(D_METHOD("set_property", "property"), &VisualScriptPropertyGet::set_property);
- ClassDB::bind_method(D_METHOD("get_property"), &VisualScriptPropertyGet::get_property);
-
- ClassDB::bind_method(D_METHOD("set_call_mode", "mode"), &VisualScriptPropertyGet::set_call_mode);
- ClassDB::bind_method(D_METHOD("get_call_mode"), &VisualScriptPropertyGet::get_call_mode);
-
- ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptPropertyGet::set_base_path);
- ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptPropertyGet::get_base_path);
-
- ClassDB::bind_method(D_METHOD("set_index", "index"), &VisualScriptPropertyGet::set_index);
- ClassDB::bind_method(D_METHOD("get_index"), &VisualScriptPropertyGet::get_index);
-
- String bt;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (i > 0) {
- bt += ",";
- }
-
- bt += Variant::get_type_name(Variant::Type(i));
- }
-
- List<String> script_extensions;
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->get_recognized_extensions(&script_extensions);
- }
-
- String script_ext_hint;
- for (const String &E : script_extensions) {
- if (!script_ext_hint.is_empty()) {
- script_ext_hint += ",";
- }
- script_ext_hint += "." + E;
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "index", PROPERTY_HINT_ENUM), "set_index", "get_index");
-
- BIND_ENUM_CONSTANT(CALL_MODE_SELF);
- BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
- BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
- BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
-}
-
-class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance {
-public:
- VisualScriptPropertyGet::CallMode call_mode;
- NodePath node_path;
- StringName property;
- StringName index;
-
- 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) {
- case VisualScriptPropertyGet::CALL_MODE_SELF: {
- Object *object = instance->get_owner_ptr();
-
- bool valid;
-
- *p_outputs[0] = object->get(property, &valid);
-
- if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index, valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Invalid index property name.");
- return 0;
- }
- } break;
- case VisualScriptPropertyGet::CALL_MODE_NODE_PATH: {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Base object is not a Node!");
- return 0;
- }
-
- Node *another = node->get_node(node_path);
- if (!another) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Path does not lead Node!");
- return 0;
- }
-
- bool valid;
-
- *p_outputs[0] = another->get(property, &valid);
-
- if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index, valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = vformat(RTR("Invalid index property name '%s' in node %s."), String(property), another->get_name());
- return 0;
- }
-
- } break;
- default: {
- bool valid;
- Variant v = *p_inputs[0];
-
- *p_outputs[1] = v.get(property, &valid);
- if (index != StringName()) {
- *p_outputs[1] = p_outputs[1]->get_named(index, valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Invalid index property name.");
- }
-
- *p_outputs[0] = v;
- };
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptPropertyGet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstancePropertyGet *instance = memnew(VisualScriptNodeInstancePropertyGet);
- instance->node = this;
- instance->instance = p_instance;
- instance->property = property;
- instance->call_mode = call_mode;
- instance->node_path = base_path;
- instance->index = index;
-
- return instance;
-}
-
-VisualScriptPropertyGet::VisualScriptPropertyGet() {
- call_mode = CALL_MODE_SELF;
- base_type = "Object";
- basic_type = Variant::NIL;
- type_cache = Variant::NIL;
-}
-
-template <VisualScriptPropertyGet::CallMode cmode>
-static Ref<VisualScriptNode> create_property_get_node(const String &p_name) {
- Ref<VisualScriptPropertyGet> node;
- node.instantiate();
- node->set_call_mode(cmode);
- return node;
-}
-
-//////////////////////////////////////////
-////////////////EMIT//////////////////////
-//////////////////////////////////////////
-
-int VisualScriptEmitSignal::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptEmitSignal::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptEmitSignal::get_input_value_port_count() const {
- Ref<VisualScript> vs = get_visual_script();
- if (vs.is_valid()) {
- if (!vs->has_custom_signal(name)) {
- return 0;
- }
-
- return vs->custom_signal_get_argument_count(name);
- }
-
- return 0;
-}
-
-int VisualScriptEmitSignal::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptEmitSignal::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptEmitSignal::get_input_value_port_info(int p_idx) const {
- Ref<VisualScript> vs = get_visual_script();
- if (vs.is_valid()) {
- if (!vs->has_custom_signal(name)) {
- return PropertyInfo();
- }
-
- return PropertyInfo(vs->custom_signal_get_argument_type(name, p_idx), vs->custom_signal_get_argument_name(name, p_idx));
- }
-
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptEmitSignal::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptEmitSignal::get_caption() const {
- return vformat(RTR("Emit %s"), name);
-}
-
-void VisualScriptEmitSignal::set_signal(const StringName &p_type) {
- if (name == p_type) {
- return;
- }
-
- name = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptEmitSignal::get_signal() const {
- return name;
-}
-
-void VisualScriptEmitSignal::_validate_property(PropertyInfo &property) const {
- if (property.name == "signal") {
- property.hint = PROPERTY_HINT_ENUM;
-
- List<StringName> sigs;
- List<MethodInfo> base_sigs;
-
- Ref<VisualScript> vs = get_visual_script();
- if (vs.is_valid()) {
- vs->get_custom_signal_list(&sigs);
- ClassDB::get_signal_list(vs->get_instance_base_type(), &base_sigs);
- }
-
- String ml;
- for (const StringName &E : sigs) {
- if (!ml.is_empty()) {
- ml += ",";
- }
- ml += E;
- }
- for (const MethodInfo &E : base_sigs) {
- if (!ml.is_empty()) {
- ml += ",";
- }
- ml += E.name;
- }
-
- property.hint_string = ml;
- }
-}
-
-void VisualScriptEmitSignal::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_signal", "name"), &VisualScriptEmitSignal::set_signal);
- ClassDB::bind_method(D_METHOD("get_signal"), &VisualScriptEmitSignal::get_signal);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal"), "set_signal", "get_signal");
-}
-
-class VisualScriptNodeInstanceEmitSignal : public VisualScriptNodeInstance {
-public:
- VisualScriptEmitSignal *node = nullptr;
- VisualScriptInstance *instance = nullptr;
- int argcount = 0;
- StringName name;
-
- //virtual int get_working_memory_size() const override { return 0; }
- //virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 {
- Object *obj = instance->get_owner_ptr();
-
- obj->emit_signalp(name, p_inputs, argcount);
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptEmitSignal::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceEmitSignal *instance = memnew(VisualScriptNodeInstanceEmitSignal);
- instance->node = this;
- instance->instance = p_instance;
- instance->name = name;
- instance->argcount = get_input_value_port_count();
- return instance;
-}
-
-VisualScriptEmitSignal::VisualScriptEmitSignal() {
-}
-
-static Ref<VisualScriptNode> create_basic_type_call_node(const String &p_name) {
- Vector<String> path = p_name.split("/");
- ERR_FAIL_COND_V(path.size() < 4, Ref<VisualScriptNode>());
- String base_type = path[2];
- String method = path[3];
-
- Ref<VisualScriptFunctionCall> node;
- node.instantiate();
-
- Variant::Type type = Variant::VARIANT_MAX;
-
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (Variant::get_type_name(Variant::Type(i)) == base_type) {
- type = Variant::Type(i);
- break;
- }
- }
-
- ERR_FAIL_COND_V(type == Variant::VARIANT_MAX, Ref<VisualScriptNode>());
-
- node->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE);
- node->set_basic_type(type);
- node->set_function(method);
-
- return node;
-}
-
-void register_visual_script_func_nodes() {
- VisualScriptLanguage::singleton->add_register_func("functions/call", create_node_generic<VisualScriptFunctionCall>);
- VisualScriptLanguage::singleton->add_register_func("functions/set", create_node_generic<VisualScriptPropertySet>);
- VisualScriptLanguage::singleton->add_register_func("functions/get", create_node_generic<VisualScriptPropertyGet>);
-
- //VisualScriptLanguage::singleton->add_register_func("functions/call_script/call_self",create_script_call_node<VisualScriptScriptCall::CALL_MODE_SELF>);
- //VisualScriptLanguage::singleton->add_register_func("functions/call_script/call_node",create_script_call_node<VisualScriptScriptCall::CALL_MODE_NODE_PATH>);
- VisualScriptLanguage::singleton->add_register_func("functions/emit_signal", create_node_generic<VisualScriptEmitSignal>);
-
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- Variant::Type t = Variant::Type(i);
- String type_name = Variant::get_type_name(t);
- Callable::CallError ce;
- Variant vt;
- Variant::construct(t, vt, nullptr, 0, ce);
- List<MethodInfo> ml;
- vt.get_method_list(&ml);
-
- for (const MethodInfo &E : ml) {
- VisualScriptLanguage::singleton->add_register_func("functions/by_type/" + type_name + "/" + E.name, create_basic_type_call_node);
- }
- }
-}
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
deleted file mode 100644
index 886ed7bc81..0000000000
--- a/modules/visual_script/visual_script_func_nodes.h
+++ /dev/null
@@ -1,363 +0,0 @@
-/*************************************************************************/
-/* visual_script_func_nodes.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 VISUAL_SCRIPT_FUNC_NODES_H
-#define VISUAL_SCRIPT_FUNC_NODES_H
-
-#include "visual_script.h"
-
-class VisualScriptFunctionCall : public VisualScriptNode {
- GDCLASS(VisualScriptFunctionCall, VisualScriptNode);
-
-public:
- enum CallMode {
- CALL_MODE_SELF,
- CALL_MODE_NODE_PATH,
- CALL_MODE_INSTANCE,
- CALL_MODE_BASIC_TYPE,
- CALL_MODE_SINGLETON,
- };
-
- enum RPCCallMode {
- RPC_DISABLED,
- RPC_RELIABLE,
- RPC_UNRELIABLE,
- RPC_RELIABLE_TO_ID,
- RPC_UNRELIABLE_TO_ID
- };
-
-private:
- CallMode call_mode;
- StringName base_type;
- String base_script;
- Variant::Type basic_type;
- NodePath base_path;
- StringName function;
- int use_default_args;
- RPCCallMode rpc_call_mode;
- StringName singleton;
- bool validate;
-
- Node *_get_base_node() const;
- StringName _get_base_type() const;
-
- MethodInfo method_cache;
- void _update_method_cache();
-
- void _set_argument_cache(const Dictionary &p_cache);
- Dictionary _get_argument_cache() const;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- void set_basic_type(Variant::Type p_type);
- Variant::Type get_basic_type() const;
-
- void set_base_type(const StringName &p_type);
- StringName get_base_type() const;
-
- void set_base_script(const String &p_path);
- String get_base_script() const;
-
- void set_singleton(const StringName &p_type);
- StringName get_singleton() const;
-
- void set_function(const StringName &p_type);
- StringName get_function() const;
-
- void set_base_path(const NodePath &p_type);
- NodePath get_base_path() const;
-
- void set_call_mode(CallMode p_mode);
- CallMode get_call_mode() const;
-
- void set_use_default_args(int p_amount);
- int get_use_default_args() const;
-
- void set_validate(bool p_amount);
- bool get_validate() const;
-
- void set_rpc_call_mode(RPCCallMode p_mode);
- RPCCallMode get_rpc_call_mode() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptFunctionCall();
-};
-
-VARIANT_ENUM_CAST(VisualScriptFunctionCall::CallMode);
-VARIANT_ENUM_CAST(VisualScriptFunctionCall::RPCCallMode);
-
-class VisualScriptPropertySet : public VisualScriptNode {
- GDCLASS(VisualScriptPropertySet, VisualScriptNode);
-
-public:
- enum CallMode {
- CALL_MODE_SELF,
- CALL_MODE_NODE_PATH,
- CALL_MODE_INSTANCE,
- CALL_MODE_BASIC_TYPE,
-
- };
-
- enum AssignOp {
- ASSIGN_OP_NONE,
- ASSIGN_OP_ADD,
- ASSIGN_OP_SUB,
- ASSIGN_OP_MUL,
- ASSIGN_OP_DIV,
- ASSIGN_OP_MOD,
- ASSIGN_OP_SHIFT_LEFT,
- ASSIGN_OP_SHIFT_RIGHT,
- ASSIGN_OP_BIT_AND,
- ASSIGN_OP_BIT_OR,
- ASSIGN_OP_BIT_XOR,
- ASSIGN_OP_MAX
- };
-
-private:
- PropertyInfo type_cache;
-
- CallMode call_mode;
- Variant::Type basic_type;
- StringName base_type;
- String base_script;
- NodePath base_path;
- StringName property;
- StringName index;
- AssignOp assign_op;
-
- Node *_get_base_node() const;
- StringName _get_base_type() const;
-
- void _update_base_type();
-
- void _update_cache();
-
- void _set_type_cache(const Dictionary &p_type);
- Dictionary _get_type_cache() const;
-
- void _adjust_input_index(PropertyInfo &pinfo) const;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- void set_base_type(const StringName &p_type);
- StringName get_base_type() const;
-
- void set_base_script(const String &p_path);
- String get_base_script() const;
-
- void set_basic_type(Variant::Type p_type);
- Variant::Type get_basic_type() const;
-
- void set_property(const StringName &p_type);
- StringName get_property() const;
-
- void set_base_path(const NodePath &p_type);
- NodePath get_base_path() const;
-
- void set_call_mode(CallMode p_mode);
- CallMode get_call_mode() const;
-
- void set_index(const StringName &p_type);
- StringName get_index() const;
-
- void set_assign_op(AssignOp p_op);
- AssignOp get_assign_op() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptPropertySet();
-};
-
-VARIANT_ENUM_CAST(VisualScriptPropertySet::CallMode);
-VARIANT_ENUM_CAST(VisualScriptPropertySet::AssignOp);
-
-class VisualScriptPropertyGet : public VisualScriptNode {
- GDCLASS(VisualScriptPropertyGet, VisualScriptNode);
-
-public:
- enum CallMode {
- CALL_MODE_SELF,
- CALL_MODE_NODE_PATH,
- CALL_MODE_INSTANCE,
- CALL_MODE_BASIC_TYPE,
-
- };
-
-private:
- Variant::Type type_cache;
-
- CallMode call_mode;
- Variant::Type basic_type;
- StringName base_type;
- String base_script;
- NodePath base_path;
- StringName property;
- StringName index;
-
- void _update_base_type();
- Node *_get_base_node() const;
- StringName _get_base_type() const;
-
- void _update_cache();
-
- void _set_type_cache(Variant::Type p_type);
- Variant::Type _get_type_cache() const;
-
- void _adjust_input_index(PropertyInfo &pinfo) const;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- void set_base_type(const StringName &p_type);
- StringName get_base_type() const;
-
- void set_base_script(const String &p_path);
- String get_base_script() const;
-
- void set_basic_type(Variant::Type p_type);
- Variant::Type get_basic_type() const;
-
- void set_property(const StringName &p_type);
- StringName get_property() const;
-
- void set_base_path(const NodePath &p_type);
- NodePath get_base_path() const;
-
- void set_call_mode(CallMode p_mode);
- CallMode get_call_mode() const;
-
- void set_index(const StringName &p_type);
- StringName get_index() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptPropertyGet();
-};
-
-VARIANT_ENUM_CAST(VisualScriptPropertyGet::CallMode);
-
-class VisualScriptEmitSignal : public VisualScriptNode {
- GDCLASS(VisualScriptEmitSignal, VisualScriptNode);
-
-private:
- StringName name;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- //virtual String get_text() const;
- virtual String get_category() const override { return "functions"; }
-
- void set_signal(const StringName &p_type);
- StringName get_signal() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptEmitSignal();
-};
-
-void register_visual_script_func_nodes();
-
-#endif // VISUAL_SCRIPT_FUNC_NODES_H
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
deleted file mode 100644
index 5907e6a489..0000000000
--- a/modules/visual_script/visual_script_nodes.cpp
+++ /dev/null
@@ -1,4072 +0,0 @@
-/*************************************************************************/
-/* visual_script_nodes.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 "visual_script_nodes.h"
-
-#include "core/config/engine.h"
-#include "core/config/project_settings.h"
-#include "core/core_constants.h"
-#include "core/input/input.h"
-#include "core/os/os.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-
-//////////////////////////////////////////
-////////////////FUNCTION//////////////////
-//////////////////////////////////////////
-
-bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value) {
- if (p_name == "argument_count") {
- int new_argc = p_value;
- int argc = arguments.size();
- if (argc == new_argc) {
- return true;
- }
-
- arguments.resize(new_argc);
-
- for (int i = argc; i < new_argc; i++) {
- arguments.write[i].name = "arg" + itos(i + 1);
- arguments.write[i].type = Variant::NIL;
- }
- ports_changed_notify();
- notify_property_list_changed();
- return true;
- }
- if (String(p_name).begins_with("argument_")) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, arguments.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- Variant::Type new_type = Variant::Type(int(p_value));
- arguments.write[idx].type = new_type;
- ports_changed_notify();
-
- return true;
- }
-
- if (what == "name") {
- arguments.write[idx].name = p_value;
- ports_changed_notify();
- return true;
- }
- }
-
- if (p_name == "stack/stackless") {
- set_stack_less(p_value);
- return true;
- }
-
- if (p_name == "stack/size") {
- stack_size = p_value;
- return true;
- }
-
- if (p_name == "rpc/mode") {
- rpc_mode = MultiplayerAPI::RPCMode(int(p_value));
- return true;
- }
-
- if (p_name == "sequenced/sequenced") {
- sequenced = p_value;
- ports_changed_notify();
- return true;
- }
-
- return false;
-}
-
-bool VisualScriptFunction::_get(const StringName &p_name, Variant &r_ret) const {
- if (p_name == "argument_count") {
- r_ret = arguments.size();
- return true;
- }
- if (String(p_name).begins_with("argument_")) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, arguments.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- r_ret = arguments[idx].type;
- return true;
- }
- if (what == "name") {
- r_ret = arguments[idx].name;
- return true;
- }
- }
-
- if (p_name == "stack/stackless") {
- r_ret = stack_less;
- return true;
- }
-
- if (p_name == "stack/size") {
- r_ret = stack_size;
- return true;
- }
-
- if (p_name == "rpc/mode") {
- r_ret = rpc_mode;
- return true;
- }
-
- if (p_name == "sequenced/sequenced") {
- r_ret = sequenced;
- return true;
- }
-
- return false;
-}
-
-void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const {
- p_list->push_back(PropertyInfo(Variant::INT, "argument_count", PROPERTY_HINT_RANGE, "0,256"));
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < arguments.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "argument_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::STRING, "argument_" + itos(i + 1) + "/name"));
- }
-
- p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced/sequenced"));
-
- if (!stack_less) {
- p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000"));
- }
- p_list->push_back(PropertyInfo(Variant::BOOL, "stack/stackless"));
- p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Any,Authority"));
-}
-
-int VisualScriptFunction::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptFunction::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptFunction::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptFunction::get_output_value_port_count() const {
- return arguments.size();
-}
-
-String VisualScriptFunction::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptFunction::get_input_value_port_info(int p_idx) const {
- ERR_FAIL_V(PropertyInfo());
-}
-
-PropertyInfo VisualScriptFunction::get_output_value_port_info(int p_idx) const {
- // Need to check it without ERR_FAIL_COND, to prevent warnings from appearing on node creation via dragging.
- if (p_idx < 0 || p_idx >= arguments.size()) {
- return PropertyInfo();
- }
- PropertyInfo out;
- out.type = arguments[p_idx].type;
- out.name = arguments[p_idx].name;
- out.hint = arguments[p_idx].hint;
- out.hint_string = arguments[p_idx].hint_string;
- return out;
-}
-
-String VisualScriptFunction::get_caption() const {
- return RTR("Function");
-}
-
-String VisualScriptFunction::get_text() const {
- return get_name(); //use name as function name I guess
-}
-
-void VisualScriptFunction::add_argument(Variant::Type p_type, const String &p_name, int p_index, const PropertyHint p_hint, const String &p_hint_string) {
- Argument arg;
- arg.name = p_name;
- arg.type = p_type;
- arg.hint = p_hint;
- arg.hint_string = p_hint_string;
- if (p_index >= 0) {
- arguments.insert(p_index, arg);
- } else {
- arguments.push_back(arg);
- }
-
- ports_changed_notify();
-}
-
-void VisualScriptFunction::set_argument_type(int p_argidx, Variant::Type p_type) {
- ERR_FAIL_INDEX(p_argidx, arguments.size());
-
- arguments.write[p_argidx].type = p_type;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptFunction::get_argument_type(int p_argidx) const {
- ERR_FAIL_INDEX_V(p_argidx, arguments.size(), Variant::NIL);
- return arguments[p_argidx].type;
-}
-
-void VisualScriptFunction::set_argument_name(int p_argidx, const String &p_name) {
- ERR_FAIL_INDEX(p_argidx, arguments.size());
-
- arguments.write[p_argidx].name = p_name;
- ports_changed_notify();
-}
-
-String VisualScriptFunction::get_argument_name(int p_argidx) const {
- ERR_FAIL_INDEX_V(p_argidx, arguments.size(), String());
- return arguments[p_argidx].name;
-}
-
-void VisualScriptFunction::remove_argument(int p_argidx) {
- ERR_FAIL_INDEX(p_argidx, arguments.size());
-
- arguments.remove_at(p_argidx);
- ports_changed_notify();
-}
-
-int VisualScriptFunction::get_argument_count() const {
- return arguments.size();
-}
-
-void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) {
- rpc_mode = p_mode;
-}
-
-MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const {
- return rpc_mode;
-}
-
-class VisualScriptNodeInstanceFunction : public VisualScriptNodeInstance {
-public:
- VisualScriptFunction *node = nullptr;
- VisualScriptInstance *instance = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- int ac = node->get_argument_count();
-
- for (int i = 0; i < ac; i++) {
-#ifdef DEBUG_ENABLED
- Variant::Type expected = node->get_argument_type(i);
- if (expected != Variant::NIL) {
- if (!Variant::can_convert_strict(p_inputs[i]->get_type(), expected)) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.expected = expected;
- r_error.argument = i;
- return 0;
- }
- }
-#endif
-
- *p_outputs[i] = *p_inputs[i];
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptFunction::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceFunction *instance = memnew(VisualScriptNodeInstanceFunction);
- instance->node = this;
- instance->instance = p_instance;
- return instance;
-}
-
-void VisualScriptFunction::reset_state() {
- arguments.clear();
- stack_size = 256;
- stack_less = false;
- sequenced = true;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
-}
-
-VisualScriptFunction::VisualScriptFunction() {
- stack_size = 256;
- stack_less = false;
- sequenced = true;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
-}
-
-void VisualScriptFunction::set_stack_less(bool p_enable) {
- stack_less = p_enable;
- notify_property_list_changed();
-}
-
-bool VisualScriptFunction::is_stack_less() const {
- return stack_less;
-}
-
-void VisualScriptFunction::set_sequenced(bool p_enable) {
- sequenced = p_enable;
-}
-
-bool VisualScriptFunction::is_sequenced() const {
- return sequenced;
-}
-
-void VisualScriptFunction::set_stack_size(int p_size) {
- ERR_FAIL_COND(p_size < 1 || p_size > 100000);
- stack_size = p_size;
-}
-
-int VisualScriptFunction::get_stack_size() const {
- return stack_size;
-}
-
-//////////////////////////////////////////
-/////////////////LISTS////////////////////
-//////////////////////////////////////////
-
-int VisualScriptLists::get_output_sequence_port_count() const {
- if (sequenced) {
- return 1;
- }
- return 0;
-}
-
-bool VisualScriptLists::has_input_sequence_port() const {
- return sequenced;
-}
-
-String VisualScriptLists::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-int VisualScriptLists::get_input_value_port_count() const {
- return inputports.size();
-}
-
-int VisualScriptLists::get_output_value_port_count() const {
- return outputports.size();
-}
-
-PropertyInfo VisualScriptLists::get_input_value_port_info(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo());
-
- PropertyInfo pi;
- pi.name = inputports[p_idx].name;
- pi.type = inputports[p_idx].type;
- return pi;
-}
-
-PropertyInfo VisualScriptLists::get_output_value_port_info(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, outputports.size(), PropertyInfo());
-
- PropertyInfo pi;
- pi.name = outputports[p_idx].name;
- pi.type = outputports[p_idx].type;
- return pi;
-}
-
-bool VisualScriptLists::is_input_port_editable() const {
- return ((flags & INPUT_EDITABLE) == INPUT_EDITABLE);
-}
-
-bool VisualScriptLists::is_input_port_name_editable() const {
- return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE);
-}
-
-bool VisualScriptLists::is_input_port_type_editable() const {
- return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE);
-}
-
-bool VisualScriptLists::is_output_port_editable() const {
- return ((flags & OUTPUT_EDITABLE) == OUTPUT_EDITABLE);
-}
-
-bool VisualScriptLists::is_output_port_name_editable() const {
- return ((flags & INPUT_NAME_EDITABLE) == INPUT_NAME_EDITABLE);
-}
-
-bool VisualScriptLists::is_output_port_type_editable() const {
- return ((flags & INPUT_TYPE_EDITABLE) == INPUT_TYPE_EDITABLE);
-}
-
-// for the inspector
-bool VisualScriptLists::_set(const StringName &p_name, const Variant &p_value) {
- if (p_name == "input_count" && is_input_port_editable()) {
- int new_argc = p_value;
- int argc = inputports.size();
- if (argc == new_argc) {
- return true;
- }
-
- inputports.resize(new_argc);
-
- for (int i = argc; i < new_argc; i++) {
- inputports.write[i].name = "arg" + itos(i + 1);
- inputports.write[i].type = Variant::NIL;
- }
- ports_changed_notify();
- notify_property_list_changed();
- return true;
- }
- if (String(p_name).begins_with("input_") && is_input_port_editable()) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, inputports.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- Variant::Type new_type = Variant::Type(int(p_value));
- inputports.write[idx].type = new_type;
- ports_changed_notify();
-
- return true;
- }
-
- if (what == "name") {
- inputports.write[idx].name = p_value;
- ports_changed_notify();
- return true;
- }
- }
-
- if (p_name == "output_count" && is_output_port_editable()) {
- int new_argc = p_value;
- int argc = outputports.size();
- if (argc == new_argc) {
- return true;
- }
-
- outputports.resize(new_argc);
-
- for (int i = argc; i < new_argc; i++) {
- outputports.write[i].name = "arg" + itos(i + 1);
- outputports.write[i].type = Variant::NIL;
- }
- ports_changed_notify();
- notify_property_list_changed();
- return true;
- }
- if (String(p_name).begins_with("output_") && is_output_port_editable()) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, outputports.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- Variant::Type new_type = Variant::Type(int(p_value));
- outputports.write[idx].type = new_type;
- ports_changed_notify();
-
- return true;
- }
-
- if (what == "name") {
- outputports.write[idx].name = p_value;
- ports_changed_notify();
- return true;
- }
- }
-
- if (p_name == "sequenced/sequenced") {
- sequenced = p_value;
- ports_changed_notify();
- return true;
- }
-
- return false;
-}
-
-bool VisualScriptLists::_get(const StringName &p_name, Variant &r_ret) const {
- if (p_name == "input_count" && is_input_port_editable()) {
- r_ret = inputports.size();
- return true;
- }
- if (String(p_name).begins_with("input_") && is_input_port_editable()) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, inputports.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- r_ret = inputports[idx].type;
- return true;
- }
- if (what == "name") {
- r_ret = inputports[idx].name;
- return true;
- }
- }
-
- if (p_name == "output_count" && is_output_port_editable()) {
- r_ret = outputports.size();
- return true;
- }
- if (String(p_name).begins_with("output_") && is_output_port_editable()) {
- int idx = String(p_name).get_slicec('_', 1).get_slicec('/', 0).to_int() - 1;
- ERR_FAIL_INDEX_V(idx, outputports.size(), false);
- String what = String(p_name).get_slice("/", 1);
- if (what == "type") {
- r_ret = outputports[idx].type;
- return true;
- }
- if (what == "name") {
- r_ret = outputports[idx].name;
- return true;
- }
- }
-
- if (p_name == "sequenced/sequenced") {
- r_ret = sequenced;
- return true;
- }
-
- return false;
-}
-
-void VisualScriptLists::_get_property_list(List<PropertyInfo> *p_list) const {
- if (is_input_port_editable()) {
- p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,256"));
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < inputports.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "input_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i + 1) + "/name"));
- }
- }
-
- if (is_output_port_editable()) {
- p_list->push_back(PropertyInfo(Variant::INT, "output_count", PROPERTY_HINT_RANGE, "0,256"));
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- for (int i = 0; i < outputports.size(); i++) {
- p_list->push_back(PropertyInfo(Variant::INT, "output_" + itos(i + 1) + "/type", PROPERTY_HINT_ENUM, argt));
- p_list->push_back(PropertyInfo(Variant::STRING, "output_" + itos(i + 1) + "/name"));
- }
- }
- p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced/sequenced"));
-}
-
-// input data port interaction
-void VisualScriptLists::add_input_data_port(Variant::Type p_type, const String &p_name, int p_index) {
- if (!is_input_port_editable()) {
- return;
- }
-
- Port inp;
- inp.name = p_name;
- inp.type = p_type;
- if (p_index >= 0) {
- inputports.insert(p_index, inp);
- } else {
- inputports.push_back(inp);
- }
-
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::set_input_data_port_type(int p_idx, Variant::Type p_type) {
- if (!is_input_port_type_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_idx, inputports.size());
-
- inputports.write[p_idx].type = p_type;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::set_input_data_port_name(int p_idx, const String &p_name) {
- if (!is_input_port_name_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_idx, inputports.size());
-
- inputports.write[p_idx].name = p_name;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::remove_input_data_port(int p_argidx) {
- if (!is_input_port_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_argidx, inputports.size());
-
- inputports.remove_at(p_argidx);
-
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-// output data port interaction
-void VisualScriptLists::add_output_data_port(Variant::Type p_type, const String &p_name, int p_index) {
- if (!is_output_port_editable()) {
- return;
- }
-
- Port out;
- out.name = p_name;
- out.type = p_type;
- if (p_index >= 0) {
- outputports.insert(p_index, out);
- } else {
- outputports.push_back(out);
- }
-
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::set_output_data_port_type(int p_idx, Variant::Type p_type) {
- if (!is_output_port_type_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_idx, outputports.size());
-
- outputports.write[p_idx].type = p_type;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::set_output_data_port_name(int p_idx, const String &p_name) {
- if (!is_output_port_name_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_idx, outputports.size());
-
- outputports.write[p_idx].name = p_name;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-void VisualScriptLists::remove_output_data_port(int p_argidx) {
- if (!is_output_port_editable()) {
- return;
- }
-
- ERR_FAIL_INDEX(p_argidx, outputports.size());
-
- outputports.remove_at(p_argidx);
-
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-// sequences
-void VisualScriptLists::set_sequenced(bool p_enable) {
- if (sequenced == p_enable) {
- return;
- }
- sequenced = p_enable;
- ports_changed_notify();
-}
-
-bool VisualScriptLists::is_sequenced() const {
- return sequenced;
-}
-
-void VisualScriptLists::reset_state() {
- inputports.clear();
- outputports.clear();
- sequenced = false;
- flags = 0;
-}
-
-VisualScriptLists::VisualScriptLists() {
- // initialize
- sequenced = false;
- flags = 0;
-}
-
-void VisualScriptLists::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_input_data_port", "type", "name", "index"), &VisualScriptLists::add_input_data_port);
- ClassDB::bind_method(D_METHOD("set_input_data_port_name", "index", "name"), &VisualScriptLists::set_input_data_port_name);
- ClassDB::bind_method(D_METHOD("set_input_data_port_type", "index", "type"), &VisualScriptLists::set_input_data_port_type);
- ClassDB::bind_method(D_METHOD("remove_input_data_port", "index"), &VisualScriptLists::remove_input_data_port);
-
- ClassDB::bind_method(D_METHOD("add_output_data_port", "type", "name", "index"), &VisualScriptLists::add_output_data_port);
- ClassDB::bind_method(D_METHOD("set_output_data_port_name", "index", "name"), &VisualScriptLists::set_output_data_port_name);
- ClassDB::bind_method(D_METHOD("set_output_data_port_type", "index", "type"), &VisualScriptLists::set_output_data_port_type);
- ClassDB::bind_method(D_METHOD("remove_output_data_port", "index"), &VisualScriptLists::remove_output_data_port);
-}
-
-//////////////////////////////////////////
-//////////////COMPOSEARRAY////////////////
-//////////////////////////////////////////
-
-int VisualScriptComposeArray::get_output_sequence_port_count() const {
- if (sequenced) {
- return 1;
- }
- return 0;
-}
-
-bool VisualScriptComposeArray::has_input_sequence_port() const {
- return sequenced;
-}
-
-String VisualScriptComposeArray::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-int VisualScriptComposeArray::get_input_value_port_count() const {
- return inputports.size();
-}
-
-int VisualScriptComposeArray::get_output_value_port_count() const {
- return 1;
-}
-
-PropertyInfo VisualScriptComposeArray::get_input_value_port_info(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, inputports.size(), PropertyInfo());
-
- PropertyInfo pi;
- pi.name = inputports[p_idx].name;
- pi.type = inputports[p_idx].type;
- return pi;
-}
-
-PropertyInfo VisualScriptComposeArray::get_output_value_port_info(int p_idx) const {
- PropertyInfo pi;
- pi.name = "out";
- pi.type = Variant::ARRAY;
- return pi;
-}
-
-String VisualScriptComposeArray::get_caption() const {
- return RTR("Compose Array");
-}
-
-String VisualScriptComposeArray::get_text() const {
- return "";
-}
-
-class VisualScriptComposeArrayNode : public VisualScriptNodeInstance {
-public:
- int input_count = 0;
- virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- if (input_count > 0) {
- Array arr;
- for (int i = 0; i < input_count; i++) {
- arr.push_back((*p_inputs[i]));
- }
- Variant va = Variant(arr);
-
- *p_outputs[0] = va;
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptComposeArray::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptComposeArrayNode *instance = memnew(VisualScriptComposeArrayNode);
- instance->input_count = inputports.size();
- return instance;
-}
-
-VisualScriptComposeArray::VisualScriptComposeArray() {
- // initialize stuff here
- sequenced = false;
- flags = INPUT_EDITABLE;
-}
-
-//////////////////////////////////////////
-////////////////OPERATOR//////////////////
-//////////////////////////////////////////
-
-int VisualScriptOperator::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptOperator::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptOperator::get_input_value_port_count() const {
- return (op == Variant::OP_BIT_NEGATE || op == Variant::OP_NOT || op == Variant::OP_NEGATE || op == Variant::OP_POSITIVE) ? 1 : 2;
-}
-
-int VisualScriptOperator::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptOperator::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const {
- static const Variant::Type port_types[Variant::OP_MAX][2] = {
- { Variant::NIL, Variant::NIL }, //OP_EQUAL,
- { Variant::NIL, Variant::NIL }, //OP_NOT_EQUAL,
- { Variant::NIL, Variant::NIL }, //OP_LESS,
- { Variant::NIL, Variant::NIL }, //OP_LESS_EQUAL,
- { Variant::NIL, Variant::NIL }, //OP_GREATER,
- { Variant::NIL, Variant::NIL }, //OP_GREATER_EQUAL,
- //mathematic
- { Variant::NIL, Variant::NIL }, //OP_ADD,
- { Variant::NIL, Variant::NIL }, //OP_SUBTRACT,
- { Variant::NIL, Variant::NIL }, //OP_MULTIPLY,
- { Variant::NIL, Variant::NIL }, //OP_DIVIDE,
- { Variant::NIL, Variant::NIL }, //OP_NEGATE,
- { Variant::NIL, Variant::NIL }, //OP_POSITIVE,
- { Variant::INT, Variant::INT }, //OP_MODULE,
- //bitwise
- { Variant::INT, Variant::INT }, //OP_SHIFT_LEFT,
- { Variant::INT, Variant::INT }, //OP_SHIFT_RIGHT,
- { Variant::INT, Variant::INT }, //OP_BIT_AND,
- { Variant::INT, Variant::INT }, //OP_BIT_OR,
- { Variant::INT, Variant::INT }, //OP_BIT_XOR,
- { Variant::INT, Variant::INT }, //OP_BIT_NEGATE,
- //logic
- { Variant::BOOL, Variant::BOOL }, //OP_AND,
- { Variant::BOOL, Variant::BOOL }, //OP_OR,
- { Variant::BOOL, Variant::BOOL }, //OP_XOR,
- { Variant::BOOL, Variant::BOOL }, //OP_NOT,
- //containment
- { Variant::NIL, Variant::NIL } //OP_IN,
- };
-
- ERR_FAIL_INDEX_V(p_idx, 2, PropertyInfo());
-
- PropertyInfo pinfo;
- pinfo.name = p_idx == 0 ? "A" : "B";
- pinfo.type = port_types[op][p_idx];
- if (pinfo.type == Variant::NIL) {
- pinfo.type = typed;
- }
- return pinfo;
-}
-
-PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
- static const Variant::Type port_types[Variant::OP_MAX] = {
- //comparison
- Variant::BOOL, //OP_EQUAL,
- Variant::BOOL, //OP_NOT_EQUAL,
- Variant::BOOL, //OP_LESS,
- Variant::BOOL, //OP_LESS_EQUAL,
- Variant::BOOL, //OP_GREATER,
- Variant::BOOL, //OP_GREATER_EQUAL,
- //mathematic
- Variant::NIL, //OP_ADD,
- Variant::NIL, //OP_SUBTRACT,
- Variant::NIL, //OP_MULTIPLY,
- Variant::NIL, //OP_DIVIDE,
- Variant::NIL, //OP_NEGATE,
- Variant::NIL, //OP_POSITIVE,
- Variant::INT, //OP_MODULE,
- //bitwise
- Variant::INT, //OP_SHIFT_LEFT,
- Variant::INT, //OP_SHIFT_RIGHT,
- Variant::INT, //OP_BIT_AND,
- Variant::INT, //OP_BIT_OR,
- Variant::INT, //OP_BIT_XOR,
- Variant::INT, //OP_BIT_NEGATE,
- //logic
- Variant::BOOL, //OP_AND,
- Variant::BOOL, //OP_OR,
- Variant::BOOL, //OP_XOR,
- Variant::BOOL, //OP_NOT,
- //containment
- Variant::BOOL //OP_IN,
- };
-
- PropertyInfo pinfo;
- pinfo.name = "";
- pinfo.type = port_types[op];
- if (pinfo.type == Variant::NIL) {
- pinfo.type = typed;
- }
- return pinfo;
-}
-
-String VisualScriptOperator::get_caption() const {
- switch (op) {
- // comparison
- case Variant::OP_EQUAL:
- return U"A = B";
- case Variant::OP_NOT_EQUAL:
- return U"A \u2260 B";
- case Variant::OP_LESS:
- return U"A < B";
- case Variant::OP_LESS_EQUAL:
- return U"A \u2264 B";
- case Variant::OP_GREATER:
- return U"A > B";
- case Variant::OP_GREATER_EQUAL:
- return U"A \u2265 B";
-
- // mathematic
- case Variant::OP_ADD:
- return U"A + B";
- case Variant::OP_SUBTRACT:
- return U"A - B";
- case Variant::OP_MULTIPLY:
- return U"A \u00D7 B";
- case Variant::OP_DIVIDE:
- return U"A \u00F7 B";
- case Variant::OP_NEGATE:
- return U"\u00AC A";
- case Variant::OP_POSITIVE:
- return U"+ A";
- case Variant::OP_MODULE:
- return U"A mod B";
-
- // bitwise
- case Variant::OP_SHIFT_LEFT:
- return U"A << B";
- case Variant::OP_SHIFT_RIGHT:
- return U"A >> B";
- case Variant::OP_BIT_AND:
- return U"A & B";
- case Variant::OP_BIT_OR:
- return U"A | B";
- case Variant::OP_BIT_XOR:
- return U"A ^ B";
- case Variant::OP_BIT_NEGATE:
- return U"~A";
-
- // logic
- case Variant::OP_AND:
- return U"A and B";
- case Variant::OP_OR:
- return U"A or B";
- case Variant::OP_XOR:
- return U"A xor B";
- case Variant::OP_NOT:
- return U"not A";
- case Variant::OP_IN:
- return U"A in B";
-
- default: {
- ERR_FAIL_V_MSG(
- U"Unknown node",
- U"Unknown node type encountered, caption not available.");
- }
- }
-}
-
-String VisualScriptOperator::get_operator_name(Variant::Operator p_op) {
- switch (p_op) {
- // comparison
- case Variant::OP_EQUAL:
- return "Are Equal";
- case Variant::OP_NOT_EQUAL:
- return "Are Not Equal";
- case Variant::OP_LESS:
- return "Less Than";
- case Variant::OP_LESS_EQUAL:
- return "Less Than or Equal";
- case Variant::OP_GREATER:
- return "Greater Than";
- case Variant::OP_GREATER_EQUAL:
- return "Greater Than or Equal";
-
- // mathematic
- case Variant::OP_ADD:
- return "Add";
- case Variant::OP_SUBTRACT:
- return "Subtract";
- case Variant::OP_MULTIPLY:
- return "Multiply";
- case Variant::OP_DIVIDE:
- return "Divide";
- case Variant::OP_NEGATE:
- return "Negate";
- case Variant::OP_POSITIVE:
- return "Positive";
- case Variant::OP_MODULE:
- return "Remainder";
-
- // bitwise
- case Variant::OP_SHIFT_LEFT:
- return "Bit Shift Left";
- case Variant::OP_SHIFT_RIGHT:
- return "Bit Shift Right";
- case Variant::OP_BIT_AND:
- return "Bit And";
- case Variant::OP_BIT_OR:
- return "Bit Or";
- case Variant::OP_BIT_XOR:
- return "Bit Xor";
- case Variant::OP_BIT_NEGATE:
- return "Bit Negate";
-
- // logic
- case Variant::OP_AND:
- return "And";
- case Variant::OP_OR:
- return "Or";
- case Variant::OP_XOR:
- return "Xor";
- case Variant::OP_NOT:
- return "Not";
- case Variant::OP_IN:
- return "In";
-
- default: {
- ERR_FAIL_INDEX_V(p_op, Variant::OP_MAX, "");
- return "Unknown Operator";
- }
- }
-}
-
-void VisualScriptOperator::set_operator(Variant::Operator p_op) {
- if (op == p_op) {
- return;
- }
- op = p_op;
- ports_changed_notify();
-}
-
-Variant::Operator VisualScriptOperator::get_operator() const {
- return op;
-}
-
-void VisualScriptOperator::set_typed(Variant::Type p_op) {
- if (typed == p_op) {
- return;
- }
-
- typed = p_op;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptOperator::get_typed() const {
- return typed;
-}
-
-void VisualScriptOperator::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualScriptOperator::set_operator);
- ClassDB::bind_method(D_METHOD("get_operator"), &VisualScriptOperator::get_operator);
-
- ClassDB::bind_method(D_METHOD("set_typed", "type"), &VisualScriptOperator::set_typed);
- ClassDB::bind_method(D_METHOD("get_typed"), &VisualScriptOperator::get_typed);
-
- String types;
- for (int i = 0; i < Variant::OP_MAX; i++) {
- if (i > 0) {
- types += ",";
- }
- types += get_operator_name(static_cast<Variant::Operator>(i));
- }
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, types), "set_operator", "get_operator");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_typed", "get_typed");
-}
-
-class VisualScriptNodeInstanceOperator : public VisualScriptNodeInstance {
-public:
- bool unary = false;
- Variant::Operator op;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- bool valid;
- if (unary) {
- Variant::evaluate(op, *p_inputs[0], Variant(), *p_outputs[0], valid);
- } else {
- Variant::evaluate(op, *p_inputs[0], *p_inputs[1], *p_outputs[0], valid);
- }
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- if (p_outputs[0]->get_type() == Variant::STRING) {
- 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());
- } 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());
- }
- }
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptOperator::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceOperator *instance = memnew(VisualScriptNodeInstanceOperator);
- instance->unary = get_input_value_port_count() == 1;
- instance->op = op;
- return instance;
-}
-
-VisualScriptOperator::VisualScriptOperator() {
- op = Variant::OP_ADD;
- typed = Variant::NIL;
-}
-
-template <Variant::Operator OP>
-static Ref<VisualScriptNode> create_op_node(const String &p_name) {
- Ref<VisualScriptOperator> node;
- node.instantiate();
- node->set_operator(OP);
- return node;
-}
-
-//////////////////////////////////////////
-////////////////OPERATOR//////////////////
-//////////////////////////////////////////
-
-int VisualScriptSelect::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptSelect::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptSelect::get_input_value_port_count() const {
- return 3;
-}
-
-int VisualScriptSelect::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSelect::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptSelect::get_input_value_port_info(int p_idx) const {
- if (p_idx == 0) {
- return PropertyInfo(Variant::BOOL, "cond");
- } else if (p_idx == 1) {
- return PropertyInfo(typed, "a");
- } else {
- return PropertyInfo(typed, "b");
- }
-}
-
-PropertyInfo VisualScriptSelect::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(typed, "out");
-}
-
-String VisualScriptSelect::get_caption() const {
- return RTR("Select");
-}
-
-String VisualScriptSelect::get_text() const {
- return RTR("a if cond, else b");
-}
-
-void VisualScriptSelect::set_typed(Variant::Type p_op) {
- if (typed == p_op) {
- return;
- }
-
- typed = p_op;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptSelect::get_typed() const {
- return typed;
-}
-
-void VisualScriptSelect::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_typed", "type"), &VisualScriptSelect::set_typed);
- ClassDB::bind_method(D_METHOD("get_typed"), &VisualScriptSelect::get_typed);
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_typed", "get_typed");
-}
-
-class VisualScriptNodeInstanceSelect : public VisualScriptNodeInstance {
-public:
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- bool cond = *p_inputs[0];
- if (cond) {
- *p_outputs[0] = *p_inputs[1];
- } else {
- *p_outputs[0] = *p_inputs[2];
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSelect::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSelect *instance = memnew(VisualScriptNodeInstanceSelect);
- return instance;
-}
-
-VisualScriptSelect::VisualScriptSelect() {
- typed = Variant::NIL;
-}
-
-//////////////////////////////////////////
-////////////////VARIABLE GET//////////////////
-//////////////////////////////////////////
-
-int VisualScriptVariableGet::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptVariableGet::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptVariableGet::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptVariableGet::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptVariableGet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptVariableGet::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptVariableGet::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "value";
- if (get_visual_script().is_valid() && get_visual_script()->has_variable(variable)) {
- PropertyInfo vinfo = get_visual_script()->get_variable_info(variable);
- pinfo.type = vinfo.type;
- pinfo.hint = vinfo.hint;
- pinfo.hint_string = vinfo.hint_string;
- }
- return pinfo;
-}
-
-String VisualScriptVariableGet::get_caption() const {
- return vformat(RTR("Get %s"), variable);
-}
-
-void VisualScriptVariableGet::set_variable(StringName p_variable) {
- if (variable == p_variable) {
- return;
- }
- variable = p_variable;
- ports_changed_notify();
-}
-
-StringName VisualScriptVariableGet::get_variable() const {
- return variable;
-}
-
-void VisualScriptVariableGet::_validate_property(PropertyInfo &property) const {
- if (property.name == "var_name" && get_visual_script().is_valid()) {
- Ref<VisualScript> vs = get_visual_script();
- List<StringName> vars;
- vs->get_variable_list(&vars);
-
- String vhint;
- for (const StringName &E : vars) {
- if (!vhint.is_empty()) {
- vhint += ",";
- }
-
- vhint += E.operator String();
- }
-
- property.hint = PROPERTY_HINT_ENUM;
- property.hint_string = vhint;
- }
-}
-
-void VisualScriptVariableGet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_variable", "name"), &VisualScriptVariableGet::set_variable);
- ClassDB::bind_method(D_METHOD("get_variable"), &VisualScriptVariableGet::get_variable);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_variable", "get_variable");
-}
-
-class VisualScriptNodeInstanceVariableGet : public VisualScriptNodeInstance {
-public:
- 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) + "'";
- return 0;
- }
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptVariableGet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceVariableGet *instance = memnew(VisualScriptNodeInstanceVariableGet);
- instance->node = this;
- instance->instance = p_instance;
- instance->variable = variable;
- return instance;
-}
-
-VisualScriptVariableGet::VisualScriptVariableGet() {
-}
-
-//////////////////////////////////////////
-////////////////VARIABLE SET//////////////////
-//////////////////////////////////////////
-
-int VisualScriptVariableSet::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptVariableSet::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptVariableSet::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptVariableSet::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptVariableSet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptVariableSet::get_input_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = "set";
- if (get_visual_script().is_valid() && get_visual_script()->has_variable(variable)) {
- PropertyInfo vinfo = get_visual_script()->get_variable_info(variable);
- pinfo.type = vinfo.type;
- pinfo.hint = vinfo.hint;
- pinfo.hint_string = vinfo.hint_string;
- }
- return pinfo;
-}
-
-PropertyInfo VisualScriptVariableSet::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptVariableSet::get_caption() const {
- return vformat(RTR("Set %s"), variable);
-}
-
-void VisualScriptVariableSet::set_variable(StringName p_variable) {
- if (variable == p_variable) {
- return;
- }
- variable = p_variable;
- ports_changed_notify();
-}
-
-StringName VisualScriptVariableSet::get_variable() const {
- return variable;
-}
-
-void VisualScriptVariableSet::_validate_property(PropertyInfo &property) const {
- if (property.name == "var_name" && get_visual_script().is_valid()) {
- Ref<VisualScript> vs = get_visual_script();
- List<StringName> vars;
- vs->get_variable_list(&vars);
-
- String vhint;
- for (const StringName &E : vars) {
- if (!vhint.is_empty()) {
- vhint += ",";
- }
-
- vhint += E.operator String();
- }
-
- property.hint = PROPERTY_HINT_ENUM;
- property.hint_string = vhint;
- }
-}
-
-void VisualScriptVariableSet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_variable", "name"), &VisualScriptVariableSet::set_variable);
- ClassDB::bind_method(D_METHOD("get_variable"), &VisualScriptVariableSet::get_variable);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_variable", "get_variable");
-}
-
-class VisualScriptNodeInstanceVariableSet : public VisualScriptNodeInstance {
-public:
- VisualScriptVariableSet *node = nullptr;
- VisualScriptInstance *instance = nullptr;
- StringName variable;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) 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) + "'";
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptVariableSet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceVariableSet *instance = memnew(VisualScriptNodeInstanceVariableSet);
- instance->node = this;
- instance->instance = p_instance;
- instance->variable = variable;
- return instance;
-}
-
-VisualScriptVariableSet::VisualScriptVariableSet() {
-}
-
-//////////////////////////////////////////
-////////////////CONSTANT//////////////////
-//////////////////////////////////////////
-
-int VisualScriptConstant::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptConstant::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptConstant::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptConstant::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptConstant::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptConstant::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptConstant::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.name = String(value);
- pinfo.type = type;
- return pinfo;
-}
-
-String VisualScriptConstant::get_caption() const {
- return RTR("Constant");
-}
-
-void VisualScriptConstant::set_constant_type(Variant::Type p_type) {
- if (type == p_type) {
- return;
- }
-
- type = p_type;
- Callable::CallError ce;
- Variant::construct(type, value, nullptr, 0, ce);
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-Variant::Type VisualScriptConstant::get_constant_type() const {
- return type;
-}
-
-void VisualScriptConstant::set_constant_value(Variant p_value) {
- if (value == p_value) {
- return;
- }
-
- value = p_value;
- ports_changed_notify();
-}
-
-Variant VisualScriptConstant::get_constant_value() const {
- return value;
-}
-
-void VisualScriptConstant::_validate_property(PropertyInfo &property) const {
- if (property.name == "value") {
- property.type = type;
- if (type == Variant::NIL) {
- property.usage = PROPERTY_USAGE_NONE; //do not save if nil
- }
- }
-}
-
-void VisualScriptConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constant_type", "type"), &VisualScriptConstant::set_constant_type);
- ClassDB::bind_method(D_METHOD("get_constant_type"), &VisualScriptConstant::get_constant_type);
-
- ClassDB::bind_method(D_METHOD("set_constant_value", "value"), &VisualScriptConstant::set_constant_value);
- ClassDB::bind_method(D_METHOD("get_constant_value"), &VisualScriptConstant::get_constant_value);
-
- String argt = "Null";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_constant_type", "get_constant_type");
- ADD_PROPERTY(PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT | PROPERTY_USAGE_DEFAULT), "set_constant_value", "get_constant_value");
-}
-
-class VisualScriptNodeInstanceConstant : public VisualScriptNodeInstance {
-public:
- Variant constant;
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- *p_outputs[0] = constant;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptConstant::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceConstant *instance = memnew(VisualScriptNodeInstanceConstant);
- instance->constant = value;
- return instance;
-}
-
-VisualScriptConstant::VisualScriptConstant() {
- type = Variant::NIL;
-}
-
-//////////////////////////////////////////
-////////////////PRELOAD//////////////////
-//////////////////////////////////////////
-
-int VisualScriptPreload::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptPreload::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptPreload::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptPreload::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptPreload::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptPreload::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const {
- PropertyInfo pinfo;
- pinfo.type = Variant::OBJECT;
- if (preload.is_valid()) {
- pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
- pinfo.hint_string = preload->get_class();
- if (preload->get_path().is_resource_file()) {
- pinfo.name = preload->get_path();
- } else if (!preload->get_name().is_empty()) {
- pinfo.name = preload->get_name();
- } else {
- pinfo.name = preload->get_class();
- }
- } else {
- pinfo.name = "<empty>";
- }
-
- return pinfo;
-}
-
-String VisualScriptPreload::get_caption() const {
- return RTR("Preload");
-}
-
-void VisualScriptPreload::set_preload(const Ref<Resource> &p_preload) {
- if (preload == p_preload) {
- return;
- }
-
- preload = p_preload;
- ports_changed_notify();
-}
-
-Ref<Resource> VisualScriptPreload::get_preload() const {
- return preload;
-}
-
-void VisualScriptPreload::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_preload", "resource"), &VisualScriptPreload::set_preload);
- ClassDB::bind_method(D_METHOD("get_preload"), &VisualScriptPreload::get_preload);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), "set_preload", "get_preload");
-}
-
-class VisualScriptNodeInstancePreload : public VisualScriptNodeInstance {
-public:
- Ref<Resource> preload;
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- *p_outputs[0] = preload;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptPreload::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstancePreload *instance = memnew(VisualScriptNodeInstancePreload);
- instance->preload = preload;
- return instance;
-}
-
-VisualScriptPreload::VisualScriptPreload() {
-}
-
-//////////////////////////////////////////
-////////////////INDEX////////////////////
-//////////////////////////////////////////
-
-int VisualScriptIndexGet::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptIndexGet::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptIndexGet::get_input_value_port_count() const {
- return 2;
-}
-
-int VisualScriptIndexGet::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptIndexGet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptIndexGet::get_input_value_port_info(int p_idx) const {
- if (p_idx == 0) {
- return PropertyInfo(Variant::NIL, "base");
- } else {
- return PropertyInfo(Variant::NIL, "index");
- }
-}
-
-PropertyInfo VisualScriptIndexGet::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptIndexGet::get_caption() const {
- return RTR("Get Index");
-}
-
-class VisualScriptNodeInstanceIndexGet : public VisualScriptNodeInstance {
-public:
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- bool valid;
- *p_outputs[0] = p_inputs[0]->get(*p_inputs[1], &valid);
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid get: " + p_inputs[0]->get_construct_string();
- }
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptIndexGet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceIndexGet *instance = memnew(VisualScriptNodeInstanceIndexGet);
- return instance;
-}
-
-VisualScriptIndexGet::VisualScriptIndexGet() {
-}
-
-//////////////////////////////////////////
-////////////////INDEXSET//////////////////
-//////////////////////////////////////////
-
-int VisualScriptIndexSet::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptIndexSet::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptIndexSet::get_input_value_port_count() const {
- return 3;
-}
-
-int VisualScriptIndexSet::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptIndexSet::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptIndexSet::get_input_value_port_info(int p_idx) const {
- if (p_idx == 0) {
- return PropertyInfo(Variant::NIL, "base");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::NIL, "index");
-
- } else {
- return PropertyInfo(Variant::NIL, "value");
- }
-}
-
-PropertyInfo VisualScriptIndexSet::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptIndexSet::get_caption() const {
- return RTR("Set Index");
-}
-
-class VisualScriptNodeInstanceIndexSet : public VisualScriptNodeInstance {
-public:
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- bool valid;
- ((Variant *)p_inputs[0])->set(*p_inputs[1], *p_inputs[2], &valid);
-
- if (!valid) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Invalid set: " + p_inputs[1]->get_construct_string();
- }
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptIndexSet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceIndexSet *instance = memnew(VisualScriptNodeInstanceIndexSet);
- return instance;
-}
-
-VisualScriptIndexSet::VisualScriptIndexSet() {
-}
-
-//////////////////////////////////////////
-////////////////GLOBALCONSTANT///////////
-//////////////////////////////////////////
-
-int VisualScriptGlobalConstant::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptGlobalConstant::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptGlobalConstant::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptGlobalConstant::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptGlobalConstant::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const {
- String name = CoreConstants::get_global_constant_name(index);
- return PropertyInfo(Variant::INT, name);
-}
-
-String VisualScriptGlobalConstant::get_caption() const {
- return RTR("Global Constant");
-}
-
-void VisualScriptGlobalConstant::set_global_constant(int p_which) {
- index = p_which;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-int VisualScriptGlobalConstant::get_global_constant() {
- return index;
-}
-
-class VisualScriptNodeInstanceGlobalConstant : public VisualScriptNodeInstance {
-public:
- 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);
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptGlobalConstant::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceGlobalConstant *instance = memnew(VisualScriptNodeInstanceGlobalConstant);
- instance->index = index;
- return instance;
-}
-
-void VisualScriptGlobalConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_global_constant", "index"), &VisualScriptGlobalConstant::set_global_constant);
- ClassDB::bind_method(D_METHOD("get_global_constant"), &VisualScriptGlobalConstant::get_global_constant);
-
- String cc;
-
- for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
- if (i > 0) {
- cc += ",";
- }
- cc += CoreConstants::get_global_constant_name(i);
- }
- ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_global_constant", "get_global_constant");
-}
-
-VisualScriptGlobalConstant::VisualScriptGlobalConstant() {
- index = 0;
-}
-
-//////////////////////////////////////////
-////////////////CLASSCONSTANT///////////
-//////////////////////////////////////////
-
-int VisualScriptClassConstant::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptClassConstant::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptClassConstant::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptClassConstant::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptClassConstant::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptClassConstant::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptClassConstant::get_output_value_port_info(int p_idx) const {
- if (name == "") {
- return PropertyInfo(Variant::INT, String(base_type));
- } else {
- return PropertyInfo(Variant::INT, String(base_type) + "." + String(name));
- }
-}
-
-String VisualScriptClassConstant::get_caption() const {
- return RTR("Class Constant");
-}
-
-void VisualScriptClassConstant::set_class_constant(const StringName &p_which) {
- name = p_which;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptClassConstant::get_class_constant() {
- return name;
-}
-
-void VisualScriptClassConstant::set_base_type(const StringName &p_which) {
- base_type = p_which;
- List<String> constants;
- ClassDB::get_integer_constant_list(base_type, &constants, true);
- if (constants.size() > 0) {
- bool found_name = false;
- for (const String &E : constants) {
- if (E == name) {
- found_name = true;
- break;
- }
- }
- if (!found_name) {
- name = constants[0];
- }
- } else {
- name = "";
- }
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptClassConstant::get_base_type() {
- return base_type;
-}
-
-class VisualScriptNodeInstanceClassConstant : public VisualScriptNodeInstance {
-public:
- 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) {
- r_error_str = "Invalid constant name, pick a valid class constant.";
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- }
-
- *p_outputs[0] = value;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptClassConstant::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceClassConstant *instance = memnew(VisualScriptNodeInstanceClassConstant);
- instance->value = ClassDB::get_integer_constant(base_type, name, &instance->valid);
- return instance;
-}
-
-void VisualScriptClassConstant::_validate_property(PropertyInfo &property) const {
- if (property.name == "constant") {
- List<String> constants;
- ClassDB::get_integer_constant_list(base_type, &constants, true);
-
- property.hint_string = "";
- for (const String &E : constants) {
- if (!property.hint_string.is_empty()) {
- property.hint_string += ",";
- }
- property.hint_string += E;
- }
- }
-}
-
-void VisualScriptClassConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_class_constant", "name"), &VisualScriptClassConstant::set_class_constant);
- ClassDB::bind_method(D_METHOD("get_class_constant"), &VisualScriptClassConstant::get_class_constant);
-
- ClassDB::bind_method(D_METHOD("set_base_type", "name"), &VisualScriptClassConstant::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptClassConstant::get_base_type);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant", PROPERTY_HINT_ENUM, ""), "set_class_constant", "get_class_constant");
-}
-
-VisualScriptClassConstant::VisualScriptClassConstant() {
- base_type = "Object";
-}
-
-//////////////////////////////////////////
-////////////////BASICTYPECONSTANT///////////
-//////////////////////////////////////////
-
-int VisualScriptBasicTypeConstant::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptBasicTypeConstant::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptBasicTypeConstant::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptBasicTypeConstant::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptBasicTypeConstant::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptBasicTypeConstant::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptBasicTypeConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, "value");
-}
-
-String VisualScriptBasicTypeConstant::get_caption() const {
- return RTR("Basic Constant");
-}
-
-String VisualScriptBasicTypeConstant::get_text() const {
- if (name == "") {
- return Variant::get_type_name(type);
- } else {
- return Variant::get_type_name(type) + "." + String(name);
- }
-}
-
-void VisualScriptBasicTypeConstant::set_basic_type_constant(const StringName &p_which) {
- name = p_which;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptBasicTypeConstant::get_basic_type_constant() const {
- return name;
-}
-
-void VisualScriptBasicTypeConstant::set_basic_type(Variant::Type p_which) {
- type = p_which;
-
- List<StringName> constants;
- Variant::get_constants_for_type(type, &constants);
- if (constants.size() > 0) {
- bool found_name = false;
- for (const StringName &E : constants) {
- if (E == name) {
- found_name = true;
- break;
- }
- }
- if (!found_name) {
- name = constants[0];
- }
- } else {
- name = "";
- }
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptBasicTypeConstant::get_basic_type() const {
- return type;
-}
-
-class VisualScriptNodeInstanceBasicTypeConstant : public VisualScriptNodeInstance {
-public:
- Variant value;
- 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) {
- r_error_str = "Invalid constant name, pick a valid basic type constant.";
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- }
-
- *p_outputs[0] = value;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceBasicTypeConstant *instance = memnew(VisualScriptNodeInstanceBasicTypeConstant);
- instance->value = Variant::get_constant_value(type, name, &instance->valid);
- return instance;
-}
-
-void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) const {
- if (property.name == "constant") {
- List<StringName> constants;
- Variant::get_constants_for_type(type, &constants);
-
- if (constants.size() == 0) {
- property.usage = PROPERTY_USAGE_NONE;
- return;
- }
- property.hint_string = "";
- for (const StringName &E : constants) {
- if (!property.hint_string.is_empty()) {
- property.hint_string += ",";
- }
- property.hint_string += String(E);
- }
- }
-}
-
-void VisualScriptBasicTypeConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_basic_type", "name"), &VisualScriptBasicTypeConstant::set_basic_type);
- ClassDB::bind_method(D_METHOD("get_basic_type"), &VisualScriptBasicTypeConstant::get_basic_type);
-
- ClassDB::bind_method(D_METHOD("set_basic_type_constant", "name"), &VisualScriptBasicTypeConstant::set_basic_type_constant);
- ClassDB::bind_method(D_METHOD("get_basic_type_constant"), &VisualScriptBasicTypeConstant::get_basic_type_constant);
-
- String argt = "Null";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, argt), "set_basic_type", "get_basic_type");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant", PROPERTY_HINT_ENUM, ""), "set_basic_type_constant", "get_basic_type_constant");
-}
-
-VisualScriptBasicTypeConstant::VisualScriptBasicTypeConstant() {
- type = Variant::NIL;
-}
-
-//////////////////////////////////////////
-////////////////MATHCONSTANT///////////
-//////////////////////////////////////////
-
-const char *VisualScriptMathConstant::const_name[MATH_CONSTANT_MAX] = {
- "One",
- "PI",
- "PI/2",
- "TAU",
- "E",
- "Sqrt2",
- "INF",
- "NAN"
-};
-
-double VisualScriptMathConstant::const_value[MATH_CONSTANT_MAX] = {
- 1.0,
- Math_PI,
- Math_PI * 0.5,
- Math_TAU,
- 2.71828182845904523536,
- Math::sqrt(2.0),
- INFINITY,
- NAN
-};
-
-int VisualScriptMathConstant::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptMathConstant::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptMathConstant::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptMathConstant::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptMathConstant::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptMathConstant::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptMathConstant::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::FLOAT, const_name[constant]);
-}
-
-String VisualScriptMathConstant::get_caption() const {
- return RTR("Math Constant");
-}
-
-void VisualScriptMathConstant::set_math_constant(MathConstant p_which) {
- constant = p_which;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptMathConstant::MathConstant VisualScriptMathConstant::get_math_constant() {
- return constant;
-}
-
-class VisualScriptNodeInstanceMathConstant : public VisualScriptNodeInstance {
-public:
- 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;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptMathConstant::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceMathConstant *instance = memnew(VisualScriptNodeInstanceMathConstant);
- instance->value = const_value[constant];
- return instance;
-}
-
-void VisualScriptMathConstant::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_math_constant", "which"), &VisualScriptMathConstant::set_math_constant);
- ClassDB::bind_method(D_METHOD("get_math_constant"), &VisualScriptMathConstant::get_math_constant);
-
- String cc;
-
- for (int i = 0; i < MATH_CONSTANT_MAX; i++) {
- if (i > 0) {
- cc += ",";
- }
- cc += const_name[i];
- }
- ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_math_constant", "get_math_constant");
-
- BIND_ENUM_CONSTANT(MATH_CONSTANT_ONE);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_PI);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_HALF_PI);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_TAU);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_E);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_SQRT2);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_INF);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_NAN);
- BIND_ENUM_CONSTANT(MATH_CONSTANT_MAX);
-}
-
-VisualScriptMathConstant::VisualScriptMathConstant() {
- constant = MATH_CONSTANT_ONE;
-}
-
-//////////////////////////////////////////
-////////////////ENGINESINGLETON///////////
-//////////////////////////////////////////
-
-int VisualScriptEngineSingleton::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptEngineSingleton::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptEngineSingleton::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptEngineSingleton::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptEngineSingleton::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptEngineSingleton::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptEngineSingleton::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, singleton);
-}
-
-String VisualScriptEngineSingleton::get_caption() const {
- return RTR("Get Engine Singleton");
-}
-
-void VisualScriptEngineSingleton::set_singleton(const String &p_string) {
- singleton = p_string;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptEngineSingleton::get_singleton() {
- return singleton;
-}
-
-class VisualScriptNodeInstanceEngineSingleton : public VisualScriptNodeInstance {
-public:
- Object *singleton = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- *p_outputs[0] = singleton;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptEngineSingleton::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceEngineSingleton *instance = memnew(VisualScriptNodeInstanceEngineSingleton);
- instance->singleton = Engine::get_singleton()->get_singleton_object(singleton);
- return instance;
-}
-
-VisualScriptEngineSingleton::TypeGuess VisualScriptEngineSingleton::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- Object *obj = Engine::get_singleton()->get_singleton_object(singleton);
- TypeGuess tg;
- tg.type = Variant::OBJECT;
- if (obj) {
- tg.gdclass = obj->get_class();
- tg.script = obj->get_script();
- }
-
- return tg;
-}
-
-void VisualScriptEngineSingleton::_validate_property(PropertyInfo &property) const {
- String cc;
-
- List<Engine::Singleton> singletons;
-
- Engine::get_singleton()->get_singletons(&singletons);
-
- for (const Engine::Singleton &E : singletons) {
- if (E.name == "VS" || E.name == "PS" || E.name == "PS2D" || E.name == "AS" || E.name == "TS" || E.name == "SS" || E.name == "SS2D") {
- continue; //skip these, too simple named
- }
-
- if (!cc.is_empty()) {
- cc += ",";
- }
- cc += E.name;
- }
-
- property.hint = PROPERTY_HINT_ENUM;
- property.hint_string = cc;
-}
-
-void VisualScriptEngineSingleton::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_singleton", "name"), &VisualScriptEngineSingleton::set_singleton);
- ClassDB::bind_method(D_METHOD("get_singleton"), &VisualScriptEngineSingleton::get_singleton);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "constant"), "set_singleton", "get_singleton");
-}
-
-VisualScriptEngineSingleton::VisualScriptEngineSingleton() {
- singleton = String();
-}
-
-//////////////////////////////////////////
-////////////////GETNODE///////////
-//////////////////////////////////////////
-
-int VisualScriptSceneNode::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptSceneNode::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptSceneNode::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptSceneNode::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSceneNode::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptSceneNode::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptSceneNode::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, path.simplified());
-}
-
-String VisualScriptSceneNode::get_caption() const {
- return RTR("Get Scene Node");
-}
-
-void VisualScriptSceneNode::set_node_path(const NodePath &p_path) {
- path = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-NodePath VisualScriptSceneNode::get_node_path() {
- return path;
-}
-
-class VisualScriptNodeInstanceSceneNode : public VisualScriptNodeInstance {
-public:
- VisualScriptSceneNode *node = nullptr;
- VisualScriptInstance *instance = nullptr;
- NodePath path;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Base object is not a Node!";
- return 0;
- }
-
- Node *another = node->get_node(path);
- if (!another) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Path does not lead Node!";
- return 0;
- }
-
- *p_outputs[0] = another;
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSceneNode::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSceneNode *instance = memnew(VisualScriptNodeInstanceSceneNode);
- instance->node = this;
- instance->instance = p_instance;
- instance->path = path;
- return instance;
-}
-
-#ifdef TOOLS_ENABLED
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
- if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
- return nullptr;
- }
-
- Ref<Script> scr = p_current_node->get_script();
-
- if (scr.is_valid() && scr == script) {
- return p_current_node;
- }
-
- for (int i = 0; i < p_current_node->get_child_count(); i++) {
- Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
- if (n) {
- return n;
- }
- }
-
- return nullptr;
-}
-
-#endif
-
-VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- VisualScriptSceneNode::TypeGuess tg;
- tg.type = Variant::OBJECT;
- tg.gdclass = SNAME("Node");
-
-#ifdef TOOLS_ENABLED
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return tg;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return tg;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return tg;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return tg;
- }
-
- Node *another = script_node->get_node(path);
-
- if (another) {
- tg.gdclass = another->get_class();
- tg.script = another->get_script();
- }
-#endif
- return tg;
-}
-
-void VisualScriptSceneNode::_validate_property(PropertyInfo &property) const {
-#ifdef TOOLS_ENABLED
- if (property.name == "node_path") {
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return;
- }
-
- property.hint_string = script_node->get_path();
- }
-#endif
-}
-
-void VisualScriptSceneNode::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_node_path", "path"), &VisualScriptSceneNode::set_node_path);
- ClassDB::bind_method(D_METHOD("get_node_path"), &VisualScriptSceneNode::get_node_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_node_path", "get_node_path");
-}
-
-VisualScriptSceneNode::VisualScriptSceneNode() {
- path = String(".");
-}
-
-//////////////////////////////////////////
-////////////////SceneTree///////////
-//////////////////////////////////////////
-
-int VisualScriptSceneTree::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptSceneTree::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptSceneTree::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptSceneTree::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSceneTree::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::OBJECT, "Scene Tree", PROPERTY_HINT_TYPE_STRING, "SceneTree");
-}
-
-String VisualScriptSceneTree::get_caption() const {
- return RTR("Get Scene Tree");
-}
-
-class VisualScriptNodeInstanceSceneTree : public VisualScriptNodeInstance {
-public:
- VisualScriptSceneTree *node = nullptr;
- VisualScriptInstance *instance = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Base object is not a Node!";
- return 0;
- }
-
- SceneTree *tree = node->get_tree();
- if (!tree) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Attempt to get SceneTree while node is not in the active tree.";
- return 0;
- }
-
- *p_outputs[0] = tree;
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSceneTree::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSceneTree *instance = memnew(VisualScriptNodeInstanceSceneTree);
- instance->node = this;
- instance->instance = p_instance;
- return instance;
-}
-
-VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- TypeGuess tg;
- tg.type = Variant::OBJECT;
- tg.gdclass = SNAME("SceneTree");
- return tg;
-}
-
-void VisualScriptSceneTree::_validate_property(PropertyInfo &property) const {
-}
-
-void VisualScriptSceneTree::_bind_methods() {
-}
-
-VisualScriptSceneTree::VisualScriptSceneTree() {
-}
-
-//////////////////////////////////////////
-////////////////RESPATH///////////
-//////////////////////////////////////////
-
-int VisualScriptResourcePath::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptResourcePath::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptResourcePath::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptResourcePath::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptResourcePath::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptResourcePath::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptResourcePath::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(Variant::STRING, path);
-}
-
-String VisualScriptResourcePath::get_caption() const {
- return RTR("Resource Path");
-}
-
-void VisualScriptResourcePath::set_resource_path(const String &p_path) {
- path = p_path;
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-String VisualScriptResourcePath::get_resource_path() {
- return path;
-}
-
-class VisualScriptNodeInstanceResourcePath : public VisualScriptNodeInstance {
-public:
- String path;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- *p_outputs[0] = path;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptResourcePath::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceResourcePath *instance = memnew(VisualScriptNodeInstanceResourcePath);
- instance->path = path;
- return instance;
-}
-
-void VisualScriptResourcePath::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_resource_path", "path"), &VisualScriptResourcePath::set_resource_path);
- ClassDB::bind_method(D_METHOD("get_resource_path"), &VisualScriptResourcePath::get_resource_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "path", PROPERTY_HINT_FILE), "set_resource_path", "get_resource_path");
-}
-
-VisualScriptResourcePath::VisualScriptResourcePath() {
- path = "";
-}
-
-//////////////////////////////////////////
-////////////////SELF///////////
-//////////////////////////////////////////
-
-int VisualScriptSelf::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptSelf::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptSelf::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptSelf::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSelf::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const {
- StringName type_name;
- if (get_visual_script().is_valid()) {
- type_name = get_visual_script()->get_instance_base_type();
- } else {
- type_name = SNAME("instance");
- }
-
- return PropertyInfo(Variant::OBJECT, type_name);
-}
-
-String VisualScriptSelf::get_caption() const {
- return RTR("Get Self");
-}
-
-class VisualScriptNodeInstanceSelf : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- *p_outputs[0] = instance->get_owner_ptr();
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSelf::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSelf *instance = memnew(VisualScriptNodeInstanceSelf);
- instance->instance = p_instance;
- return instance;
-}
-
-VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- VisualScriptSceneNode::TypeGuess tg;
- tg.type = Variant::OBJECT;
- tg.gdclass = SNAME("Object");
-
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return tg;
- }
-
- tg.gdclass = script->get_instance_base_type();
- tg.script = script;
-
- return tg;
-}
-
-void VisualScriptSelf::_bind_methods() {
-}
-
-VisualScriptSelf::VisualScriptSelf() {
-}
-
-//////////////////////////////////////////
-////////////////CUSTOM (SCRIPTED)///////////
-//////////////////////////////////////////
-
-int VisualScriptCustomNode::get_output_sequence_port_count() const {
- int ret;
- if (GDVIRTUAL_CALL(_get_output_sequence_port_count, ret)) {
- return ret;
- }
- return 0;
-}
-
-bool VisualScriptCustomNode::has_input_sequence_port() const {
- bool ret;
- if (GDVIRTUAL_CALL(_has_input_sequence_port, ret)) {
- return ret;
- }
- return false;
-}
-
-int VisualScriptCustomNode::get_input_value_port_count() const {
- int ret;
- if (GDVIRTUAL_CALL(_get_input_value_port_count, ret)) {
- return ret;
- }
- return 0;
-}
-
-int VisualScriptCustomNode::get_output_value_port_count() const {
- int ret;
- if (GDVIRTUAL_CALL(_get_output_value_port_count, ret)) {
- return ret;
- }
- return 0;
-}
-
-String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const {
- String ret;
- if (GDVIRTUAL_CALL(_get_output_sequence_port_text, p_port, ret)) {
- return ret;
- }
-
- return String();
-}
-
-PropertyInfo VisualScriptCustomNode::get_input_value_port_info(int p_idx) const {
- PropertyInfo info;
- {
- int type;
- if (GDVIRTUAL_CALL(_get_input_value_port_type, p_idx, type)) {
- info.type = Variant::Type(type);
- }
- }
- {
- String name;
- if (GDVIRTUAL_CALL(_get_input_value_port_name, p_idx, name)) {
- info.name = name;
- }
- }
- {
- int hint;
- if (GDVIRTUAL_CALL(_get_input_value_port_hint, p_idx, hint)) {
- info.hint = PropertyHint(hint);
- }
- }
-
- {
- String hint_string;
- if (GDVIRTUAL_CALL(_get_input_value_port_hint_string, p_idx, hint_string)) {
- info.hint_string = hint_string;
- }
- }
-
- return info;
-}
-
-PropertyInfo VisualScriptCustomNode::get_output_value_port_info(int p_idx) const {
- PropertyInfo info;
- {
- int type;
- if (GDVIRTUAL_CALL(_get_output_value_port_type, p_idx, type)) {
- info.type = Variant::Type(type);
- }
- }
- {
- String name;
- if (GDVIRTUAL_CALL(_get_output_value_port_name, p_idx, name)) {
- info.name = name;
- }
- }
- {
- int hint;
- if (GDVIRTUAL_CALL(_get_output_value_port_hint, p_idx, hint)) {
- info.hint = PropertyHint(hint);
- }
- }
-
- {
- String hint_string;
- if (GDVIRTUAL_CALL(_get_output_value_port_hint_string, p_idx, hint_string)) {
- info.hint_string = hint_string;
- }
- }
- return info;
-}
-
-VisualScriptCustomNode::TypeGuess VisualScriptCustomNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
- TypeGuess tg;
- PropertyInfo pi = VisualScriptCustomNode::get_output_value_port_info(p_output);
- tg.type = pi.type;
- if (pi.type == Variant::OBJECT) {
- if (pi.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- if (pi.hint_string.is_resource_file()) {
- tg.script = ResourceLoader::load(pi.hint_string);
- } else if (ClassDB::class_exists(pi.hint_string)) {
- tg.gdclass = pi.hint_string;
- }
- }
- }
- return tg;
-}
-
-String VisualScriptCustomNode::get_caption() const {
- String ret;
- if (GDVIRTUAL_CALL(_get_caption, ret)) {
- return ret;
- }
- return RTR("CustomNode");
-}
-
-String VisualScriptCustomNode::get_text() const {
- String ret;
- if (GDVIRTUAL_CALL(_get_text, ret)) {
- return ret;
- }
- return "";
-}
-
-String VisualScriptCustomNode::get_category() const {
- String ret;
- if (GDVIRTUAL_CALL(_get_category, ret)) {
- return ret;
- }
- return "Custom";
-}
-
-class VisualScriptNodeInstanceCustomNode : public VisualScriptNodeInstance {
-public:
- 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 {
- if (GDVIRTUAL_IS_OVERRIDDEN_PTR(node, _step)) {
- Array in_values;
- Array out_values;
- Array work_mem;
-
- in_values.resize(in_count);
-
- for (int i = 0; i < in_count; i++) {
- in_values[i] = *p_inputs[i];
- }
-
- out_values.resize(out_count);
-
- work_mem.resize(work_mem_size);
-
- for (int i = 0; i < work_mem_size; i++) {
- work_mem[i] = p_working_mem[i];
- }
-
- int ret_out;
-
- Variant ret;
- GDVIRTUAL_CALL_PTR(node, _step, in_values, out_values, p_start_mode, work_mem, ret);
- if (ret.get_type() == Variant::STRING) {
- r_error_str = ret;
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- } else if (ret.is_num()) {
- ret_out = ret;
- } else {
- r_error_str = RTR("Invalid return value from _step(), must be integer (seq out), or string (error).");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
-
- for (int i = 0; i < out_count; i++) {
- if (i < out_values.size()) {
- *p_outputs[i] = out_values[i];
- }
- }
-
- for (int i = 0; i < work_mem_size; i++) {
- if (i < work_mem.size()) {
- p_working_mem[i] = work_mem[i];
- }
- }
-
- return ret_out;
- } else {
- r_error_str = RTR("Custom node has no _step() method, can't process graph.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptCustomNode::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceCustomNode *instance = memnew(VisualScriptNodeInstanceCustomNode);
- instance->instance = p_instance;
- instance->node = this;
- instance->in_count = get_input_value_port_count();
- instance->out_count = get_output_value_port_count();
-
- instance->work_mem_size = 0;
- GDVIRTUAL_CALL(_get_working_memory_size, instance->work_mem_size);
-
- return instance;
-}
-
-void VisualScriptCustomNode::_script_changed() {
- call_deferred(SNAME("ports_changed_notify"));
-}
-
-void VisualScriptCustomNode::_bind_methods() {
- GDVIRTUAL_BIND(_get_output_sequence_port_count);
- GDVIRTUAL_BIND(_has_input_sequence_port);
- GDVIRTUAL_BIND(_get_output_sequence_port_text, "seq_idx");
-
- GDVIRTUAL_BIND(_get_input_value_port_count);
- GDVIRTUAL_BIND(_get_input_value_port_type, "input_idx");
- GDVIRTUAL_BIND(_get_input_value_port_name, "input_idx");
- GDVIRTUAL_BIND(_get_input_value_port_hint, "input_idx");
- GDVIRTUAL_BIND(_get_input_value_port_hint_string, "input_idx");
-
- GDVIRTUAL_BIND(_get_output_value_port_count);
- GDVIRTUAL_BIND(_get_output_value_port_type, "output_idx");
- GDVIRTUAL_BIND(_get_output_value_port_name, "output_idx");
- GDVIRTUAL_BIND(_get_output_value_port_hint, "output_idx");
- GDVIRTUAL_BIND(_get_output_value_port_hint_string, "output_idx");
-
- GDVIRTUAL_BIND(_get_caption);
- GDVIRTUAL_BIND(_get_text);
- GDVIRTUAL_BIND(_get_category);
-
- GDVIRTUAL_BIND(_get_working_memory_size);
-
- GDVIRTUAL_BIND(_step, "inputs", "outputs", "start_mode", "working_mem");
-
- BIND_ENUM_CONSTANT(START_MODE_BEGIN_SEQUENCE);
- BIND_ENUM_CONSTANT(START_MODE_CONTINUE_SEQUENCE);
- BIND_ENUM_CONSTANT(START_MODE_RESUME_YIELD);
-
- BIND_CONSTANT(STEP_PUSH_STACK_BIT);
- BIND_CONSTANT(STEP_GO_BACK_BIT);
- BIND_CONSTANT(STEP_NO_ADVANCE_BIT);
- BIND_CONSTANT(STEP_EXIT_FUNCTION_BIT);
- BIND_CONSTANT(STEP_YIELD_BIT);
-}
-
-VisualScriptCustomNode::VisualScriptCustomNode() {
- connect("script_changed", callable_mp(this, &VisualScriptCustomNode::_script_changed));
-}
-
-//////////////////////////////////////////
-////////////////SUBCALL///////////
-//////////////////////////////////////////
-
-int VisualScriptSubCall::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptSubCall::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptSubCall::get_input_value_port_count() const {
- Ref<Script> script = get_script();
-
- if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
- MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
- return mi.arguments.size();
- }
-
- return 0;
-}
-
-int VisualScriptSubCall::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptSubCall::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptSubCall::get_input_value_port_info(int p_idx) const {
- Ref<Script> script = get_script();
- if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
- MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
- return mi.arguments[p_idx];
- }
-
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptSubCall::get_output_value_port_info(int p_idx) const {
- Ref<Script> script = get_script();
- if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
- MethodInfo mi = script->get_method_info(VisualScriptLanguage::singleton->_subcall);
- return mi.return_val;
- }
- return PropertyInfo();
-}
-
-String VisualScriptSubCall::get_caption() const {
- return RTR("SubCall");
-}
-
-String VisualScriptSubCall::get_text() const {
- Ref<Script> script = get_script();
- if (script.is_valid()) {
- if (!script->get_name().is_empty()) {
- return script->get_name();
- }
- if (script->get_path().is_resource_file()) {
- return script->get_path().get_file();
- }
- return script->get_class();
- }
- return "";
-}
-
-String VisualScriptSubCall::get_category() const {
- return "custom";
-}
-
-class VisualScriptNodeInstanceSubCall : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- VisualScriptSubCall *subcall = nullptr;
- int input_args = 0;
- bool valid = false;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- if (!valid) {
- r_error_str = "Node requires a script with a _subcall(<args>) method to work.";
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
- *p_outputs[0] = subcall->callp(VisualScriptLanguage::singleton->_subcall, p_inputs, input_args, r_error);
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptSubCall::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceSubCall *instance = memnew(VisualScriptNodeInstanceSubCall);
- instance->instance = p_instance;
- Ref<Script> script = get_script();
- if (script.is_valid() && script->has_method(VisualScriptLanguage::singleton->_subcall)) {
- instance->valid = true;
- instance->input_args = get_input_value_port_count();
- } else {
- instance->valid = false;
- }
- return instance;
-}
-
-void VisualScriptSubCall::_bind_methods() {
- // Since this is script only, registering virtual function is no longer valid. Will have to go in docs.
-}
-
-VisualScriptSubCall::VisualScriptSubCall() {
-}
-
-//////////////////////////////////////////
-////////////////Comment///////////
-//////////////////////////////////////////
-
-int VisualScriptComment::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptComment::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptComment::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptComment::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptComment::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptComment::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptComment::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptComment::get_caption() const {
- return title;
-}
-
-String VisualScriptComment::get_text() const {
- return description;
-}
-
-void VisualScriptComment::set_title(const String &p_title) {
- if (title == p_title) {
- return;
- }
- title = p_title;
- ports_changed_notify();
-}
-
-String VisualScriptComment::get_title() const {
- return title;
-}
-
-void VisualScriptComment::set_description(const String &p_description) {
- if (description == p_description) {
- return;
- }
- description = p_description;
- ports_changed_notify();
-}
-
-String VisualScriptComment::get_description() const {
- return description;
-}
-
-void VisualScriptComment::set_size(const Size2 &p_size) {
- if (size == p_size) {
- return;
- }
- size = p_size;
- ports_changed_notify();
-}
-
-Size2 VisualScriptComment::get_size() const {
- return size;
-}
-
-String VisualScriptComment::get_category() const {
- return "data";
-}
-
-class VisualScriptNodeInstanceComment : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptComment::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceComment *instance = memnew(VisualScriptNodeInstanceComment);
- instance->instance = p_instance;
- return instance;
-}
-
-void VisualScriptComment::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_title", "title"), &VisualScriptComment::set_title);
- ClassDB::bind_method(D_METHOD("get_title"), &VisualScriptComment::get_title);
-
- ClassDB::bind_method(D_METHOD("set_description", "description"), &VisualScriptComment::set_description);
- ClassDB::bind_method(D_METHOD("get_description"), &VisualScriptComment::get_description);
-
- ClassDB::bind_method(D_METHOD("set_size", "size"), &VisualScriptComment::set_size);
- ClassDB::bind_method(D_METHOD("get_size"), &VisualScriptComment::get_size);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
-}
-
-VisualScriptComment::VisualScriptComment() {
- title = "Comment";
- size = Size2(150, 150);
-}
-
-//////////////////////////////////////////
-////////////////Constructor///////////
-//////////////////////////////////////////
-
-int VisualScriptConstructor::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptConstructor::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptConstructor::get_input_value_port_count() const {
- return constructor.arguments.size();
-}
-
-int VisualScriptConstructor::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptConstructor::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-PropertyInfo VisualScriptConstructor::get_input_value_port_info(int p_idx) const {
- return constructor.arguments[p_idx];
-}
-
-PropertyInfo VisualScriptConstructor::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, "value");
-}
-
-String VisualScriptConstructor::get_caption() const {
- return vformat(RTR("Construct %s"), Variant::get_type_name(type));
-}
-
-String VisualScriptConstructor::get_category() const {
- return "functions";
-}
-
-void VisualScriptConstructor::set_constructor_type(Variant::Type p_type) {
- if (type == p_type) {
- return;
- }
-
- type = p_type;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptConstructor::get_constructor_type() const {
- return type;
-}
-
-void VisualScriptConstructor::set_constructor(const Dictionary &p_info) {
- constructor = MethodInfo::from_dict(p_info);
- ports_changed_notify();
-}
-
-Dictionary VisualScriptConstructor::get_constructor() const {
- return constructor;
-}
-
-class VisualScriptNodeInstanceConstructor : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- Variant::Type type;
- int argcount = 0;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- Callable::CallError ce;
- Variant::construct(type, *p_outputs[0], p_inputs, argcount, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- r_error_str = "Invalid arguments for constructor";
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptConstructor::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceConstructor *instance = memnew(VisualScriptNodeInstanceConstructor);
- instance->instance = p_instance;
- instance->type = type;
- instance->argcount = constructor.arguments.size();
- return instance;
-}
-
-void VisualScriptConstructor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_constructor_type", "type"), &VisualScriptConstructor::set_constructor_type);
- ClassDB::bind_method(D_METHOD("get_constructor_type"), &VisualScriptConstructor::get_constructor_type);
-
- ClassDB::bind_method(D_METHOD("set_constructor", "constructor"), &VisualScriptConstructor::set_constructor);
- ClassDB::bind_method(D_METHOD("get_constructor"), &VisualScriptConstructor::get_constructor);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor_type", "get_constructor_type");
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "constructor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor", "get_constructor");
-}
-
-VisualScriptConstructor::VisualScriptConstructor() {
- type = Variant::NIL;
-}
-
-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>());
-
- Ref<VisualScriptConstructor> vsc;
- vsc.instantiate();
- vsc->set_constructor_type(constructor_map[p_name].first);
- vsc->set_constructor(constructor_map[p_name].second);
-
- return vsc;
-}
-
-//////////////////////////////////////////
-////////////////LocalVar///////////
-//////////////////////////////////////////
-
-int VisualScriptLocalVar::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptLocalVar::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptLocalVar::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptLocalVar::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptLocalVar::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-PropertyInfo VisualScriptLocalVar::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptLocalVar::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, name);
-}
-
-String VisualScriptLocalVar::get_caption() const {
- return RTR("Get Local Var");
-}
-
-String VisualScriptLocalVar::get_category() const {
- return "data";
-}
-
-void VisualScriptLocalVar::set_var_name(const StringName &p_name) {
- if (name == p_name) {
- return;
- }
-
- name = p_name;
- ports_changed_notify();
-}
-
-StringName VisualScriptLocalVar::get_var_name() const {
- return name;
-}
-
-void VisualScriptLocalVar::set_var_type(Variant::Type p_type) {
- type = p_type;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptLocalVar::get_var_type() const {
- return type;
-}
-
-class VisualScriptNodeInstanceLocalVar : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- StringName name;
-
- virtual int get_working_memory_size() const override { return 1; }
- 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] = *p_working_mem;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptLocalVar::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceLocalVar *instance = memnew(VisualScriptNodeInstanceLocalVar);
- instance->instance = p_instance;
- instance->name = name;
-
- return instance;
-}
-
-void VisualScriptLocalVar::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_var_name", "name"), &VisualScriptLocalVar::set_var_name);
- ClassDB::bind_method(D_METHOD("get_var_name"), &VisualScriptLocalVar::get_var_name);
-
- ClassDB::bind_method(D_METHOD("set_var_type", "type"), &VisualScriptLocalVar::set_var_type);
- ClassDB::bind_method(D_METHOD("get_var_type"), &VisualScriptLocalVar::get_var_type);
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_var_name", "get_var_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
-}
-
-VisualScriptLocalVar::VisualScriptLocalVar() {
- name = "new_local";
- type = Variant::NIL;
-}
-
-//////////////////////////////////////////
-////////////////LocalVar///////////
-//////////////////////////////////////////
-
-int VisualScriptLocalVarSet::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptLocalVarSet::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptLocalVarSet::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptLocalVarSet::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptLocalVarSet::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-PropertyInfo VisualScriptLocalVarSet::get_input_value_port_info(int p_idx) const {
- return PropertyInfo(type, "set");
-}
-
-PropertyInfo VisualScriptLocalVarSet::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(type, "get");
-}
-
-String VisualScriptLocalVarSet::get_caption() const {
- return RTR("Set Local Var");
-}
-
-String VisualScriptLocalVarSet::get_text() const {
- return name;
-}
-
-String VisualScriptLocalVarSet::get_category() const {
- return "data";
-}
-
-void VisualScriptLocalVarSet::set_var_name(const StringName &p_name) {
- if (name == p_name) {
- return;
- }
-
- name = p_name;
- ports_changed_notify();
-}
-
-StringName VisualScriptLocalVarSet::get_var_name() const {
- return name;
-}
-
-void VisualScriptLocalVarSet::set_var_type(Variant::Type p_type) {
- type = p_type;
- ports_changed_notify();
-}
-
-Variant::Type VisualScriptLocalVarSet::get_var_type() const {
- return type;
-}
-
-class VisualScriptNodeInstanceLocalVarSet : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- StringName name;
-
- virtual int get_working_memory_size() const override { return 1; }
- 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_working_mem = *p_inputs[0];
- *p_outputs[0] = *p_working_mem;
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptLocalVarSet::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceLocalVarSet *instance = memnew(VisualScriptNodeInstanceLocalVarSet);
- instance->instance = p_instance;
- instance->name = name;
-
- return instance;
-}
-
-void VisualScriptLocalVarSet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_var_name", "name"), &VisualScriptLocalVarSet::set_var_name);
- ClassDB::bind_method(D_METHOD("get_var_name"), &VisualScriptLocalVarSet::get_var_name);
-
- ClassDB::bind_method(D_METHOD("set_var_type", "type"), &VisualScriptLocalVarSet::set_var_type);
- ClassDB::bind_method(D_METHOD("get_var_type"), &VisualScriptLocalVarSet::get_var_type);
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "var_name"), "set_var_name", "get_var_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_var_type", "get_var_type");
-}
-
-VisualScriptLocalVarSet::VisualScriptLocalVarSet() {
- name = "new_local";
- type = Variant::NIL;
-}
-
-//////////////////////////////////////////
-////////////////LocalVar///////////
-//////////////////////////////////////////
-
-int VisualScriptInputAction::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptInputAction::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptInputAction::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptInputAction::get_output_value_port_count() const {
- return 1;
-}
-
-String VisualScriptInputAction::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-PropertyInfo VisualScriptInputAction::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptInputAction::get_output_value_port_info(int p_idx) const {
- String mstr;
- switch (mode) {
- case MODE_PRESSED: {
- mstr = "pressed";
- } break;
- case MODE_RELEASED: {
- mstr = "not pressed";
- } break;
- case MODE_JUST_PRESSED: {
- mstr = "just pressed";
- } break;
- case MODE_JUST_RELEASED: {
- mstr = "just released";
- } break;
- }
-
- return PropertyInfo(Variant::BOOL, mstr);
-}
-
-String VisualScriptInputAction::get_caption() const {
- return vformat(RTR("Action %s"), name);
-}
-
-String VisualScriptInputAction::get_category() const {
- return "data";
-}
-
-void VisualScriptInputAction::set_action_name(const StringName &p_name) {
- if (name == p_name) {
- return;
- }
-
- name = p_name;
- ports_changed_notify();
-}
-
-StringName VisualScriptInputAction::get_action_name() const {
- return name;
-}
-
-void VisualScriptInputAction::set_action_mode(Mode p_mode) {
- if (mode == p_mode) {
- return;
- }
-
- mode = p_mode;
- ports_changed_notify();
-}
-
-VisualScriptInputAction::Mode VisualScriptInputAction::get_action_mode() const {
- return mode;
-}
-
-class VisualScriptNodeInstanceInputAction : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- StringName action;
- VisualScriptInputAction::Mode mode;
-
- 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 (mode) {
- case VisualScriptInputAction::MODE_PRESSED: {
- *p_outputs[0] = Input::get_singleton()->is_action_pressed(action);
- } break;
- case VisualScriptInputAction::MODE_RELEASED: {
- *p_outputs[0] = !Input::get_singleton()->is_action_pressed(action);
- } break;
- case VisualScriptInputAction::MODE_JUST_PRESSED: {
- *p_outputs[0] = Input::get_singleton()->is_action_just_pressed(action);
- } break;
- case VisualScriptInputAction::MODE_JUST_RELEASED: {
- *p_outputs[0] = Input::get_singleton()->is_action_just_released(action);
- } break;
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptInputAction::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceInputAction *instance = memnew(VisualScriptNodeInstanceInputAction);
- instance->instance = p_instance;
- instance->action = name;
- instance->mode = mode;
-
- return instance;
-}
-
-void VisualScriptInputAction::_validate_property(PropertyInfo &property) const {
- if (property.name == "action") {
- property.hint = PROPERTY_HINT_ENUM;
- String actions;
-
- List<PropertyInfo> pinfo;
- ProjectSettings::get_singleton()->get_property_list(&pinfo);
- Vector<String> al;
-
- for (const PropertyInfo &pi : pinfo) {
- if (!pi.name.begins_with("input/")) {
- continue;
- }
-
- String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
-
- al.push_back(name);
- }
-
- al.sort();
-
- for (int i = 0; i < al.size(); i++) {
- if (!actions.is_empty()) {
- actions += ",";
- }
- actions += al[i];
- }
-
- property.hint_string = actions;
- }
-}
-
-void VisualScriptInputAction::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_action_name", "name"), &VisualScriptInputAction::set_action_name);
- ClassDB::bind_method(D_METHOD("get_action_name"), &VisualScriptInputAction::get_action_name);
-
- ClassDB::bind_method(D_METHOD("set_action_mode", "mode"), &VisualScriptInputAction::set_action_mode);
- ClassDB::bind_method(D_METHOD("get_action_mode"), &VisualScriptInputAction::get_action_mode);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), "set_action_name", "get_action_name");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Pressed,Released,JustPressed,JustReleased"), "set_action_mode", "get_action_mode");
-
- BIND_ENUM_CONSTANT(MODE_PRESSED);
- BIND_ENUM_CONSTANT(MODE_RELEASED);
- BIND_ENUM_CONSTANT(MODE_JUST_PRESSED);
- BIND_ENUM_CONSTANT(MODE_JUST_RELEASED);
-}
-
-VisualScriptInputAction::VisualScriptInputAction() {
- name = "";
- mode = MODE_PRESSED;
-}
-
-//////////////////////////////////////////
-////////////////Constructor///////////
-//////////////////////////////////////////
-
-int VisualScriptDeconstruct::get_output_sequence_port_count() const {
- return 0;
-}
-
-bool VisualScriptDeconstruct::has_input_sequence_port() const {
- return false;
-}
-
-int VisualScriptDeconstruct::get_input_value_port_count() const {
- return 1;
-}
-
-int VisualScriptDeconstruct::get_output_value_port_count() const {
- return elements.size();
-}
-
-String VisualScriptDeconstruct::get_output_sequence_port_text(int p_port) const {
- return "";
-}
-
-PropertyInfo VisualScriptDeconstruct::get_input_value_port_info(int p_idx) const {
- return PropertyInfo(type, "value");
-}
-
-PropertyInfo VisualScriptDeconstruct::get_output_value_port_info(int p_idx) const {
- return PropertyInfo(elements[p_idx].type, elements[p_idx].name);
-}
-
-String VisualScriptDeconstruct::get_caption() const {
- return vformat(RTR("Deconstruct %s"), Variant::get_type_name(type));
-}
-
-String VisualScriptDeconstruct::get_category() const {
- return "functions";
-}
-
-void VisualScriptDeconstruct::_update_elements() {
- elements.clear();
- Variant v;
- Callable::CallError ce;
- Variant::construct(type, v, nullptr, 0, ce);
-
- List<PropertyInfo> pinfo;
- v.get_property_list(&pinfo);
-
- for (const PropertyInfo &E : pinfo) {
- Element e;
- e.name = E.name;
- e.type = E.type;
- elements.push_back(e);
- }
-}
-
-void VisualScriptDeconstruct::set_deconstruct_type(Variant::Type p_type) {
- if (type == p_type) {
- return;
- }
-
- type = p_type;
- _update_elements();
- ports_changed_notify();
- notify_property_list_changed(); //to make input appear/disappear
-}
-
-Variant::Type VisualScriptDeconstruct::get_deconstruct_type() const {
- return type;
-}
-
-void VisualScriptDeconstruct::_set_elem_cache(const Array &p_elements) {
- ERR_FAIL_COND(p_elements.size() % 2 == 1);
- elements.resize(p_elements.size() / 2);
- for (int i = 0; i < elements.size(); i++) {
- elements.write[i].name = p_elements[i * 2 + 0];
- elements.write[i].type = Variant::Type(int(p_elements[i * 2 + 1]));
- }
-}
-
-Array VisualScriptDeconstruct::_get_elem_cache() const {
- Array ret;
- for (int i = 0; i < elements.size(); i++) {
- ret.push_back(elements[i].name);
- ret.push_back(elements[i].type);
- }
- return ret;
-}
-
-class VisualScriptNodeInstanceDeconstruct : public VisualScriptNodeInstance {
-public:
- VisualScriptInstance *instance = nullptr;
- Vector<StringName> outputs;
-
- //virtual int get_working_memory_size() const override { return 0; }
-
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
- Variant in = *p_inputs[0];
-
- for (int i = 0; i < outputs.size(); i++) {
- bool valid;
- *p_outputs[i] = in.get(outputs[i], &valid);
- if (!valid) {
- r_error_str = "Can't obtain element '" + String(outputs[i]) + "' from " + Variant::get_type_name(in.get_type());
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
- }
-
- return 0;
- }
-};
-
-VisualScriptNodeInstance *VisualScriptDeconstruct::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceDeconstruct *instance = memnew(VisualScriptNodeInstanceDeconstruct);
- instance->instance = p_instance;
- instance->outputs.resize(elements.size());
- for (int i = 0; i < elements.size(); i++) {
- instance->outputs.write[i] = elements[i].name;
- }
-
- return instance;
-}
-
-void VisualScriptDeconstruct::_validate_property(PropertyInfo &property) const {
-}
-
-void VisualScriptDeconstruct::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_deconstruct_type", "type"), &VisualScriptDeconstruct::set_deconstruct_type);
- ClassDB::bind_method(D_METHOD("get_deconstruct_type"), &VisualScriptDeconstruct::get_deconstruct_type);
-
- ClassDB::bind_method(D_METHOD("_set_elem_cache", "_cache"), &VisualScriptDeconstruct::_set_elem_cache);
- ClassDB::bind_method(D_METHOD("_get_elem_cache"), &VisualScriptDeconstruct::_get_elem_cache);
-
- String argt = "Any";
- for (int i = 1; i < Variant::VARIANT_MAX; i++) {
- argt += "," + Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_deconstruct_type", "get_deconstruct_type");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elem_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_elem_cache", "_get_elem_cache");
-}
-
-VisualScriptDeconstruct::VisualScriptDeconstruct() {
- type = Variant::NIL;
-}
-
-template <Variant::Type T>
-static Ref<VisualScriptNode> create_node_deconst_typed(const String &p_name) {
- Ref<VisualScriptDeconstruct> node;
- node.instantiate();
- node->set_deconstruct_type(T);
- return node;
-}
-
-void register_visual_script_nodes() {
- VisualScriptLanguage::singleton->add_register_func("data/set_variable", create_node_generic<VisualScriptVariableSet>);
- VisualScriptLanguage::singleton->add_register_func("data/get_variable", create_node_generic<VisualScriptVariableGet>);
- VisualScriptLanguage::singleton->add_register_func("data/engine_singleton", create_node_generic<VisualScriptEngineSingleton>);
- VisualScriptLanguage::singleton->add_register_func("data/scene_node", create_node_generic<VisualScriptSceneNode>);
- VisualScriptLanguage::singleton->add_register_func("data/scene_tree", create_node_generic<VisualScriptSceneTree>);
- VisualScriptLanguage::singleton->add_register_func("data/resource_path", create_node_generic<VisualScriptResourcePath>);
- VisualScriptLanguage::singleton->add_register_func("data/self", create_node_generic<VisualScriptSelf>);
- VisualScriptLanguage::singleton->add_register_func("data/comment", create_node_generic<VisualScriptComment>);
- VisualScriptLanguage::singleton->add_register_func("data/get_local_variable", create_node_generic<VisualScriptLocalVar>);
- VisualScriptLanguage::singleton->add_register_func("data/set_local_variable", create_node_generic<VisualScriptLocalVarSet>);
- VisualScriptLanguage::singleton->add_register_func("data/preload", create_node_generic<VisualScriptPreload>);
- VisualScriptLanguage::singleton->add_register_func("data/action", create_node_generic<VisualScriptInputAction>);
-
- VisualScriptLanguage::singleton->add_register_func("constants/constant", create_node_generic<VisualScriptConstant>);
- VisualScriptLanguage::singleton->add_register_func("constants/math_constant", create_node_generic<VisualScriptMathConstant>);
- VisualScriptLanguage::singleton->add_register_func("constants/class_constant", create_node_generic<VisualScriptClassConstant>);
- VisualScriptLanguage::singleton->add_register_func("constants/global_constant", create_node_generic<VisualScriptGlobalConstant>);
- VisualScriptLanguage::singleton->add_register_func("constants/basic_type_constant", create_node_generic<VisualScriptBasicTypeConstant>);
-
- VisualScriptLanguage::singleton->add_register_func("custom/custom_node", create_node_generic<VisualScriptCustomNode>);
- VisualScriptLanguage::singleton->add_register_func("custom/sub_call", create_node_generic<VisualScriptSubCall>);
-
- VisualScriptLanguage::singleton->add_register_func("index/get_index", create_node_generic<VisualScriptIndexGet>);
- VisualScriptLanguage::singleton->add_register_func("index/set_index", create_node_generic<VisualScriptIndexSet>);
-
- VisualScriptLanguage::singleton->add_register_func("operators/compare/equal", create_op_node<Variant::OP_EQUAL>);
- VisualScriptLanguage::singleton->add_register_func("operators/compare/not_equal", create_op_node<Variant::OP_NOT_EQUAL>);
- VisualScriptLanguage::singleton->add_register_func("operators/compare/less", create_op_node<Variant::OP_LESS>);
- VisualScriptLanguage::singleton->add_register_func("operators/compare/less_equal", create_op_node<Variant::OP_LESS_EQUAL>);
- VisualScriptLanguage::singleton->add_register_func("operators/compare/greater", create_op_node<Variant::OP_GREATER>);
- VisualScriptLanguage::singleton->add_register_func("operators/compare/greater_equal", create_op_node<Variant::OP_GREATER_EQUAL>);
- //mathematic
- VisualScriptLanguage::singleton->add_register_func("operators/math/add", create_op_node<Variant::OP_ADD>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/subtract", create_op_node<Variant::OP_SUBTRACT>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/multiply", create_op_node<Variant::OP_MULTIPLY>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/divide", create_op_node<Variant::OP_DIVIDE>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/negate", create_op_node<Variant::OP_NEGATE>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/positive", create_op_node<Variant::OP_POSITIVE>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/remainder", create_op_node<Variant::OP_MODULE>);
- //bitwise
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_left", create_op_node<Variant::OP_SHIFT_LEFT>);
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_right", create_op_node<Variant::OP_SHIFT_RIGHT>);
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_and", create_op_node<Variant::OP_BIT_AND>);
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_or", create_op_node<Variant::OP_BIT_OR>);
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_xor", create_op_node<Variant::OP_BIT_XOR>);
- VisualScriptLanguage::singleton->add_register_func("operators/bitwise/bit_negate", create_op_node<Variant::OP_BIT_NEGATE>);
- //logic
- VisualScriptLanguage::singleton->add_register_func("operators/logic/and", create_op_node<Variant::OP_AND>);
- VisualScriptLanguage::singleton->add_register_func("operators/logic/or", create_op_node<Variant::OP_OR>);
- VisualScriptLanguage::singleton->add_register_func("operators/logic/xor", create_op_node<Variant::OP_XOR>);
- VisualScriptLanguage::singleton->add_register_func("operators/logic/not", create_op_node<Variant::OP_NOT>);
- VisualScriptLanguage::singleton->add_register_func("operators/logic/in", create_op_node<Variant::OP_IN>);
- VisualScriptLanguage::singleton->add_register_func("operators/logic/select", create_node_generic<VisualScriptSelect>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::VECTOR2), create_node_deconst_typed<Variant::Type::VECTOR2>);
- 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>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM2D), create_node_deconst_typed<Variant::Type::TRANSFORM2D>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PLANE), create_node_deconst_typed<Variant::Type::PLANE>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::QUATERNION), create_node_deconst_typed<Variant::Type::QUATERNION>);
- 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++) {
- List<MethodInfo> constructors;
- Variant::get_constructor_list(Variant::Type(i), &constructors);
-
- for (const MethodInfo &E : constructors) {
- if (E.arguments.size() > 0) {
- String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + "(";
- for (int j = 0; j < E.arguments.size(); j++) {
- if (j > 0) {
- name += ", ";
- }
- if (E.arguments.size() == 1) {
- name += Variant::get_type_name(E.arguments[j].type);
- } else {
- name += E.arguments[j].name;
- }
- }
- name += ")";
- VisualScriptLanguage::singleton->add_register_func(name, create_constructor_node);
- Pair<Variant::Type, MethodInfo> pair;
- pair.first = Variant::Type(i);
- pair.second = E;
- constructor_map[name] = pair;
- }
- }
- }
-}
-
-void unregister_visual_script_nodes() {
- constructor_map.clear();
-}
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
deleted file mode 100644
index 35e3c490cd..0000000000
--- a/modules/visual_script/visual_script_nodes.h
+++ /dev/null
@@ -1,1092 +0,0 @@
-/*************************************************************************/
-/* visual_script_nodes.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 VISUAL_SCRIPT_NODES_H
-#define VISUAL_SCRIPT_NODES_H
-
-#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 {
- GDCLASS(VisualScriptFunction, VisualScriptNode);
-
- struct Argument {
- String name;
- Variant::Type type;
- PropertyHint hint;
- String hint_string;
- };
-
- Vector<Argument> arguments;
-
- bool stack_less;
- int stack_size;
- MultiplayerAPI::RPCMode rpc_mode;
- bool sequenced;
-
-protected:
- 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:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "flow_control"; }
-
- void add_argument(Variant::Type p_type, const String &p_name, int p_index = -1, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(""));
- void set_argument_type(int p_argidx, Variant::Type p_type);
- Variant::Type get_argument_type(int p_argidx) const;
- void set_argument_name(int p_argidx, const String &p_name);
- String get_argument_name(int p_argidx) const;
- void remove_argument(int p_argidx);
- int get_argument_count() const;
-
- void set_stack_less(bool p_enable);
- bool is_stack_less() const;
-
- void set_sequenced(bool p_enable);
- bool is_sequenced() const;
-
- void set_stack_size(int p_size);
- int get_stack_size() const;
-
- void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
- MultiplayerAPI::RPCMode get_rpc_mode() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual void reset_state() override;
-
- VisualScriptFunction();
-};
-
-class VisualScriptLists : public VisualScriptNode {
- GDCLASS(VisualScriptLists, VisualScriptNode)
-
- struct Port {
- String name;
- Variant::Type type;
- };
-
-protected:
- Vector<Port> inputports;
- Vector<Port> outputports;
-
- enum {
- OUTPUT_EDITABLE = 0x0001,
- OUTPUT_NAME_EDITABLE = 0x0002,
- OUTPUT_TYPE_EDITABLE = 0x0004,
- INPUT_EDITABLE = 0x0008,
- INPUT_NAME_EDITABLE = 0x000F,
- INPUT_TYPE_EDITABLE = 0x0010,
- };
-
- int flags;
-
- bool sequenced;
-
- 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;
-
- static void _bind_methods();
-
-public:
- virtual void reset_state() override;
-
- virtual bool is_output_port_editable() const;
- virtual bool is_output_port_name_editable() const;
- virtual bool is_output_port_type_editable() const;
-
- virtual bool is_input_port_editable() const;
- virtual bool is_input_port_name_editable() const;
- virtual bool is_input_port_type_editable() const;
-
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- void add_input_data_port(Variant::Type p_type, const String &p_name, int p_index = -1);
- void set_input_data_port_type(int p_idx, Variant::Type p_type);
- void set_input_data_port_name(int p_idx, const String &p_name);
- void remove_input_data_port(int p_argidx);
-
- void add_output_data_port(Variant::Type p_type, const String &p_name, int p_index = -1);
- void set_output_data_port_type(int p_idx, Variant::Type p_type);
- void set_output_data_port_name(int p_idx, const String &p_name);
- void remove_output_data_port(int p_argidx);
-
- void set_sequenced(bool p_enable);
- bool is_sequenced() const;
-
- VisualScriptLists();
-};
-
-class VisualScriptComposeArray : public VisualScriptLists {
- GDCLASS(VisualScriptComposeArray, VisualScriptLists)
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptComposeArray();
-};
-
-class VisualScriptOperator : public VisualScriptNode {
- GDCLASS(VisualScriptOperator, VisualScriptNode);
-
- Variant::Type typed;
- Variant::Operator op;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "operators"; }
-
- void set_operator(Variant::Operator p_op);
- Variant::Operator get_operator() const;
-
- void set_typed(Variant::Type p_op);
- Variant::Type get_typed() const;
-
- static String get_operator_name(Variant::Operator p_op);
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptOperator();
-};
-
-class VisualScriptSelect : public VisualScriptNode {
- GDCLASS(VisualScriptSelect, VisualScriptNode);
-
- Variant::Type typed;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "operators"; }
-
- void set_typed(Variant::Type p_op);
- Variant::Type get_typed() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptSelect();
-};
-
-class VisualScriptVariableGet : public VisualScriptNode {
- GDCLASS(VisualScriptVariableGet, VisualScriptNode);
-
- StringName variable;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_variable(StringName p_variable);
- StringName get_variable() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptVariableGet();
-};
-
-class VisualScriptVariableSet : public VisualScriptNode {
- GDCLASS(VisualScriptVariableSet, VisualScriptNode);
-
- StringName variable;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_variable(StringName p_variable);
- StringName get_variable() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptVariableSet();
-};
-
-class VisualScriptConstant : public VisualScriptNode {
- GDCLASS(VisualScriptConstant, VisualScriptNode);
-
- Variant::Type type;
- Variant value;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "constants"; }
-
- void set_constant_type(Variant::Type p_type);
- Variant::Type get_constant_type() const;
-
- void set_constant_value(Variant p_value);
- Variant get_constant_value() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptConstant();
-};
-
-class VisualScriptPreload : public VisualScriptNode {
- GDCLASS(VisualScriptPreload, VisualScriptNode);
-
- Ref<Resource> preload;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_preload(const Ref<Resource> &p_preload);
- Ref<Resource> get_preload() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptPreload();
-};
-
-class VisualScriptIndexGet : public VisualScriptNode {
- GDCLASS(VisualScriptIndexGet, VisualScriptNode);
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "operators"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptIndexGet();
-};
-
-class VisualScriptIndexSet : public VisualScriptNode {
- GDCLASS(VisualScriptIndexSet, VisualScriptNode);
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "operators"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptIndexSet();
-};
-
-class VisualScriptGlobalConstant : public VisualScriptNode {
- GDCLASS(VisualScriptGlobalConstant, VisualScriptNode);
-
- int index;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "constants"; }
-
- void set_global_constant(int p_which);
- int get_global_constant();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptGlobalConstant();
-};
-
-class VisualScriptClassConstant : public VisualScriptNode {
- GDCLASS(VisualScriptClassConstant, VisualScriptNode);
-
- StringName base_type;
- StringName name;
-
-protected:
- static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "constants"; }
-
- void set_class_constant(const StringName &p_which);
- StringName get_class_constant();
-
- void set_base_type(const StringName &p_which);
- StringName get_base_type();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptClassConstant();
-};
-
-class VisualScriptBasicTypeConstant : public VisualScriptNode {
- GDCLASS(VisualScriptBasicTypeConstant, VisualScriptNode);
-
- Variant::Type type;
- StringName name;
-
-protected:
- static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "constants"; }
-
- void set_basic_type_constant(const StringName &p_which);
- StringName get_basic_type_constant() const;
-
- void set_basic_type(Variant::Type p_which);
- Variant::Type get_basic_type() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptBasicTypeConstant();
-};
-
-class VisualScriptMathConstant : public VisualScriptNode {
- GDCLASS(VisualScriptMathConstant, VisualScriptNode);
-
-public:
- enum MathConstant {
- MATH_CONSTANT_ONE,
- MATH_CONSTANT_PI,
- MATH_CONSTANT_HALF_PI,
- MATH_CONSTANT_TAU,
- MATH_CONSTANT_E,
- MATH_CONSTANT_SQRT2,
- MATH_CONSTANT_INF,
- MATH_CONSTANT_NAN,
- MATH_CONSTANT_MAX
- };
-
-private:
- static const char *const_name[MATH_CONSTANT_MAX];
- static double const_value[MATH_CONSTANT_MAX];
- MathConstant constant;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "constants"; }
-
- void set_math_constant(MathConstant p_which);
- MathConstant get_math_constant();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptMathConstant();
-};
-
-VARIANT_ENUM_CAST(VisualScriptMathConstant::MathConstant)
-
-class VisualScriptEngineSingleton : public VisualScriptNode {
- GDCLASS(VisualScriptEngineSingleton, VisualScriptNode);
-
- String singleton;
-
-protected:
- void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_singleton(const String &p_string);
- String get_singleton();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptEngineSingleton();
-};
-
-class VisualScriptSceneNode : public VisualScriptNode {
- GDCLASS(VisualScriptSceneNode, VisualScriptNode);
-
- NodePath path;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_node_path(const NodePath &p_path);
- NodePath get_node_path();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptSceneNode();
-};
-
-class VisualScriptSceneTree : public VisualScriptNode {
- GDCLASS(VisualScriptSceneTree, VisualScriptNode);
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptSceneTree();
-};
-
-class VisualScriptResourcePath : public VisualScriptNode {
- GDCLASS(VisualScriptResourcePath, VisualScriptNode);
-
- String path;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- void set_resource_path(const String &p_path);
- String get_resource_path();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptResourcePath();
-};
-
-class VisualScriptSelf : public VisualScriptNode {
- GDCLASS(VisualScriptSelf, VisualScriptNode);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override { return "data"; }
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- VisualScriptSelf();
-};
-
-class VisualScriptCustomNode : public VisualScriptNode {
- GDCLASS(VisualScriptCustomNode, VisualScriptNode);
-
-protected:
- static void _bind_methods();
- friend class VisualScriptNodeInstanceCustomNode;
- GDVIRTUAL0RC(int, _get_output_sequence_port_count)
- GDVIRTUAL0RC(bool, _has_input_sequence_port)
- GDVIRTUAL1RC(String, _get_output_sequence_port_text, int)
-
- GDVIRTUAL0RC(int, _get_input_value_port_count)
- GDVIRTUAL1RC(int, _get_input_value_port_type, int)
- GDVIRTUAL1RC(String, _get_input_value_port_name, int)
- GDVIRTUAL1RC(int, _get_input_value_port_hint, int)
- GDVIRTUAL1RC(String, _get_input_value_port_hint_string, int)
-
- GDVIRTUAL0RC(int, _get_output_value_port_count)
- GDVIRTUAL1RC(int, _get_output_value_port_type, int)
- GDVIRTUAL1RC(String, _get_output_value_port_name, int)
- GDVIRTUAL1RC(int, _get_output_value_port_hint, int)
- GDVIRTUAL1RC(String, _get_output_value_port_hint_string, int)
-
- GDVIRTUAL0RC(String, _get_caption)
- GDVIRTUAL0RC(String, _get_text)
- GDVIRTUAL0RC(String, _get_category)
-
- GDVIRTUAL0RC(int, _get_working_memory_size)
-
- GDVIRTUAL4RC(Variant, _step, Array, Array, int, Array)
-
-public:
- enum StartMode { //replicated for step
- START_MODE_BEGIN_SEQUENCE,
- START_MODE_CONTINUE_SEQUENCE,
- START_MODE_RESUME_YIELD
- };
-
- enum { //replicated for step
- STEP_SHIFT = 1 << 24,
- STEP_MASK = STEP_SHIFT - 1,
- STEP_PUSH_STACK_BIT = STEP_SHIFT, //push bit to stack
- STEP_GO_BACK_BIT = STEP_SHIFT << 1, //go back to previous node
- STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, //do not advance past this node
- STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, //return from function
- STEP_YIELD_BIT = STEP_SHIFT << 4, //yield (will find VisualScriptFunctionState state in first working memory)
- };
-
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
-
- void _script_changed();
-
- VisualScriptCustomNode();
-};
-
-VARIANT_ENUM_CAST(VisualScriptCustomNode::StartMode);
-
-class VisualScriptSubCall : public VisualScriptNode {
- GDCLASS(VisualScriptSubCall, VisualScriptNode);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptSubCall();
-};
-
-class VisualScriptComment : public VisualScriptNode {
- GDCLASS(VisualScriptComment, VisualScriptNode);
-
- String title;
- String description;
- Size2 size;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override;
-
- void set_title(const String &p_title);
- String get_title() const;
-
- void set_description(const String &p_description);
- String get_description() const;
-
- void set_size(const Size2 &p_size);
- Size2 get_size() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptComment();
-};
-
-class VisualScriptConstructor : public VisualScriptNode {
- GDCLASS(VisualScriptConstructor, VisualScriptNode);
-
- Variant::Type type;
- MethodInfo constructor;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override;
-
- void set_constructor_type(Variant::Type p_type);
- Variant::Type get_constructor_type() const;
-
- void set_constructor(const Dictionary &p_info);
- Dictionary get_constructor() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptConstructor();
-};
-
-class VisualScriptLocalVar : public VisualScriptNode {
- GDCLASS(VisualScriptLocalVar, VisualScriptNode);
-
- StringName name;
- Variant::Type type;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override;
-
- void set_var_name(const StringName &p_name);
- StringName get_var_name() const;
-
- void set_var_type(Variant::Type p_type);
- Variant::Type get_var_type() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptLocalVar();
-};
-
-class VisualScriptLocalVarSet : public VisualScriptNode {
- GDCLASS(VisualScriptLocalVarSet, VisualScriptNode);
-
- StringName name;
- Variant::Type type;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override;
-
- void set_var_name(const StringName &p_name);
- StringName get_var_name() const;
-
- void set_var_type(Variant::Type p_type);
- Variant::Type get_var_type() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptLocalVarSet();
-};
-
-class VisualScriptInputAction : public VisualScriptNode {
- GDCLASS(VisualScriptInputAction, VisualScriptNode);
-
-public:
- enum Mode {
- MODE_PRESSED,
- MODE_RELEASED,
- MODE_JUST_PRESSED,
- MODE_JUST_RELEASED,
- };
-
- StringName name;
- Mode mode;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override;
-
- void set_action_name(const StringName &p_name);
- StringName get_action_name() const;
-
- void set_action_mode(Mode p_mode);
- Mode get_action_mode() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptInputAction();
-};
-
-VARIANT_ENUM_CAST(VisualScriptInputAction::Mode)
-
-class VisualScriptDeconstruct : public VisualScriptNode {
- GDCLASS(VisualScriptDeconstruct, VisualScriptNode);
-
- struct Element {
- StringName name;
- Variant::Type type;
- };
-
- Vector<Element> elements;
-
- void _update_elements();
- Variant::Type type;
-
- void _set_elem_cache(const Array &p_elements);
- Array _get_elem_cache() const;
-
- virtual void _validate_property(PropertyInfo &property) const override;
-
-protected:
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_category() const override;
-
- void set_deconstruct_type(Variant::Type p_type);
- Variant::Type get_deconstruct_type() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptDeconstruct();
-};
-
-void register_visual_script_nodes();
-void unregister_visual_script_nodes();
-
-#endif // VISUAL_SCRIPT_NODES_H
diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp
deleted file mode 100644
index 96e91a0baf..0000000000
--- a/modules/visual_script/visual_script_yield_nodes.cpp
+++ /dev/null
@@ -1,598 +0,0 @@
-/*************************************************************************/
-/* visual_script_yield_nodes.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 "visual_script_yield_nodes.h"
-
-#include "core/os/os.h"
-#include "scene/main/node.h"
-#include "scene/main/scene_tree.h"
-#include "visual_script_nodes.h"
-
-//////////////////////////////////////////
-////////////////YIELD///////////
-//////////////////////////////////////////
-
-int VisualScriptYield::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptYield::has_input_sequence_port() const {
- return true;
-}
-
-int VisualScriptYield::get_input_value_port_count() const {
- return 0;
-}
-
-int VisualScriptYield::get_output_value_port_count() const {
- return 0;
-}
-
-String VisualScriptYield::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptYield::get_input_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-PropertyInfo VisualScriptYield::get_output_value_port_info(int p_idx) const {
- return PropertyInfo();
-}
-
-String VisualScriptYield::get_caption() const {
- return yield_mode == YIELD_RETURN ? RTR("Yield") : RTR("Wait");
-}
-
-String VisualScriptYield::get_text() const {
- switch (yield_mode) {
- case YIELD_RETURN:
- return "";
- break;
- case YIELD_FRAME:
- return RTR("Next Frame");
- break;
- case YIELD_PHYSICS_FRAME:
- return RTR("Next Physics Frame");
- break;
- case YIELD_WAIT:
- return vformat(RTR("%s sec(s)"), rtos(wait_time));
- break;
- }
-
- return String();
-}
-
-class VisualScriptNodeInstanceYield : public VisualScriptNodeInstance {
-public:
- VisualScriptYield::YieldMode mode;
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return 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 (p_start_mode == START_MODE_RESUME_YIELD) {
- return 0; //resuming yield
- } else {
- //yield
-
- SceneTree *tree = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
- if (!tree) {
- r_error_str = "Main Loop is not SceneTree";
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
-
- Ref<VisualScriptFunctionState> state;
- state.instantiate();
-
- int ret = STEP_YIELD_BIT;
- switch (mode) {
- case VisualScriptYield::YIELD_RETURN:
- ret = STEP_EXIT_FUNCTION_BIT;
- break; //return the yield
- case VisualScriptYield::YIELD_FRAME:
- state->connect_to_signal(tree, "process_frame", Array());
- break;
- case VisualScriptYield::YIELD_PHYSICS_FRAME:
- state->connect_to_signal(tree, "physics_frame", Array());
- break;
- case VisualScriptYield::YIELD_WAIT:
- state->connect_to_signal(tree->create_timer(wait_time).ptr(), "timeout", Array());
- break;
- }
-
- *p_working_mem = state;
-
- return ret;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptYield::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceYield *instance = memnew(VisualScriptNodeInstanceYield);
- //instance->instance=p_instance;
- instance->mode = yield_mode;
- instance->wait_time = wait_time;
- return instance;
-}
-
-void VisualScriptYield::set_yield_mode(YieldMode p_mode) {
- if (yield_mode == p_mode) {
- return;
- }
- yield_mode = p_mode;
- ports_changed_notify();
- notify_property_list_changed();
-}
-
-VisualScriptYield::YieldMode VisualScriptYield::get_yield_mode() {
- return yield_mode;
-}
-
-void VisualScriptYield::set_wait_time(double p_time) {
- if (wait_time == p_time) {
- return;
- }
- wait_time = p_time;
- ports_changed_notify();
-}
-
-double VisualScriptYield::get_wait_time() {
- return wait_time;
-}
-
-void VisualScriptYield::_validate_property(PropertyInfo &property) const {
- if (property.name == "wait_time") {
- if (yield_mode != YIELD_WAIT) {
- property.usage = PROPERTY_USAGE_NONE;
- }
- }
-}
-
-void VisualScriptYield::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_yield_mode", "mode"), &VisualScriptYield::set_yield_mode);
- ClassDB::bind_method(D_METHOD("get_yield_mode"), &VisualScriptYield::get_yield_mode);
-
- ClassDB::bind_method(D_METHOD("set_wait_time", "sec"), &VisualScriptYield::set_wait_time);
- ClassDB::bind_method(D_METHOD("get_wait_time"), &VisualScriptYield::get_wait_time);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NO_EDITOR), "set_yield_mode", "get_yield_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time"), "set_wait_time", "get_wait_time");
-
- BIND_ENUM_CONSTANT(YIELD_FRAME);
- BIND_ENUM_CONSTANT(YIELD_PHYSICS_FRAME);
- BIND_ENUM_CONSTANT(YIELD_WAIT);
-}
-
-VisualScriptYield::VisualScriptYield() {
- yield_mode = YIELD_FRAME;
- wait_time = 1;
-}
-
-template <VisualScriptYield::YieldMode MODE>
-static Ref<VisualScriptNode> create_yield_node(const String &p_name) {
- Ref<VisualScriptYield> node;
- node.instantiate();
- node->set_yield_mode(MODE);
- return node;
-}
-
-///////////////////////////////////////////////////
-////////////////YIELD SIGNAL//////////////////////
-//////////////////////////////////////////////////
-
-int VisualScriptYieldSignal::get_output_sequence_port_count() const {
- return 1;
-}
-
-bool VisualScriptYieldSignal::has_input_sequence_port() const {
- return true;
-}
-#ifdef TOOLS_ENABLED
-
-static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
- if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
- return nullptr;
- }
-
- Ref<Script> scr = p_current_node->get_script();
-
- if (scr.is_valid() && scr == script) {
- return p_current_node;
- }
-
- for (int i = 0; i < p_current_node->get_child_count(); i++) {
- Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
- if (n) {
- return n;
- }
- }
-
- return nullptr;
-}
-
-#endif
-Node *VisualScriptYieldSignal::_get_base_node() const {
-#ifdef TOOLS_ENABLED
- Ref<Script> script = get_visual_script();
- if (!script.is_valid()) {
- return nullptr;
- }
-
- MainLoop *main_loop = OS::get_singleton()->get_main_loop();
- SceneTree *scene_tree = Object::cast_to<SceneTree>(main_loop);
-
- if (!scene_tree) {
- return nullptr;
- }
-
- Node *edited_scene = scene_tree->get_edited_scene_root();
-
- if (!edited_scene) {
- return nullptr;
- }
-
- Node *script_node = _find_script_node(edited_scene, edited_scene, script);
-
- if (!script_node) {
- return nullptr;
- }
-
- if (!script_node->has_node(base_path)) {
- return nullptr;
- }
-
- Node *path_to = script_node->get_node(base_path);
-
- return path_to;
-#else
-
- return nullptr;
-#endif
-}
-
-StringName VisualScriptYieldSignal::_get_base_type() const {
- if (call_mode == CALL_MODE_SELF && get_visual_script().is_valid()) {
- return get_visual_script()->get_instance_base_type();
- } else if (call_mode == CALL_MODE_NODE_PATH && get_visual_script().is_valid()) {
- Node *path = _get_base_node();
- if (path) {
- return path->get_class();
- }
- }
-
- return base_type;
-}
-
-int VisualScriptYieldSignal::get_input_value_port_count() const {
- if (call_mode == CALL_MODE_INSTANCE) {
- return 1;
- } else {
- return 0;
- }
-}
-
-int VisualScriptYieldSignal::get_output_value_port_count() const {
- MethodInfo sr;
-
- if (!ClassDB::get_signal(_get_base_type(), signal, &sr)) {
- return 0;
- }
-
- return sr.arguments.size();
-}
-
-String VisualScriptYieldSignal::get_output_sequence_port_text(int p_port) const {
- return String();
-}
-
-PropertyInfo VisualScriptYieldSignal::get_input_value_port_info(int p_idx) const {
- if (call_mode == CALL_MODE_INSTANCE) {
- return PropertyInfo(Variant::OBJECT, "instance");
- } else {
- return PropertyInfo();
- }
-}
-
-PropertyInfo VisualScriptYieldSignal::get_output_value_port_info(int p_idx) const {
- MethodInfo sr;
-
- if (!ClassDB::get_signal(_get_base_type(), signal, &sr)) {
- return PropertyInfo(); //no signal
- }
- ERR_FAIL_INDEX_V(p_idx, sr.arguments.size(), PropertyInfo());
- return sr.arguments[p_idx];
-}
-
-String VisualScriptYieldSignal::get_caption() const {
- switch (call_mode) {
- case CALL_MODE_SELF: {
- return RTR("WaitSignal");
- } break;
- case CALL_MODE_NODE_PATH: {
- return RTR("WaitNodeSignal");
- } break;
- case CALL_MODE_INSTANCE: {
- return RTR("WaitInstanceSignal");
- } break;
- }
- return String();
-}
-
-String VisualScriptYieldSignal::get_text() const {
- if (call_mode == CALL_MODE_SELF) {
- return " " + String(signal) + "()";
- } else {
- return " " + _get_base_type() + "." + String(signal) + "()";
- }
-}
-
-void VisualScriptYieldSignal::set_base_type(const StringName &p_type) {
- if (base_type == p_type) {
- return;
- }
-
- base_type = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptYieldSignal::get_base_type() const {
- return base_type;
-}
-
-void VisualScriptYieldSignal::set_signal(const StringName &p_type) {
- if (signal == p_type) {
- return;
- }
-
- signal = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-StringName VisualScriptYieldSignal::get_signal() const {
- return signal;
-}
-
-void VisualScriptYieldSignal::set_base_path(const NodePath &p_type) {
- if (base_path == p_type) {
- return;
- }
-
- base_path = p_type;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-NodePath VisualScriptYieldSignal::get_base_path() const {
- return base_path;
-}
-
-void VisualScriptYieldSignal::set_call_mode(CallMode p_mode) {
- if (call_mode == p_mode) {
- return;
- }
-
- call_mode = p_mode;
-
- notify_property_list_changed();
- ports_changed_notify();
-}
-
-VisualScriptYieldSignal::CallMode VisualScriptYieldSignal::get_call_mode() const {
- return call_mode;
-}
-
-void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
- if (property.name == "base_type") {
- if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
- }
-
- if (property.name == "node_path") {
- if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = PROPERTY_USAGE_NONE;
- } else {
- Node *bnode = _get_base_node();
- if (bnode) {
- property.hint_string = bnode->get_path(); //convert to long string
- }
- }
- }
-
- if (property.name == "signal") {
- property.hint = PROPERTY_HINT_ENUM;
-
- List<MethodInfo> methods;
-
- ClassDB::get_signal_list(_get_base_type(), &methods);
-
- List<String> mstring;
- for (const MethodInfo &E : methods) {
- if (E.name.begins_with("_")) {
- continue;
- }
- mstring.push_back(E.name.get_slice(":", 0));
- }
-
- mstring.sort();
-
- String ml;
- for (const String &E : mstring) {
- if (!ml.is_empty()) {
- ml += ",";
- }
- ml += E;
- }
-
- property.hint_string = ml;
- }
-}
-
-void VisualScriptYieldSignal::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_base_type", "base_type"), &VisualScriptYieldSignal::set_base_type);
- ClassDB::bind_method(D_METHOD("get_base_type"), &VisualScriptYieldSignal::get_base_type);
-
- ClassDB::bind_method(D_METHOD("set_signal", "signal"), &VisualScriptYieldSignal::set_signal);
- ClassDB::bind_method(D_METHOD("get_signal"), &VisualScriptYieldSignal::get_signal);
-
- ClassDB::bind_method(D_METHOD("set_call_mode", "mode"), &VisualScriptYieldSignal::set_call_mode);
- ClassDB::bind_method(D_METHOD("get_call_mode"), &VisualScriptYieldSignal::get_call_mode);
-
- ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &VisualScriptYieldSignal::set_base_path);
- ClassDB::bind_method(D_METHOD("get_base_path"), &VisualScriptYieldSignal::get_base_path);
-
- String bt;
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- if (i > 0) {
- bt += ",";
- }
-
- bt += Variant::get_type_name(Variant::Type(i));
- }
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance"), "set_call_mode", "get_call_mode");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "signal"), "set_signal", "get_signal");
-
- BIND_ENUM_CONSTANT(CALL_MODE_SELF);
- BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
- BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
-}
-
-class VisualScriptNodeInstanceYieldSignal : public VisualScriptNodeInstance {
-public:
- VisualScriptYieldSignal::CallMode call_mode;
- NodePath node_path;
- int output_args = 0;
- StringName signal;
-
- 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; }
- //virtual bool get_output_port_unsequenced(int p_idx,Variant* r_value,Variant* p_working_mem,String &r_error) const { return true; }
-
- 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 (p_start_mode == START_MODE_RESUME_YIELD) {
- return 0; //resuming yield
- } else {
- //yield
-
- Object *object = nullptr;
-
- switch (call_mode) {
- case VisualScriptYieldSignal::CALL_MODE_SELF: {
- object = instance->get_owner_ptr();
-
- } break;
- case VisualScriptYieldSignal::CALL_MODE_NODE_PATH: {
- Node *node = Object::cast_to<Node>(instance->get_owner_ptr());
- if (!node) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Base object is not a Node!";
- return 0;
- }
-
- Node *another = node->get_node(node_path);
- if (!another) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Path does not lead Node!";
- return 0;
- }
-
- object = another;
-
- } break;
- case VisualScriptYieldSignal::CALL_MODE_INSTANCE: {
- object = *p_inputs[0];
- if (!object) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = "Supplied instance input is null.";
- return 0;
- }
-
- } break;
- }
-
- Ref<VisualScriptFunctionState> state;
- state.instantiate();
-
- state->connect_to_signal(object, signal, Array());
-
- *p_working_mem = state;
-
- return STEP_YIELD_BIT;
- }
- }
-};
-
-VisualScriptNodeInstance *VisualScriptYieldSignal::instantiate(VisualScriptInstance *p_instance) {
- VisualScriptNodeInstanceYieldSignal *instance = memnew(VisualScriptNodeInstanceYieldSignal);
- instance->node = this;
- instance->instance = p_instance;
- instance->signal = signal;
- instance->call_mode = call_mode;
- instance->node_path = base_path;
- instance->output_args = get_output_value_port_count();
- return instance;
-}
-
-VisualScriptYieldSignal::VisualScriptYieldSignal() {
- call_mode = CALL_MODE_SELF;
- base_type = "Object";
-}
-
-template <VisualScriptYieldSignal::CallMode cmode>
-static Ref<VisualScriptNode> create_yield_signal_node(const String &p_name) {
- Ref<VisualScriptYieldSignal> node;
- node.instantiate();
- node->set_call_mode(cmode);
- return node;
-}
-
-void register_visual_script_yield_nodes() {
- VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_frame", create_yield_node<VisualScriptYield::YIELD_FRAME>);
- VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_physics_frame", create_yield_node<VisualScriptYield::YIELD_PHYSICS_FRAME>);
- VisualScriptLanguage::singleton->add_register_func("functions/wait/wait_time", create_yield_node<VisualScriptYield::YIELD_WAIT>);
-
- VisualScriptLanguage::singleton->add_register_func("functions/yield", create_yield_node<VisualScriptYield::YIELD_RETURN>);
- VisualScriptLanguage::singleton->add_register_func("functions/yield_signal", create_node_generic<VisualScriptYieldSignal>);
-}
diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h
deleted file mode 100644
index a7bf4e8a78..0000000000
--- a/modules/visual_script/visual_script_yield_nodes.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*************************************************************************/
-/* visual_script_yield_nodes.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 VISUAL_SCRIPT_YIELD_NODES_H
-#define VISUAL_SCRIPT_YIELD_NODES_H
-
-#include "visual_script.h"
-
-class VisualScriptYield : public VisualScriptNode {
- GDCLASS(VisualScriptYield, VisualScriptNode);
-
-public:
- enum YieldMode {
- YIELD_RETURN,
- YIELD_FRAME,
- YIELD_PHYSICS_FRAME,
- YIELD_WAIT
-
- };
-
-private:
- YieldMode yield_mode;
- double wait_time;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- void set_yield_mode(YieldMode p_mode);
- YieldMode get_yield_mode();
-
- void set_wait_time(double p_time);
- double get_wait_time();
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptYield();
-};
-VARIANT_ENUM_CAST(VisualScriptYield::YieldMode)
-
-class VisualScriptYieldSignal : public VisualScriptNode {
- GDCLASS(VisualScriptYieldSignal, VisualScriptNode);
-
-public:
- enum CallMode {
- CALL_MODE_SELF,
- CALL_MODE_NODE_PATH,
- CALL_MODE_INSTANCE,
-
- };
-
-private:
- CallMode call_mode;
- StringName base_type;
- NodePath base_path;
- StringName signal;
-
- Node *_get_base_node() const;
- StringName _get_base_type() const;
-
-protected:
- virtual void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
-public:
- virtual int get_output_sequence_port_count() const override;
- virtual bool has_input_sequence_port() const override;
-
- virtual String get_output_sequence_port_text(int p_port) const override;
-
- virtual int get_input_value_port_count() const override;
- virtual int get_output_value_port_count() const override;
-
- virtual PropertyInfo get_input_value_port_info(int p_idx) const override;
- virtual PropertyInfo get_output_value_port_info(int p_idx) const override;
-
- virtual String get_caption() const override;
- virtual String get_text() const override;
- virtual String get_category() const override { return "functions"; }
-
- void set_base_type(const StringName &p_type);
- StringName get_base_type() const;
-
- void set_signal(const StringName &p_type);
- StringName get_signal() const;
-
- void set_base_path(const NodePath &p_type);
- NodePath get_base_path() const;
-
- void set_call_mode(CallMode p_mode);
- CallMode get_call_mode() const;
-
- virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
-
- VisualScriptYieldSignal();
-};
-
-VARIANT_ENUM_CAST(VisualScriptYieldSignal::CallMode);
-
-void register_visual_script_yield_nodes();
-
-#endif // VISUAL_SCRIPT_YIELD_NODES_H
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 8315eea614..f3ae35ce42 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* audio_stream_ogg_vorbis.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* audio_stream_ogg_vorbis.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "audio_stream_ogg_vorbis.h"
@@ -74,7 +74,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
if (beat_length_frames >= 0) {
/**
* Length determined by beat length
- * This code is commented out because, in practice, it is prefered that the fade
+ * This code is commented out because, in practice, it is preferred 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
@@ -153,8 +153,11 @@ int AudioStreamPlaybackOggVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p
return -1;
}
- ERR_FAIL_COND_V_MSG((err = vorbis_synthesis(&block, packet)), 0, "Error during vorbis synthesis " + itos(err));
- ERR_FAIL_COND_V_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), 0, "Error during vorbis block processing " + itos(err));
+ err = vorbis_synthesis(&block, packet);
+ ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis synthesis " + itos(err));
+
+ err = vorbis_synthesis_blockin(&dsp_state, &block);
+ ERR_FAIL_COND_V_MSG(err != 0, 0, "Error during vorbis block processing " + itos(err));
have_packets_left = !packet->e_o_s;
}
@@ -223,7 +226,7 @@ bool AudioStreamPlaybackOggVorbis::_alloc_vorbis() {
return true;
}
-void AudioStreamPlaybackOggVorbis::start(float p_from_pos) {
+void AudioStreamPlaybackOggVorbis::start(double p_from_pos) {
ERR_FAIL_COND(!ready);
loop_fade_remaining = FADE_SIZE;
active = true;
@@ -244,15 +247,15 @@ int AudioStreamPlaybackOggVorbis::get_loop_count() const {
return loops;
}
-float AudioStreamPlaybackOggVorbis::get_playback_position() const {
- return float(frames_mixed) / vorbis_data->get_sampling_rate();
+double AudioStreamPlaybackOggVorbis::get_playback_position() const {
+ return double(frames_mixed) / (double)vorbis_data->get_sampling_rate();
}
void AudioStreamPlaybackOggVorbis::tag_used_streams() {
vorbis_stream->tag_used(get_playback_position());
}
-void AudioStreamPlaybackOggVorbis::seek(float p_time) {
+void AudioStreamPlaybackOggVorbis::seek(double p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
if (!active) {
@@ -290,11 +293,15 @@ void AudioStreamPlaybackOggVorbis::seek(float p_time) {
headers_remaining = 3;
}
if (!headers_remaining) {
- ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err));
- ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err));
+ err = vorbis_synthesis(&block, packet);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
+
+ err = vorbis_synthesis_blockin(&dsp_state, &block);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
- ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err));
+ err = vorbis_synthesis_read(&dsp_state, samples_out);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
samples_in_page += samples_out;
@@ -341,12 +348,16 @@ void AudioStreamPlaybackOggVorbis::seek(float p_time) {
headers_remaining = 3;
}
if (!headers_remaining) {
- ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err));
- ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err));
+ err = vorbis_synthesis(&block, packet);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis synthesis " + itos(err));
+
+ err = vorbis_synthesis_blockin(&dsp_state, &block);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis block processing " + itos(err));
int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
int read_samples = samples_to_burn > samples_out ? samples_out : samples_to_burn;
- ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err));
+ err = vorbis_synthesis_read(&dsp_state, samples_out);
+ ERR_FAIL_COND_MSG(err != 0, "Error during vorbis read updating " + itos(err));
samples_to_burn -= read_samples;
if (samples_to_burn <= 0) {
@@ -427,9 +438,7 @@ void AudioStreamOggVorbis::maybe_update_info() {
}
if (i == 0) {
packet->b_o_s = 1;
- }
- if (i == 0) {
ERR_FAIL_COND(!vorbis_synthesis_idheader(packet));
}
@@ -462,15 +471,15 @@ bool AudioStreamOggVorbis::has_loop() const {
return loop;
}
-void AudioStreamOggVorbis::set_loop_offset(float p_seconds) {
+void AudioStreamOggVorbis::set_loop_offset(double p_seconds) {
loop_offset = p_seconds;
}
-float AudioStreamOggVorbis::get_loop_offset() const {
+double AudioStreamOggVorbis::get_loop_offset() const {
return loop_offset;
}
-float AudioStreamOggVorbis::get_length() const {
+double AudioStreamOggVorbis::get_length() const {
ERR_FAIL_COND_V(packet_sequence.is_null(), 0);
return packet_sequence->get_length();
}
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 0350e1f761..ad6746eae2 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* audio_stream_ogg_vorbis.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* audio_stream_ogg_vorbis.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 AUDIO_STREAM_OGG_VORBIS_H
#define AUDIO_STREAM_OGG_VORBIS_H
@@ -83,14 +83,14 @@ protected:
virtual float get_stream_sampling_rate() override;
public:
- virtual void start(float p_from_pos = 0.0) override;
+ virtual void start(double p_from_pos = 0.0) override;
virtual void stop() override;
virtual bool is_playing() const override;
virtual int get_loop_count() const override; //times it looped
- virtual float get_playback_position() const override;
- virtual void seek(float p_time) override;
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
virtual void tag_used_streams() override;
@@ -127,8 +127,8 @@ public:
void set_loop(bool p_enable);
virtual bool has_loop() const override;
- void set_loop_offset(float p_seconds);
- float get_loop_offset() const;
+ void set_loop_offset(double p_seconds);
+ double get_loop_offset() const;
void set_bpm(double p_bpm);
virtual double get_bpm() const override;
@@ -145,7 +145,7 @@ public:
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 double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 7ce885a37a..a231ef179d 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -1,5 +1,6 @@
def can_build(env, platform):
- return env.module_check_dependencies("vorbis", ["ogg"])
+ env.module_add_dependencies("vorbis", ["ogg"])
+ return True
def configure(env):
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
index 84a71fe82d..e131ff6dc9 100644
--- a/modules/vorbis/register_types.cpp
+++ b/modules/vorbis/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
diff --git a/modules/vorbis/register_types.h b/modules/vorbis/register_types.h
index 74c18b9c04..8b70a35e22 100644
--- a/modules/vorbis/register_types.h
+++ b/modules/vorbis/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 VORBIS_REGISTER_TYPES_H
#define VORBIS_REGISTER_TYPES_H
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index bf5f7206b8..64d254f221 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* resource_importer_ogg_vorbis.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_importer_ogg_vorbis.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_importer_ogg_vorbis.h"
@@ -110,15 +110,18 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
size_t packet_count = 0;
bool done = false;
while (!done) {
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ err = ogg_sync_check(&sync_state);
+ ERR_FAIL_COND_V_MSG(err != 0, 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)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ err = ogg_sync_check(&sync_state);
+ ERR_FAIL_COND_V_MSG(err != 0, 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)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ err = ogg_sync_check(&sync_state);
+ ERR_FAIL_COND_V_MSG(err != 0, 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) {
@@ -127,12 +130,14 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
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)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ err = ogg_sync_check(&sync_state);
+ ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
}
if (done) {
break;
}
- ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
+ err = ogg_sync_check(&sync_state);
+ ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
// Have a page now.
if (!initialized_stream) {
@@ -142,7 +147,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str
initialized_stream = true;
}
ogg_stream_pagein(&stream_state, &page);
- ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Ref<AudioStreamOggVorbis>(), "Ogg stream error " + itos(err));
+ err = ogg_stream_check(&stream_state);
+ ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg stream error " + itos(err));
int desync_iters = 0;
Vector<Vector<uint8_t>> packet_data;
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index a1a970e2cc..e7ca41011a 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* resource_importer_ogg_vorbis.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_importer_ogg_vorbis.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_IMPORTER_OGG_VORBIS_H
#define RESOURCE_IMPORTER_OGG_VORBIS_H
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 80d62400c8..72ad1ea5e4 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -12,123 +12,129 @@ thirdparty_obj = []
if env["builtin_libwebp"]:
thirdparty_dir = "#thirdparty/libwebp/"
thirdparty_sources = [
- "dec/alpha_dec.c",
- "dec/buffer_dec.c",
- "dec/frame_dec.c",
- "dec/idec_dec.c",
- "dec/io_dec.c",
- "dec/quant_dec.c",
- "dec/tree_dec.c",
- "dec/vp8_dec.c",
- "dec/vp8l_dec.c",
- "dec/webp_dec.c",
- "demux/anim_decode.c",
- "demux/demux.c",
- "dsp/alpha_processing.c",
- "dsp/alpha_processing_mips_dsp_r2.c",
- "dsp/alpha_processing_neon.c",
- "dsp/alpha_processing_sse2.c",
- "dsp/alpha_processing_sse41.c",
- "dsp/cost.c",
- "dsp/cost_mips32.c",
- "dsp/cost_mips_dsp_r2.c",
- "dsp/cost_neon.c",
- "dsp/cost_sse2.c",
- "dsp/cpu.c",
- "dsp/dec.c",
- "dsp/dec_clip_tables.c",
- "dsp/dec_mips32.c",
- "dsp/dec_mips_dsp_r2.c",
- "dsp/dec_msa.c",
- "dsp/dec_neon.c",
- "dsp/dec_sse2.c",
- "dsp/dec_sse41.c",
- "dsp/enc.c",
- "dsp/enc_mips32.c",
- "dsp/enc_mips_dsp_r2.c",
- "dsp/enc_msa.c",
- "dsp/enc_neon.c",
- "dsp/enc_sse2.c",
- "dsp/enc_sse41.c",
- "dsp/filters.c",
- "dsp/filters_mips_dsp_r2.c",
- "dsp/filters_msa.c",
- "dsp/filters_neon.c",
- "dsp/filters_sse2.c",
- "dsp/lossless.c",
- "dsp/lossless_enc.c",
- "dsp/lossless_enc_mips32.c",
- "dsp/lossless_enc_mips_dsp_r2.c",
- "dsp/lossless_enc_msa.c",
- "dsp/lossless_enc_neon.c",
- "dsp/lossless_enc_sse2.c",
- "dsp/lossless_enc_sse41.c",
- "dsp/lossless_mips_dsp_r2.c",
- "dsp/lossless_msa.c",
- "dsp/lossless_neon.c",
- "dsp/lossless_sse2.c",
- "dsp/lossless_sse41.c",
- "dsp/rescaler.c",
- "dsp/rescaler_mips32.c",
- "dsp/rescaler_mips_dsp_r2.c",
- "dsp/rescaler_msa.c",
- "dsp/rescaler_neon.c",
- "dsp/rescaler_sse2.c",
- "dsp/ssim.c",
- "dsp/ssim_sse2.c",
- "dsp/upsampling.c",
- "dsp/upsampling_mips_dsp_r2.c",
- "dsp/upsampling_msa.c",
- "dsp/upsampling_neon.c",
- "dsp/upsampling_sse2.c",
- "dsp/upsampling_sse41.c",
- "dsp/yuv.c",
- "dsp/yuv_mips32.c",
- "dsp/yuv_mips_dsp_r2.c",
- "dsp/yuv_neon.c",
- "dsp/yuv_sse2.c",
- "dsp/yuv_sse41.c",
- "enc/alpha_enc.c",
- "enc/analysis_enc.c",
- "enc/backward_references_cost_enc.c",
- "enc/backward_references_enc.c",
- "enc/config_enc.c",
- "enc/cost_enc.c",
- "enc/filter_enc.c",
- "enc/frame_enc.c",
- "enc/histogram_enc.c",
- "enc/iterator_enc.c",
- "enc/near_lossless_enc.c",
- "enc/picture_csp_enc.c",
- "enc/picture_enc.c",
- "enc/picture_psnr_enc.c",
- "enc/picture_rescale_enc.c",
- "enc/picture_tools_enc.c",
- "enc/predictor_enc.c",
- "enc/quant_enc.c",
- "enc/syntax_enc.c",
- "enc/token_enc.c",
- "enc/tree_enc.c",
- "enc/vp8l_enc.c",
- "enc/webp_enc.c",
- "mux/anim_encode.c",
- "mux/muxedit.c",
- "mux/muxinternal.c",
- "mux/muxread.c",
- "utils/bit_reader_utils.c",
- "utils/bit_writer_utils.c",
- "utils/color_cache_utils.c",
- "utils/filters_utils.c",
- "utils/huffman_encode_utils.c",
- "utils/huffman_utils.c",
- "utils/quant_levels_dec_utils.c",
- "utils/quant_levels_utils.c",
- "utils/random_utils.c",
- "utils/rescaler_utils.c",
- "utils/thread_utils.c",
- "utils/utils.c",
+ "sharpyuv/sharpyuv.c",
+ "sharpyuv/sharpyuv_csp.c",
+ "sharpyuv/sharpyuv_dsp.c",
+ "sharpyuv/sharpyuv_gamma.c",
+ "sharpyuv/sharpyuv_neon.c",
+ "sharpyuv/sharpyuv_sse2.c",
+ "src/dec/alpha_dec.c",
+ "src/dec/buffer_dec.c",
+ "src/dec/frame_dec.c",
+ "src/dec/idec_dec.c",
+ "src/dec/io_dec.c",
+ "src/dec/quant_dec.c",
+ "src/dec/tree_dec.c",
+ "src/dec/vp8_dec.c",
+ "src/dec/vp8l_dec.c",
+ "src/dec/webp_dec.c",
+ "src/demux/anim_decode.c",
+ "src/demux/demux.c",
+ "src/dsp/alpha_processing.c",
+ "src/dsp/alpha_processing_mips_dsp_r2.c",
+ "src/dsp/alpha_processing_neon.c",
+ "src/dsp/alpha_processing_sse2.c",
+ "src/dsp/alpha_processing_sse41.c",
+ "src/dsp/cost.c",
+ "src/dsp/cost_mips32.c",
+ "src/dsp/cost_mips_dsp_r2.c",
+ "src/dsp/cost_neon.c",
+ "src/dsp/cost_sse2.c",
+ "src/dsp/cpu.c",
+ "src/dsp/dec.c",
+ "src/dsp/dec_clip_tables.c",
+ "src/dsp/dec_mips32.c",
+ "src/dsp/dec_mips_dsp_r2.c",
+ "src/dsp/dec_msa.c",
+ "src/dsp/dec_neon.c",
+ "src/dsp/dec_sse2.c",
+ "src/dsp/dec_sse41.c",
+ "src/dsp/enc.c",
+ "src/dsp/enc_mips32.c",
+ "src/dsp/enc_mips_dsp_r2.c",
+ "src/dsp/enc_msa.c",
+ "src/dsp/enc_neon.c",
+ "src/dsp/enc_sse2.c",
+ "src/dsp/enc_sse41.c",
+ "src/dsp/filters.c",
+ "src/dsp/filters_mips_dsp_r2.c",
+ "src/dsp/filters_msa.c",
+ "src/dsp/filters_neon.c",
+ "src/dsp/filters_sse2.c",
+ "src/dsp/lossless.c",
+ "src/dsp/lossless_enc.c",
+ "src/dsp/lossless_enc_mips32.c",
+ "src/dsp/lossless_enc_mips_dsp_r2.c",
+ "src/dsp/lossless_enc_msa.c",
+ "src/dsp/lossless_enc_neon.c",
+ "src/dsp/lossless_enc_sse2.c",
+ "src/dsp/lossless_enc_sse41.c",
+ "src/dsp/lossless_mips_dsp_r2.c",
+ "src/dsp/lossless_msa.c",
+ "src/dsp/lossless_neon.c",
+ "src/dsp/lossless_sse2.c",
+ "src/dsp/lossless_sse41.c",
+ "src/dsp/rescaler.c",
+ "src/dsp/rescaler_mips32.c",
+ "src/dsp/rescaler_mips_dsp_r2.c",
+ "src/dsp/rescaler_msa.c",
+ "src/dsp/rescaler_neon.c",
+ "src/dsp/rescaler_sse2.c",
+ "src/dsp/ssim.c",
+ "src/dsp/ssim_sse2.c",
+ "src/dsp/upsampling.c",
+ "src/dsp/upsampling_mips_dsp_r2.c",
+ "src/dsp/upsampling_msa.c",
+ "src/dsp/upsampling_neon.c",
+ "src/dsp/upsampling_sse2.c",
+ "src/dsp/upsampling_sse41.c",
+ "src/dsp/yuv.c",
+ "src/dsp/yuv_mips32.c",
+ "src/dsp/yuv_mips_dsp_r2.c",
+ "src/dsp/yuv_neon.c",
+ "src/dsp/yuv_sse2.c",
+ "src/dsp/yuv_sse41.c",
+ "src/enc/alpha_enc.c",
+ "src/enc/analysis_enc.c",
+ "src/enc/backward_references_cost_enc.c",
+ "src/enc/backward_references_enc.c",
+ "src/enc/config_enc.c",
+ "src/enc/cost_enc.c",
+ "src/enc/filter_enc.c",
+ "src/enc/frame_enc.c",
+ "src/enc/histogram_enc.c",
+ "src/enc/iterator_enc.c",
+ "src/enc/near_lossless_enc.c",
+ "src/enc/picture_csp_enc.c",
+ "src/enc/picture_enc.c",
+ "src/enc/picture_psnr_enc.c",
+ "src/enc/picture_rescale_enc.c",
+ "src/enc/picture_tools_enc.c",
+ "src/enc/predictor_enc.c",
+ "src/enc/quant_enc.c",
+ "src/enc/syntax_enc.c",
+ "src/enc/token_enc.c",
+ "src/enc/tree_enc.c",
+ "src/enc/vp8l_enc.c",
+ "src/enc/webp_enc.c",
+ "src/mux/anim_encode.c",
+ "src/mux/muxedit.c",
+ "src/mux/muxinternal.c",
+ "src/mux/muxread.c",
+ "src/utils/bit_reader_utils.c",
+ "src/utils/bit_writer_utils.c",
+ "src/utils/color_cache_utils.c",
+ "src/utils/filters_utils.c",
+ "src/utils/huffman_encode_utils.c",
+ "src/utils/huffman_utils.c",
+ "src/utils/quant_levels_dec_utils.c",
+ "src/utils/quant_levels_utils.c",
+ "src/utils/random_utils.c",
+ "src/utils/rescaler_utils.c",
+ "src/utils/thread_utils.c",
+ "src/utils/utils.c",
]
- thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_webp.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"])
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 778d562278..13a22b5d18 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_webp.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_webp.h"
@@ -48,7 +48,7 @@ static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
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, BitField<ImageFormatLoader::LoaderFlags> p_flags, 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);
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index 9a5dc6cd7c..688223ede6 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* image_loader_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* image_loader_webp.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IMAGE_LOADER_WEBP_H
#define IMAGE_LOADER_WEBP_H
@@ -35,7 +35,7 @@
class ImageLoaderWebP : public ImageFormatLoader {
public:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderWebP();
};
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 29f633743e..1d8f67999e 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -1,39 +1,39 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_loader_webp.h"
#include "resource_saver_webp.h"
-static ImageLoaderWebP *image_loader_webp = nullptr;
+static Ref<ImageLoaderWebP> image_loader_webp;
static Ref<ResourceSaverWebP> resource_saver_webp;
void initialize_webp_module(ModuleInitializationLevel p_level) {
@@ -41,9 +41,10 @@ void initialize_webp_module(ModuleInitializationLevel p_level) {
return;
}
- image_loader_webp = memnew(ImageLoaderWebP);
- resource_saver_webp.instantiate();
+ image_loader_webp.instantiate();
ImageLoader::add_image_format_loader(image_loader_webp);
+
+ resource_saver_webp.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_webp);
}
@@ -52,7 +53,9 @@ void uninitialize_webp_module(ModuleInitializationLevel p_level) {
return;
}
- memdelete(image_loader_webp);
+ ImageLoader::remove_image_format_loader(image_loader_webp);
+ image_loader_webp.unref();
+
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 6e37dcfb61..caac7b48df 100644
--- a/modules/webp/register_types.h
+++ b/modules/webp/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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_REGISTER_TYPES_H
#define WEBP_REGISTER_TYPES_H
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
index bd71c2869a..2f09ab7964 100644
--- a/modules/webp/resource_saver_webp.cpp
+++ b/modules/webp/resource_saver_webp.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_saver_webp.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -38,8 +38,8 @@
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.");
+ 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();
@@ -52,7 +52,7 @@ Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_im
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));
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WebP at path: '%s'.", p_path));
const uint8_t *reader = buffer.ptr();
diff --git a/modules/webp/resource_saver_webp.h b/modules/webp/resource_saver_webp.h
index cbd5864463..5546c69e6d 100644
--- a/modules/webp/resource_saver_webp.h
+++ b/modules/webp/resource_saver_webp.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* resource_saver_webp.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
index 8657a98853..eb9403dcc2 100644
--- a/modules/webp/webp_common.cpp
+++ b/modules/webp/webp_common.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webp_common.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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"
@@ -41,40 +41,21 @@ 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;
+ return _webp_packer(p_image, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), false);
}
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);
+ float compression_factor = GLOBAL_GET("rendering/textures/webp_compression/lossless_compression_factor");
+ compression_factor = CLAMP(compression_factor, 0.0f, 100.0f);
+
+ return _webp_packer(p_image, compression_factor, true);
+}
+
+Vector<uint8_t> _webp_packer(const Ref<Image> &p_image, float p_quality, bool p_lossless) {
+ int compression_method = GLOBAL_GET("rendering/textures/webp_compression/compression_method");
+ compression_method = CLAMP(compression_method, 0, 6);
Ref<Image> img = p_image->duplicate();
if (img->detect_alpha()) {
@@ -87,16 +68,21 @@ Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
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...
+ // we need to use the more complex API in order to access specific flags...
WebPConfig config;
WebPPicture pic;
- if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
+ if (!WebPConfigInit(&config) || !WebPPictureInit(&pic)) {
ERR_FAIL_V(Vector<uint8_t>());
}
WebPMemoryWriter wrt;
- config.exact = 1;
+ if (p_lossless) {
+ config.lossless = 1;
+ config.exact = 1;
+ }
+ config.method = compression_method;
+ config.quality = p_quality;
pic.use_argb = 1;
pic.width = s.width;
pic.height = s.height;
@@ -139,7 +125,7 @@ Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
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.");
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WebP image.");
}
Vector<uint8_t> dst_image;
@@ -183,7 +169,7 @@ Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
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);
+ p_image->set_data(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
return OK;
}
diff --git a/modules/webp/webp_common.h b/modules/webp/webp_common.h
index 11bef40256..d73c543e71 100644
--- a/modules/webp/webp_common.h
+++ b/modules/webp/webp_common.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webp_common.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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
@@ -37,6 +37,8 @@ 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);
+// Helper function for those above.
+Vector<uint8_t> _webp_packer(const Ref<Image> &p_image, float p_quality, bool p_lossless);
// 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);
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index e6b9959840..e315633f55 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -5,7 +5,7 @@ Import("env_modules")
env_webrtc = env_modules.Clone()
-if env["platform"] == "javascript":
+if env["platform"] == "web":
# Our JavaScript/C++ interface.
env.AddJSLibraries(["library_godot_webrtc.js"])
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
index a9ba8a23de..a186631ca8 100644
--- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml
+++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
@@ -22,8 +22,8 @@
<method name="get_id" qualifiers="const">
<return type="int" />
<description>
- Returns the id assigned to this channel during creation (or auto-assigned during negotiation).
- If the channel is not negotiated out-of-band the id will only be available after the connection is established (will return [code]65535[/code] until then).
+ Returns the ID assigned to this channel during creation (or auto-assigned during negotiation).
+ If the channel is not negotiated out-of-band the ID will only be available after the connection is established (will return [code]65535[/code] until then).
</description>
</method>
<method name="get_label" qualifiers="const">
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml
index f937fba9d6..a10ea25b8c 100644
--- a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml
@@ -48,9 +48,9 @@
</description>
</method>
<method name="_get_packet" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="r_buffer" type="const uint8_t **" />
- <argument index="1" name="r_buffer_size" type="int32_t*" />
+ <return type="int" enum="Error" />
+ <param index="0" name="r_buffer" type="const uint8_t **" />
+ <param index="1" name="r_buffer_size" type="int32_t*" />
<description>
</description>
</method>
@@ -60,12 +60,12 @@
</description>
</method>
<method name="_get_ready_state" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="WebRTCDataChannel.ChannelState" />
<description>
</description>
</method>
<method name="_get_write_mode" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="WebRTCDataChannel.WriteMode" />
<description>
</description>
</method>
@@ -80,20 +80,20 @@
</description>
</method>
<method name="_poll" qualifiers="virtual">
- <return type="int" />
+ <return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_put_packet" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="p_buffer" type="const uint8_t*" />
- <argument index="1" name="p_buffer_size" type="int" />
+ <return type="int" enum="Error" />
+ <param index="0" name="p_buffer" type="const uint8_t*" />
+ <param index="1" name="p_buffer_size" type="int" />
<description>
</description>
</method>
<method name="_set_write_mode" qualifiers="virtual">
<return type="void" />
- <argument index="0" name="p_write_mode" type="int" />
+ <param index="0" name="p_write_mode" type="int" enum="WebRTCDataChannel.WriteMode" />
<description>
</description>
</method>
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index df92097135..5266a36637 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -6,7 +6,7 @@
<description>
This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer].
You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections.
- [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer].
+ When creating the peer via [method create_client] or [method create_server] the [method MultiplayerPeer.is_server_relay_supported] method will return [code]true[/code] enabling peer exchange and packet relaying when supported by the [MultiplayerAPI] implementation.
[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>
@@ -14,23 +14,42 @@
<methods>
<method name="add_peer">
<return type="int" enum="Error" />
- <argument index="0" name="peer" type="WebRTCPeerConnection" />
- <argument index="1" name="peer_id" type="int" />
- <argument index="2" name="unreliable_lifetime" type="int" default="1" />
+ <param index="0" name="peer" type="WebRTCPeerConnection" />
+ <param index="1" name="peer_id" type="int" />
+ <param index="2" name="unreliable_lifetime" type="int" default="1" />
<description>
Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
</description>
</method>
- <method name="close">
- <return type="void" />
+ <method name="create_client">
+ <return type="int" enum="Error" />
+ <param index="0" name="peer_id" type="int" />
+ <param index="1" name="channels_config" type="Array" default="[]" />
<description>
- Close all the add peer connections and channels, freeing all resources.
+ Initialize the multiplayer peer as a client with the given [code]peer_id[/code] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [code]peer_id[/code] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
+ 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="create_mesh">
+ <return type="int" enum="Error" />
+ <param index="0" name="peer_id" type="int" />
+ <param index="1" name="channels_config" type="Array" default="[]" />
+ <description>
+ Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [code]peer_id[/code] (must be between 1 and 2147483647).
+ </description>
+ </method>
+ <method name="create_server">
+ <return type="int" enum="Error" />
+ <param index="0" name="channels_config" type="Array" default="[]" />
+ <description>
+ Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
+ 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="get_peer">
<return type="Dictionary" />
- <argument index="0" name="peer_id" type="int" />
+ <param index="0" name="peer_id" type="int" />
<description>
Returns a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
</description>
@@ -43,26 +62,14 @@
</method>
<method name="has_peer">
<return type="bool" />
- <argument index="0" name="peer_id" type="int" />
+ <param index="0" name="peer_id" type="int" />
<description>
Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
</description>
</method>
- <method name="initialize">
- <return type="int" enum="Error" />
- <argument index="0" name="peer_id" type="int" />
- <argument index="1" name="server_compatibility" type="bool" default="false" />
- <argument index="2" name="channels_config" type="Array" default="[]" />
- <description>
- 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 MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
- </description>
- </method>
<method name="remove_peer">
<return type="void" />
- <argument index="0" name="peer_id" type="int" />
+ <param index="0" name="peer_id" type="int" />
<description>
Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted.
</description>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index fed67397d1..4ecc71ddbb 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -16,9 +16,9 @@
<methods>
<method name="add_ice_candidate">
<return type="int" enum="Error" />
- <argument index="0" name="media" type="String" />
- <argument index="1" name="index" type="int" />
- <argument index="2" name="name" type="String" />
+ <param index="0" name="media" type="String" />
+ <param index="1" name="index" type="int" />
+ <param index="2" name="name" type="String" />
<description>
Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created].
</description>
@@ -32,8 +32,8 @@
</method>
<method name="create_data_channel">
<return type="WebRTCDataChannel" />
- <argument index="0" name="label" type="String" />
- <argument index="1" name="options" type="Dictionary" default="{}" />
+ <param index="0" name="label" type="String" />
+ <param index="1" name="options" type="Dictionary" default="{}" />
<description>
Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
@@ -67,9 +67,21 @@
Returns the connection state. See [enum ConnectionState].
</description>
</method>
+ <method name="get_gathering_state" qualifiers="const">
+ <return type="int" enum="WebRTCPeerConnection.GatheringState" />
+ <description>
+ Returns the ICE [enum GatheringState] of the connection. This lets you detect, for example, when collection of ICE candidates has finished.
+ </description>
+ </method>
+ <method name="get_signaling_state" qualifiers="const">
+ <return type="int" enum="WebRTCPeerConnection.SignalingState" />
+ <description>
+ Returns the [enum SignalingState] on the local end of the connection while connecting or reconnecting to another peer.
+ </description>
+ </method>
<method name="initialize">
<return type="int" enum="Error" />
- <argument index="0" name="configuration" type="Dictionary" default="{}" />
+ <param index="0" name="configuration" type="Dictionary" default="{}" />
<description>
Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
Valid [code]options[/code] are:
@@ -97,15 +109,15 @@
</method>
<method name="set_default_extension" qualifiers="static">
<return type="void" />
- <argument index="0" name="extension_class" type="StringName" />
+ <param 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" />
- <argument index="1" name="sdp" type="String" />
+ <param index="0" name="type" type="String" />
+ <param index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the local peer. This should be called in response to [signal session_description_created].
After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned).
@@ -113,8 +125,8 @@
</method>
<method name="set_remote_description">
<return type="int" enum="Error" />
- <argument index="0" name="type" type="String" />
- <argument index="1" name="sdp" type="String" />
+ <param index="0" name="type" type="String" />
+ <param index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server.
If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer.
@@ -124,23 +136,23 @@
</methods>
<signals>
<signal name="data_channel_received">
- <argument index="0" name="channel" type="WebRTCDataChannel" />
+ <param index="0" name="channel" type="WebRTCDataChannel" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
</description>
</signal>
<signal name="ice_candidate_created">
- <argument index="0" name="media" type="String" />
- <argument index="1" name="index" type="int" />
- <argument index="2" name="name" type="String" />
+ <param index="0" name="media" type="String" />
+ <param index="1" name="index" type="int" />
+ <param index="2" name="name" type="String" />
<description>
Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server.
</description>
</signal>
<signal name="session_description_created">
- <argument index="0" name="type" type="String" />
- <argument index="1" name="sdp" type="String" />
+ <param index="0" name="type" type="String" />
+ <param index="1" name="sdp" type="String" />
<description>
Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server.
</description>
@@ -165,5 +177,32 @@
<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
The peer connection is closed (after calling [method close] for example).
</constant>
+ <constant name="GATHERING_STATE_NEW" value="0" enum="GatheringState">
+ The peer connection was just created and hasn't done any networking yet.
+ </constant>
+ <constant name="GATHERING_STATE_GATHERING" value="1" enum="GatheringState">
+ The ICE agent is in the process of gathering candidates for the connection.
+ </constant>
+ <constant name="GATHERING_STATE_COMPLETE" value="2" enum="GatheringState">
+ The ICE agent has finished gathering candidates. If something happens that requires collecting new candidates, such as a new interface being added or the addition of a new ICE server, the state will revert to gathering to gather those candidates.
+ </constant>
+ <constant name="SIGNALING_STATE_STABLE" value="0" enum="SignalingState">
+ There is no ongoing exchange of offer and answer underway. This may mean that the [WebRTCPeerConnection] is new ([constant STATE_NEW]) or that negotiation is complete and a connection has been established ([constant STATE_CONNECTED]).
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_LOCAL_OFFER" value="1" enum="SignalingState">
+ The local peer has called [method set_local_description], passing in SDP representing an offer (usually created by calling [method create_offer]), and the offer has been applied successfully.
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_REMOTE_OFFER" value="2" enum="SignalingState">
+ The remote peer has created an offer and used the signaling server to deliver it to the local peer, which has set the offer as the remote description by calling [method set_remote_description].
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_LOCAL_PRANSWER" value="3" enum="SignalingState">
+ The offer sent by the remote peer has been applied and an answer has been created and applied by calling [method set_local_description]. This provisional answer describes the supported media formats and so forth, but may not have a complete set of ICE candidates included. Further candidates will be delivered separately later.
+ </constant>
+ <constant name="SIGNALING_STATE_HAVE_REMOTE_PRANSWER" value="4" enum="SignalingState">
+ A provisional answer has been received and successfully applied in response to an offer previously sent and established by calling [method set_local_description].
+ </constant>
+ <constant name="SIGNALING_STATE_CLOSED" value="5" enum="SignalingState">
+ The [WebRTCPeerConnection] has been closed.
+ </constant>
</constants>
</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
index 163d939ac1..474d2f6a89 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -8,10 +8,10 @@
</tutorials>
<methods>
<method name="_add_ice_candidate" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="p_sdp_mid_name" type="String" />
- <argument index="1" name="p_sdp_mline_index" type="int" />
- <argument index="2" name="p_sdp_name" type="String" />
+ <return type="int" enum="Error" />
+ <param index="0" name="p_sdp_mid_name" type="String" />
+ <param index="1" name="p_sdp_mline_index" type="int" />
+ <param index="2" name="p_sdp_name" type="String" />
<description>
</description>
</method>
@@ -22,43 +22,53 @@
</method>
<method name="_create_data_channel" qualifiers="virtual">
<return type="Object" />
- <argument index="0" name="p_label" type="String" />
- <argument index="1" name="p_config" type="Dictionary" />
+ <param index="0" name="p_label" type="String" />
+ <param index="1" name="p_config" type="Dictionary" />
<description>
</description>
</method>
<method name="_create_offer" qualifiers="virtual">
- <return type="int" />
+ <return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_get_connection_state" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="WebRTCPeerConnection.ConnectionState" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_gathering_state" qualifiers="virtual const">
+ <return type="int" enum="WebRTCPeerConnection.GatheringState" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_signaling_state" qualifiers="virtual const">
+ <return type="int" enum="WebRTCPeerConnection.SignalingState" />
<description>
</description>
</method>
<method name="_initialize" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="p_config" type="Dictionary" />
+ <return type="int" enum="Error" />
+ <param index="0" name="p_config" type="Dictionary" />
<description>
</description>
</method>
<method name="_poll" qualifiers="virtual">
- <return type="int" />
+ <return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_set_local_description" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="p_type" type="String" />
- <argument index="1" name="p_sdp" type="String" />
+ <return type="int" enum="Error" />
+ <param index="0" name="p_type" type="String" />
+ <param index="1" name="p_sdp" type="String" />
<description>
</description>
</method>
<method name="_set_remote_description" qualifiers="virtual">
- <return type="int" />
- <argument index="0" name="p_type" type="String" />
- <argument index="1" name="p_sdp" type="String" />
+ <return type="int" enum="Error" />
+ <param index="0" name="p_type" type="String" />
+ <param index="1" name="p_sdp" type="String" />
<description>
</description>
</method>
diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js
index e57e4299e0..7ece4aa872 100644
--- a/modules/webrtc/library_godot_webrtc.js
+++ b/modules/webrtc/library_godot_webrtc.js
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* library_godot_webrtc.js */
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* library_godot_webrtc.js */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
const GodotRTCDataChannel = {
// Our socket implementation that forwards events to C++.
@@ -220,64 +220,123 @@ mergeInto(LibraryManager.library, GodotRTCDataChannel);
const GodotRTCPeerConnection = {
$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
$GodotRTCPeerConnection: {
- onstatechange: function (p_id, p_conn, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
- return;
- }
- let state;
- switch (p_conn.iceConnectionState) {
- case 'new':
- state = 0;
- break;
- case 'checking':
- state = 1;
- break;
- case 'connected':
- case 'completed':
- state = 2;
- break;
- case 'disconnected':
- state = 3;
- break;
- case 'failed':
- state = 4;
- break;
- case 'closed':
- default:
- state = 5;
- break;
- }
- callback(state);
+ // Enums
+ ConnectionState: {
+ 'new': 0,
+ 'connecting': 1,
+ 'connected': 2,
+ 'disconnected': 3,
+ 'failed': 4,
+ 'closed': 5,
},
- onicecandidate: function (p_id, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref || !event.candidate) {
- return;
+ ConnectionStateCompat: {
+ // Using values from IceConnectionState for browsers that do not support ConnectionState (notably Firefox).
+ 'new': 0,
+ 'checking': 1,
+ 'connected': 2,
+ 'completed': 2,
+ 'disconnected': 3,
+ 'failed': 4,
+ 'closed': 5,
+ },
+
+ IceGatheringState: {
+ 'new': 0,
+ 'gathering': 1,
+ 'complete': 2,
+ },
+
+ SignalingState: {
+ 'stable': 0,
+ 'have-local-offer': 1,
+ 'have-remote-offer': 2,
+ 'have-local-pranswer': 3,
+ 'have-remote-pranswer': 4,
+ 'closed': 5,
+ },
+
+ // Callbacks
+ create: function (config, onConnectionChange, onSignalingChange, onIceGatheringChange, onIceCandidate, onDataChannel) {
+ let conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ GodotRuntime.error(e);
+ return 0;
}
- const c = event.candidate;
- const candidate_str = GodotRuntime.allocString(c.candidate);
- const mid_str = GodotRuntime.allocString(c.sdpMid);
- callback(mid_str, c.sdpMLineIndex, candidate_str);
- GodotRuntime.free(candidate_str);
- GodotRuntime.free(mid_str);
+ const id = IDHandler.add(conn);
+
+ if ('connectionState' in conn && conn['connectionState'] !== undefined) {
+ // Use "connectionState" if supported
+ conn.onconnectionstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onConnectionChange(GodotRTCPeerConnection.ConnectionState[conn.connectionState] || 0);
+ };
+ } else {
+ // Fall back to using "iceConnectionState" when "connectionState" is not supported (notably Firefox).
+ conn.oniceconnectionstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onConnectionChange(GodotRTCPeerConnection.ConnectionStateCompat[conn.iceConnectionState] || 0);
+ };
+ }
+ conn.onicegatheringstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onIceGatheringChange(GodotRTCPeerConnection.IceGatheringState[conn.iceGatheringState] || 0);
+ };
+ conn.onsignalingstatechange = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ onSignalingChange(GodotRTCPeerConnection.SignalingState[conn.signalingState] || 0);
+ };
+ conn.onicecandidate = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ const c = event.candidate;
+ if (!c || !c.candidate) {
+ return;
+ }
+ const candidate_str = GodotRuntime.allocString(c.candidate);
+ const mid_str = GodotRuntime.allocString(c.sdpMid);
+ onIceCandidate(mid_str, c.sdpMLineIndex, candidate_str);
+ GodotRuntime.free(candidate_str);
+ GodotRuntime.free(mid_str);
+ };
+ conn.ondatachannel = function (event) {
+ if (!IDHandler.get(id)) {
+ return;
+ }
+ const cid = IDHandler.add(event.channel);
+ onDataChannel(cid);
+ };
+ return id;
},
- ondatachannel: function (p_id, callback, event) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
+ destroy: function (p_id) {
+ const conn = IDHandler.get(p_id);
+ if (!conn) {
return;
}
-
- const cid = IDHandler.add(event.channel);
- callback(cid);
+ conn.onconnectionstatechange = null;
+ conn.oniceconnectionstatechange = null;
+ conn.onicegatheringstatechange = null;
+ conn.onsignalingstatechange = null;
+ conn.onicecandidate = null;
+ conn.ondatachannel = null;
+ IDHandler.remove(p_id);
},
onsession: function (p_id, callback, session) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
+ if (!IDHandler.get(p_id)) {
return;
}
const type_str = GodotRuntime.allocString(session.type);
@@ -297,27 +356,19 @@ const GodotRTCPeerConnection = {
},
},
- godot_js_rtc_pc_create__sig: 'iiiiii',
- godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
- const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
- const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
- const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
-
- const config = JSON.parse(GodotRuntime.parseString(p_config));
- let conn = null;
- try {
- conn = new RTCPeerConnection(config);
- } catch (e) {
- GodotRuntime.error(e);
- return 0;
- }
-
- const base = GodotRTCPeerConnection;
- const id = IDHandler.add(conn);
- conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
- conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
- conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
- return id;
+ godot_js_rtc_pc_create__sig: 'iiiiiiii',
+ godot_js_rtc_pc_create: function (p_config, p_ref, p_on_connection_state_change, p_on_ice_gathering_state_change, p_on_signaling_state_change, p_on_ice_candidate, p_on_datachannel) {
+ const wrap = function (p_func) {
+ return GodotRuntime.get_func(p_func).bind(null, p_ref);
+ };
+ return GodotRTCPeerConnection.create(
+ JSON.parse(GodotRuntime.parseString(p_config)),
+ wrap(p_on_connection_state_change),
+ wrap(p_on_signaling_state_change),
+ wrap(p_on_ice_gathering_state_change),
+ wrap(p_on_ice_candidate),
+ wrap(p_on_datachannel)
+ );
},
godot_js_rtc_pc_close__sig: 'vi',
@@ -331,14 +382,7 @@ const GodotRTCPeerConnection = {
godot_js_rtc_pc_destroy__sig: 'vi',
godot_js_rtc_pc_destroy: function (p_id) {
- const ref = IDHandler.get(p_id);
- if (!ref) {
- return;
- }
- ref.oniceconnectionstatechange = null;
- ref.onicecandidate = null;
- ref.ondatachannel = null;
- IDHandler.remove(p_id);
+ GodotRTCPeerConnection.destroy(p_id);
},
godot_js_rtc_pc_offer_create__sig: 'viiii',
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 09cd538b96..93db5eb709 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/config/project_settings.h"
@@ -43,8 +43,7 @@ void initialize_webrtc_module(ModuleInitializationLevel p_level) {
}
#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"));
+ GLOBAL_DEF(PropertyInfo(Variant::INT, NAME, PROPERTY_HINT_RANGE, "2," #_MAX_ ",1,or_greater"), _VAL_);
SET_HINT(WRTC_IN_BUF, 64, 4096);
diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h
index 17171d0e0b..d874c3e503 100644
--- a/modules/webrtc/register_types.h
+++ b/modules/webrtc/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_REGISTER_TYPES_H
#define WEBRTC_REGISTER_TYPES_H
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
index b4af4b8415..71cbd27387 100644
--- a/modules/webrtc/webrtc_data_channel.cpp
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_data_channel.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_data_channel.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webrtc_data_channel.h"
#include "core/config/project_settings.h"
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index 9d20ad3266..e884c8425d 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_data_channel.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_data_channel.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_DATA_CHANNEL_H
#define WEBRTC_DATA_CHANNEL_H
diff --git a/modules/webrtc/webrtc_data_channel_extension.cpp b/modules/webrtc/webrtc_data_channel_extension.cpp
index b7ea8d22bb..b09997e445 100644
--- a/modules/webrtc/webrtc_data_channel_extension.cpp
+++ b/modules/webrtc/webrtc_data_channel_extension.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_data_channel_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_data_channel_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webrtc_data_channel_extension.h"
@@ -56,160 +56,20 @@ void WebRTCDataChannelExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_buffered_amount);
}
-int WebRTCDataChannelExtension::get_available_packet_count() const {
- int count;
- if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
- return count;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_available_packet_count is unimplemented!");
- return -1;
-}
-
Error WebRTCDataChannelExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- int err;
+ Error err;
if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
- return (Error)err;
+ return err;
}
WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_packet_native is unimplemented!");
return FAILED;
}
Error WebRTCDataChannelExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- int err;
+ Error err;
if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
- return (Error)err;
+ return err;
}
WARN_PRINT_ONCE("WebRTCDataChannelExtension::_put_packet_native is unimplemented!");
return FAILED;
}
-
-int WebRTCDataChannelExtension::get_max_packet_size() const {
- int size;
- if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
- return size;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_size is unimplemented!");
- return 0;
-}
-
-Error WebRTCDataChannelExtension::poll() {
- int err;
- if (GDVIRTUAL_CALL(_poll, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_poll is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-void WebRTCDataChannelExtension::close() {
- if (GDVIRTUAL_CALL(_close)) {
- return;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_close is unimplemented!");
-}
-
-void WebRTCDataChannelExtension::set_write_mode(WriteMode p_mode) {
- if (GDVIRTUAL_CALL(_set_write_mode, p_mode)) {
- return;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_set_write_mode is unimplemented!");
-}
-
-WebRTCDataChannel::WriteMode WebRTCDataChannelExtension::get_write_mode() const {
- int mode;
- if (GDVIRTUAL_CALL(_get_write_mode, mode)) {
- return (WriteMode)mode;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_write_mode is unimplemented!");
- return WRITE_MODE_BINARY;
-}
-
-bool WebRTCDataChannelExtension::was_string_packet() const {
- bool was_string;
- if (GDVIRTUAL_CALL(_was_string_packet, was_string)) {
- return was_string;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_was_string_packet is unimplemented!");
- return false;
-}
-
-WebRTCDataChannel::ChannelState WebRTCDataChannelExtension::get_ready_state() const {
- int state;
- if (GDVIRTUAL_CALL(_get_ready_state, state)) {
- return (ChannelState)state;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_ready_state is unimplemented!");
- return STATE_CLOSED;
-}
-
-String WebRTCDataChannelExtension::get_label() const {
- String label;
- if (GDVIRTUAL_CALL(_get_label, label)) {
- return label;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_label is unimplemented!");
- return label;
-}
-
-bool WebRTCDataChannelExtension::is_ordered() const {
- bool ordered;
- if (GDVIRTUAL_CALL(_is_ordered, ordered)) {
- return ordered;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_ordered is unimplemented!");
- return false;
-}
-
-int WebRTCDataChannelExtension::get_id() const {
- int id;
- if (GDVIRTUAL_CALL(_get_id, id)) {
- return id;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_id is unimplemented!");
- return -1;
-}
-
-int WebRTCDataChannelExtension::get_max_packet_life_time() const {
- int lifetime;
- if (GDVIRTUAL_CALL(_get_max_packet_life_time, lifetime)) {
- return lifetime;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_life_time is unimplemented!");
- return -1;
-}
-
-int WebRTCDataChannelExtension::get_max_retransmits() const {
- int retransmits;
- if (GDVIRTUAL_CALL(_get_max_retransmits, retransmits)) {
- return retransmits;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_retransmits is unimplemented!");
- return -1;
-}
-
-String WebRTCDataChannelExtension::get_protocol() const {
- String protocol;
- if (GDVIRTUAL_CALL(_get_protocol, protocol)) {
- return protocol;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_protocol is unimplemented!");
- return protocol;
-}
-
-bool WebRTCDataChannelExtension::is_negotiated() const {
- bool negotiated;
- if (GDVIRTUAL_CALL(_is_negotiated, negotiated)) {
- return negotiated;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_negotiated is unimplemented!");
- return false;
-}
-
-int WebRTCDataChannelExtension::get_buffered_amount() const {
- int amount;
- if (GDVIRTUAL_CALL(_get_buffered_amount, amount)) {
- return amount;
- }
- WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_buffered_amount is unimplemented!");
- return -1;
-}
diff --git a/modules/webrtc/webrtc_data_channel_extension.h b/modules/webrtc/webrtc_data_channel_extension.h
index 83bb627815..462e089592 100644
--- a/modules/webrtc/webrtc_data_channel_extension.h
+++ b/modules/webrtc/webrtc_data_channel_extension.h
@@ -1,38 +1,39 @@
-/*************************************************************************/
-/* webrtc_data_channel_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_data_channel_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_DATA_CHANNEL_EXTENSION_H
#define WEBRTC_DATA_CHANNEL_EXTENSION_H
#include "webrtc_data_channel.h"
+#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
@@ -44,53 +45,33 @@ protected:
static void _bind_methods();
public:
- virtual void set_write_mode(WriteMode mode) override;
- virtual WriteMode get_write_mode() const override;
- virtual bool was_string_packet() const override;
+ EXBIND0R(Error, poll);
+ EXBIND0(close);
- virtual ChannelState get_ready_state() const override;
- virtual String get_label() const override;
- virtual bool is_ordered() const override;
- virtual int get_id() const override;
- virtual int get_max_packet_life_time() const override;
- virtual int get_max_retransmits() const override;
- virtual String get_protocol() const override;
- virtual bool is_negotiated() const override;
- virtual int get_buffered_amount() const override;
+ EXBIND1(set_write_mode, WriteMode);
+ EXBIND0RC(WriteMode, get_write_mode);
- virtual Error poll() override;
- virtual void close() override;
+ EXBIND0RC(bool, was_string_packet);
+
+ EXBIND0RC(ChannelState, get_ready_state);
+ EXBIND0RC(String, get_label);
+ EXBIND0RC(bool, is_ordered);
+ EXBIND0RC(int, get_id);
+ EXBIND0RC(int, get_max_packet_life_time);
+ EXBIND0RC(int, get_max_retransmits);
+ EXBIND0RC(String, get_protocol);
+ EXBIND0RC(bool, is_negotiated);
+ EXBIND0RC(int, get_buffered_amount);
/** Inherited from PacketPeer: **/
- virtual int get_available_packet_count() const override;
+ EXBIND0RC(int, get_available_packet_count);
+ EXBIND0RC(int, get_max_packet_size);
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const override;
-
/** GDExtension **/
- GDVIRTUAL0RC(int, _get_available_packet_count);
- GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
- GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
- GDVIRTUAL0RC(int, _get_max_packet_size);
-
- GDVIRTUAL0R(int, _poll);
- GDVIRTUAL0(_close);
-
- GDVIRTUAL1(_set_write_mode, int);
- GDVIRTUAL0RC(int, _get_write_mode);
-
- GDVIRTUAL0RC(bool, _was_string_packet);
-
- GDVIRTUAL0RC(int, _get_ready_state);
- GDVIRTUAL0RC(String, _get_label);
- GDVIRTUAL0RC(bool, _is_ordered);
- GDVIRTUAL0RC(int, _get_id);
- GDVIRTUAL0RC(int, _get_max_packet_life_time);
- GDVIRTUAL0RC(int, _get_max_retransmits);
- GDVIRTUAL0RC(String, _get_protocol);
- GDVIRTUAL0RC(bool, _is_negotiated);
- GDVIRTUAL0RC(int, _get_buffered_amount);
+ GDVIRTUAL2R(Error, _get_packet, GDExtensionConstPtr<const uint8_t *>, GDExtensionPtr<int>);
+ GDVIRTUAL2R(Error, _put_packet, GDExtensionConstPtr<const uint8_t>, int);
WebRTCDataChannelExtension() {}
};
diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
index 0fb074b0c2..22d528a471 100644
--- a/modules/webrtc/webrtc_data_channel_js.cpp
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -1,34 +1,34 @@
-/*************************************************************************/
-/* webrtc_data_channel_js.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
+/**************************************************************************/
+/* webrtc_data_channel_js.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef WEB_ENABLED
#include "webrtc_data_channel_js.h"
diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h
index d059ec31ed..03cb550f19 100644
--- a/modules/webrtc/webrtc_data_channel_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -1,37 +1,37 @@
-/*************************************************************************/
-/* webrtc_data_channel_js.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_data_channel_js.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_DATA_CHANNEL_JS_H
#define WEBRTC_DATA_CHANNEL_JS_H
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
#include "webrtc_data_channel.h"
@@ -89,6 +89,6 @@ public:
~WebRTCDataChannelJS();
};
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
#endif // WEBRTC_DATA_CHANNEL_JS_H
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index e03b6b2473..36d0b41889 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_multiplayer_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webrtc_multiplayer_peer.h"
@@ -34,13 +34,14 @@
#include "core/os/os.h"
void WebRTCMultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_server", "channels_config"), &WebRTCMultiplayerPeer::create_server, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_client", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_client, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("create_mesh", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_mesh, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1));
ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayerPeer::get_peer);
ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayerPeer::get_peers);
- ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
}
void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) {
@@ -52,6 +53,15 @@ int WebRTCMultiplayerPeer::get_packet_peer() const {
return next_packet_peer;
}
+int WebRTCMultiplayerPeer::get_packet_channel() const {
+ return next_packet_channel < CH_RESERVED_MAX ? 0 : next_packet_channel - CH_RESERVED_MAX + 1;
+}
+
+MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_packet_mode() const {
+ ERR_FAIL_INDEX_V(next_packet_channel, channels_modes.size(), TRANSFER_MODE_RELIABLE);
+ return channels_modes[next_packet_channel];
+}
+
bool WebRTCMultiplayerPeer::is_server() const {
return unique_id == TARGET_PEER_SERVER;
}
@@ -113,24 +123,14 @@ void WebRTCMultiplayerPeer::poll() {
// Signal newly connected peers
for (int &E : add) {
// Already connected to server: simply notify new peer.
- // NOTE: Mesh is always connected.
- if (connection_status == CONNECTION_CONNECTED) {
- emit_signal(SNAME("peer_connected"), E);
- }
-
- // Server emulation mode suppresses peer_conencted until server connects.
- if (server_compat && E == TARGET_PEER_SERVER) {
+ if (network_mode == MODE_CLIENT) {
+ ERR_CONTINUE(E != TARGET_PEER_SERVER); // Bug.
// Server connected.
connection_status = CONNECTION_CONNECTED;
emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER);
emit_signal(SNAME("connection_succeeded"));
- // Notify of all previously connected peers
- for (const KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
- if (F.key != 1 && F.value->connected) {
- emit_signal(SNAME("peer_connected"), F.key);
- }
- }
- break; // Because we already notified of all newly added peers.
+ } else {
+ emit_signal(SNAME("peer_connected"), E);
}
}
// Fetch next packet
@@ -150,11 +150,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
continue;
}
+ int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
+ next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
+ idx++;
}
++E;
}
@@ -165,11 +168,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
continue;
}
+ int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
+ next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
+ idx++;
}
if (E->key == (int)next_packet_peer) {
break;
@@ -177,6 +183,7 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
++E;
}
// No packet found
+ next_packet_channel = 0;
next_packet_peer = 0;
}
@@ -184,11 +191,28 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status()
return connection_status;
}
-Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
+Error WebRTCMultiplayerPeer::create_server(Array p_channels_config) {
+ return _initialize(1, MODE_SERVER, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::create_client(int p_self_id, Array p_channels_config) {
+ ERR_FAIL_COND_V_MSG(p_self_id == 1, ERR_INVALID_PARAMETER, "Clients cannot have ID 1.");
+ return _initialize(p_self_id, MODE_CLIENT, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::create_mesh(int p_self_id, Array p_channels_config) {
+ return _initialize(p_self_id, MODE_MESH, p_channels_config);
+}
+
+Error WebRTCMultiplayerPeer::_initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config) {
ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
channels_config.clear();
+ channels_modes.clear();
+ channels_modes.push_back(TRANSFER_MODE_RELIABLE);
+ channels_modes.push_back(TRANSFER_MODE_UNRELIABLE_ORDERED);
+ channels_modes.push_back(TRANSFER_MODE_UNRELIABLE);
for (int i = 0; i < p_channels_config.size(); i++) {
- ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'");
+ ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
@@ -207,16 +231,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
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));
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
+ channels_modes.push_back((TransferMode)mode);
}
unique_id = p_self_id;
- server_compat = p_server_compat;
+ network_mode = p_mode;
// Mesh and server are always connected
- if (!server_compat || p_self_id == 1) {
+ if (p_mode != MODE_CLIENT) {
connection_status = CONNECTION_CONNECTED;
} else {
connection_status = CONNECTION_CONNECTING;
@@ -224,6 +249,10 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
return OK;
}
+bool WebRTCMultiplayerPeer::is_server_relay_supported() const {
+ return network_mode == MODE_SERVER || network_mode == MODE_CLIENT;
+}
+
int WebRTCMultiplayerPeer::get_unique_id() const {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
return unique_id;
@@ -261,7 +290,10 @@ Dictionary WebRTCMultiplayerPeer::get_peers() {
}
Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
- ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(network_mode == MODE_NONE, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(network_mode == MODE_CLIENT && p_peer_id != 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(network_mode == MODE_SERVER && p_peer_id == 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_peer_id < 1 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED);
// Peer must be valid, and in new state (to create data channels)
@@ -308,10 +340,21 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
if (peer->connected) {
peer->connected = false;
emit_signal(SNAME("peer_disconnected"), p_peer_id);
- if (server_compat && p_peer_id == TARGET_PEER_SERVER) {
- emit_signal(SNAME("server_disconnected"));
+ if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
+ connection_status = CONNECTION_DISCONNECTED;
+ }
+ }
+}
+
+void WebRTCMultiplayerPeer::disconnect_peer(int p_peer_id, bool p_force) {
+ ERR_FAIL_COND(!peer_map.has(p_peer_id));
+ if (p_force) {
+ peer_map.erase(p_peer_id);
+ if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
connection_status = CONNECTION_DISCONNECTED;
}
+ } else {
+ peer_map[p_peer_id]->connection->close(); // Will be removed during next poll.
}
}
@@ -403,7 +446,9 @@ void WebRTCMultiplayerPeer::close() {
channels_config.clear();
unique_id = 0;
next_packet_peer = 0;
+ next_packet_channel = 0;
target_peer = 0;
+ network_mode = MODE_NONE;
connection_status = CONNECTION_DISCONNECTED;
}
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index ea7c60036b..95668f4f8c 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_multiplayer_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_MULTIPLAYER_PEER_H
#define WEBRTC_MULTIPLAYER_PEER_H
@@ -48,6 +48,13 @@ private:
CH_RESERVED_MAX = 3
};
+ enum NetworkMode {
+ MODE_NONE,
+ MODE_SERVER,
+ MODE_CLIENT,
+ MODE_MESH,
+ };
+
class ConnectedPeer : public RefCounted {
public:
Ref<WebRTCPeerConnection> connection;
@@ -67,43 +74,53 @@ private:
int client_count = 0;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
int next_packet_peer = 0;
- bool server_compat = false;
+ int next_packet_channel = 0;
+ NetworkMode network_mode = MODE_NONE;
HashMap<int, Ref<ConnectedPeer>> peer_map;
+ List<TransferMode> channels_modes;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer();
+ Ref<ConnectedPeer> _get_next_peer();
+ Error _initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config = Array());
public:
WebRTCMultiplayerPeer() {}
~WebRTCMultiplayerPeer();
- Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
+ Error create_server(Array p_channels_config = Array());
+ Error create_client(int p_self_id, Array p_channels_config = Array());
+ Error create_mesh(int p_self_id, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id);
Dictionary get_peer(int p_peer_id);
Dictionary get_peers();
- void close();
// PacketPeer
- Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
- Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- int get_available_packet_count() const override;
- int get_max_packet_size() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_available_packet_count() const override;
+ virtual int get_max_packet_size() const override;
// MultiplayerPeer
- void set_target_peer(int p_peer_id) override;
+ virtual void set_target_peer(int p_peer_id) override;
- int get_unique_id() const override;
- int get_packet_peer() const override;
+ virtual int get_unique_id() const override;
+ virtual int get_packet_peer() const override;
+ virtual int get_packet_channel() const override;
+ virtual TransferMode get_packet_mode() const override;
- bool is_server() const override;
+ virtual bool is_server() const override;
+ virtual bool is_server_relay_supported() const override;
- void poll() override;
+ virtual void poll() override;
+ virtual void close() override;
+ virtual void disconnect_peer(int p_peer_id, bool p_force = false) override;
- ConnectionStatus get_connection_status() const override;
+ virtual ConnectionStatus get_connection_status() const override;
};
#endif // WEBRTC_MULTIPLAYER_PEER_H
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 75716017d7..8bad6fd784 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -1,36 +1,36 @@
-/*************************************************************************/
-/* webrtc_peer_connection.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_peer_connection.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webrtc_peer_connection.h"
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
#include "webrtc_peer_connection_js.h"
#endif
@@ -44,7 +44,7 @@ void WebRTCPeerConnection::set_default_extension(const StringName &p_extension)
}
WebRTCPeerConnection *WebRTCPeerConnection::create() {
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
return memnew(WebRTCPeerConnectionJS);
#else
if (default_extension == String()) {
@@ -69,6 +69,8 @@ void WebRTCPeerConnection::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
+ ClassDB::bind_method(D_METHOD("get_gathering_state"), &WebRTCPeerConnection::get_gathering_state);
+ ClassDB::bind_method(D_METHOD("get_signaling_state"), &WebRTCPeerConnection::get_signaling_state);
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
@@ -80,6 +82,17 @@ void WebRTCPeerConnection::_bind_methods() {
BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
BIND_ENUM_CONSTANT(STATE_FAILED);
BIND_ENUM_CONSTANT(STATE_CLOSED);
+
+ BIND_ENUM_CONSTANT(GATHERING_STATE_NEW);
+ BIND_ENUM_CONSTANT(GATHERING_STATE_GATHERING);
+ BIND_ENUM_CONSTANT(GATHERING_STATE_COMPLETE);
+
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_STABLE);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_OFFER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_OFFER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_PRANSWER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_PRANSWER);
+ BIND_ENUM_CONSTANT(SIGNALING_STATE_CLOSED);
}
WebRTCPeerConnection::WebRTCPeerConnection() {
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
index 122ea3d00f..2f8ed8b1ed 100644
--- a/modules/webrtc/webrtc_peer_connection.h
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webrtc_peer_connection.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_peer_connection.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_PEER_CONNECTION_H
#define WEBRTC_PEER_CONNECTION_H
@@ -47,6 +47,21 @@ public:
STATE_CLOSED
};
+ enum GatheringState {
+ GATHERING_STATE_NEW,
+ GATHERING_STATE_GATHERING,
+ GATHERING_STATE_COMPLETE,
+ };
+
+ enum SignalingState {
+ SIGNALING_STATE_STABLE,
+ SIGNALING_STATE_HAVE_LOCAL_OFFER,
+ SIGNALING_STATE_HAVE_REMOTE_OFFER,
+ SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
+ SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
+ SIGNALING_STATE_CLOSED,
+ };
+
private:
static StringName default_extension;
@@ -57,6 +72,8 @@ public:
static void set_default_extension(const StringName &p_name);
virtual ConnectionState get_connection_state() const = 0;
+ virtual GatheringState get_gathering_state() const = 0;
+ virtual SignalingState get_signaling_state() const = 0;
virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
@@ -74,5 +91,7 @@ public:
};
VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::GatheringState);
+VARIANT_ENUM_CAST(WebRTCPeerConnection::SignalingState);
#endif // WEBRTC_PEER_CONNECTION_H
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
index 85c04b3b19..15e6bac3b0 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.cpp
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -1,37 +1,39 @@
-/*************************************************************************/
-/* webrtc_peer_connection_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_peer_connection_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webrtc_peer_connection_extension.h"
void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_connection_state);
+ GDVIRTUAL_BIND(_get_gathering_state);
+ GDVIRTUAL_BIND(_get_signaling_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
GDVIRTUAL_BIND(_create_offer);
@@ -42,24 +44,6 @@ void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_close);
}
-WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const {
- int state;
- if (GDVIRTUAL_CALL(_get_connection_state, state)) {
- return (ConnectionState)state;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_get_connection_state is unimplemented!");
- return STATE_DISCONNECTED;
-}
-
-Error WebRTCPeerConnectionExtension::initialize(Dictionary p_config) {
- int err;
- if (GDVIRTUAL_CALL(_initialize, p_config, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_initialize is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String p_label, Dictionary p_options) {
Object *ret = nullptr;
if (GDVIRTUAL_CALL(_create_data_channel, p_label, p_options, ret)) {
@@ -70,55 +54,3 @@ Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String
WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_data_channel is unimplemented!");
return nullptr;
}
-
-Error WebRTCPeerConnectionExtension::create_offer() {
- int err;
- if (GDVIRTUAL_CALL(_create_offer, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_offer is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-Error WebRTCPeerConnectionExtension::set_local_description(String p_type, String p_sdp) {
- int err;
- if (GDVIRTUAL_CALL(_set_local_description, p_type, p_sdp, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_local_description is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-Error WebRTCPeerConnectionExtension::set_remote_description(String p_type, String p_sdp) {
- int err;
- if (GDVIRTUAL_CALL(_set_remote_description, p_type, p_sdp, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_remote_description is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-Error WebRTCPeerConnectionExtension::add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) {
- int err;
- if (GDVIRTUAL_CALL(_add_ice_candidate, p_sdp_mid_name, p_sdp_mline_index, p_sdp_name, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_add_ice_candidate is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-Error WebRTCPeerConnectionExtension::poll() {
- int err;
- if (GDVIRTUAL_CALL(_poll, err)) {
- return (Error)err;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_poll is unimplemented!");
- return ERR_UNCONFIGURED;
-}
-
-void WebRTCPeerConnectionExtension::close() {
- if (GDVIRTUAL_CALL(_close)) {
- return;
- }
- WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_close is unimplemented!");
-}
diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h
index bde19c173b..a2a5c773f0 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -1,38 +1,39 @@
-/*************************************************************************/
-/* webrtc_peer_connection_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_peer_connection_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_PEER_CONNECTION_EXTENSION_H
#define WEBRTC_PEER_CONNECTION_EXTENSION_H
#include "webrtc_peer_connection.h"
+#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/object/script_language.h"
#include "core/variant/native_ptr.h"
@@ -44,27 +45,23 @@ protected:
static void _bind_methods();
public:
- virtual ConnectionState get_connection_state() const override;
-
- virtual Error initialize(Dictionary p_config = Dictionary()) override;
+ // FIXME Can't be directly exposed due to issues in exchanging Ref(s) between godot and extensions.
+ // See godot-cpp GH-652 .
virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) override;
- virtual Error create_offer() override;
- virtual Error set_remote_description(String type, String sdp) override;
- virtual Error set_local_description(String type, String sdp) override;
- virtual Error add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) override;
- virtual Error poll() override;
- virtual void close() override;
+ GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary);
+ // EXBIND2R(Ref<WebRTCDataChannel>, create_data_channel, String, Dictionary);
/** GDExtension **/
- GDVIRTUAL0RC(int, _get_connection_state);
- GDVIRTUAL1R(int, _initialize, Dictionary);
- GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary);
- GDVIRTUAL0R(int, _create_offer);
- GDVIRTUAL2R(int, _set_remote_description, String, String);
- GDVIRTUAL2R(int, _set_local_description, String, String);
- GDVIRTUAL3R(int, _add_ice_candidate, String, int, String);
- GDVIRTUAL0R(int, _poll);
- GDVIRTUAL0(_close);
+ EXBIND0RC(ConnectionState, get_connection_state);
+ EXBIND0RC(GatheringState, get_gathering_state);
+ EXBIND0RC(SignalingState, get_signaling_state);
+ EXBIND1R(Error, initialize, Dictionary);
+ EXBIND0R(Error, create_offer);
+ EXBIND2R(Error, set_remote_description, String, String);
+ EXBIND2R(Error, set_local_description, String, String);
+ EXBIND3R(Error, add_ice_candidate, String, int, String);
+ EXBIND0R(Error, poll);
+ EXBIND0(close);
WebRTCPeerConnectionExtension() {}
};
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index ee3a302fa2..f979a20367 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -1,34 +1,34 @@
-/*************************************************************************/
-/* webrtc_peer_connection_js.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
+/**************************************************************************/
+/* webrtc_peer_connection_js.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef WEB_ENABLED
#include "webrtc_peer_connection_js.h"
@@ -51,6 +51,16 @@ void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_sta
peer->_conn_state = (ConnectionState)p_state;
}
+void WebRTCPeerConnectionJS::_on_gathering_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_gathering_state = (GatheringState)p_state;
+}
+
+void WebRTCPeerConnectionJS::_on_signaling_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_signaling_state = (SignalingState)p_state;
+}
+
void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
ERR_PRINT("RTCPeerConnection error!");
}
@@ -100,7 +110,7 @@ Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
_conn_state = STATE_NEW;
String config = Variant(p_config).to_json_string();
- _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_ice_candidate, &_on_data_channel);
+ _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_gathering_state_changed, &_on_signaling_state_changed, &_on_ice_candidate, &_on_data_channel);
return _js_id ? OK : FAILED;
}
@@ -117,14 +127,19 @@ Error WebRTCPeerConnectionJS::poll() {
return OK;
}
+WebRTCPeerConnection::GatheringState WebRTCPeerConnectionJS::get_gathering_state() const {
+ return _gathering_state;
+}
+
+WebRTCPeerConnection::SignalingState WebRTCPeerConnectionJS::get_signaling_state() const {
+ return _signaling_state;
+}
+
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
return _conn_state;
}
WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
- _conn_state = STATE_NEW;
- _js_id = 0;
-
Dictionary config;
initialize(config);
}
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index 76b8c7fff8..1d7d10bde8 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -1,47 +1,49 @@
-/*************************************************************************/
-/* webrtc_peer_connection_js.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webrtc_peer_connection_js.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBRTC_PEER_CONNECTION_JS_H
#define WEBRTC_PEER_CONNECTION_JS_H
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
#include "webrtc_peer_connection.h"
extern "C" {
typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnIceGatheringStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnSignalingStateChange)(void *p_obj, int p_state);
typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
typedef void (*RTCOnError)(void *p_obj);
-extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
+extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_connection_state_change, RTCOnIceGatheringStateChange p_on_gathering_state_change, RTCOnSignalingStateChange p_on_signaling_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
extern void godot_js_rtc_pc_close(int p_id);
extern void godot_js_rtc_pc_destroy(int p_id);
extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
@@ -55,10 +57,14 @@ class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
private:
- int _js_id;
- ConnectionState _conn_state;
+ int _js_id = 0;
+ ConnectionState _conn_state = STATE_NEW;
+ GatheringState _gathering_state = GATHERING_STATE_NEW;
+ SignalingState _signaling_state = SIGNALING_STATE_STABLE;
static void _on_connection_state_changed(void *p_obj, int p_state);
+ static void _on_gathering_state_changed(void *p_obj, int p_state);
+ static void _on_signaling_state_changed(void *p_obj, int p_state);
static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
static void _on_data_channel(void *p_obj, int p_channel);
static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
@@ -66,6 +72,8 @@ private:
public:
virtual ConnectionState get_connection_state() const override;
+ virtual GatheringState get_gathering_state() const override;
+ virtual SignalingState get_signaling_state() const override;
virtual Error initialize(Dictionary configuration = Dictionary()) override;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()) override;
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index dc0661995f..3f834471e5 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -7,7 +7,7 @@ env_ws = env_modules.Clone()
thirdparty_obj = []
-if env["platform"] == "javascript":
+if env["platform"] == "web":
# Our JavaScript/C++ interface.
env.AddJSLibraries(["library_godot_websocket.js"])
@@ -41,7 +41,7 @@ elif env["builtin_wslay"]:
module_obj = []
env_ws.add_source_files(module_obj, "*.cpp")
-if env["tools"]:
+if env.editor_build:
env_ws.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
deleted file mode 100644
index ad2acf8a21..0000000000
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketClient" inherits="WebSocketMultiplayerPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A WebSocket client implementation.
- </brief_description>
- <description>
- This class implements a WebSocket client compatible with any RFC 6455-compliant WebSocket server.
- This client can be optionally used as a multiplayer peer for the [MultiplayerAPI].
- After starting the client ([method connect_to_url]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
- You will receive appropriate signals when connecting, disconnecting, or when new data is available.
- [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="connect_to_url">
- <return type="int" enum="Error" />
- <argument index="0" name="url" type="String" />
- <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" />
- <argument index="2" name="gd_mp_api" type="bool" default="false" />
- <argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" />
- <description>
- Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested.
- If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a multiplayer peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted.
- If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]).
- You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request.
- [b]Note:[/b] To avoid mixed content warnings or errors in HTML5, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's SSL certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the SSL certificate.
- [b]Note:[/b] Specifying [code]custom_headers[/code] is not supported in HTML5 exports due to browsers restrictions.
- </description>
- </method>
- <method name="disconnect_from_host">
- <return type="void" />
- <argument index="0" name="code" type="int" default="1000" />
- <argument index="1" name="reason" type="String" default="&quot;&quot;" />
- <description>
- Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information.
- </description>
- </method>
- <method name="get_connected_host" qualifiers="const">
- <return type="String" />
- <description>
- Returns the IP address of the currently connected host.
- </description>
- </method>
- <method name="get_connected_port" qualifiers="const">
- <return type="int" />
- <description>
- Returns the IP port of the currently connected host.
- </description>
- </method>
- </methods>
- <members>
- <member name="trusted_ssl_certificate" type="X509Certificate" setter="set_trusted_ssl_certificate" getter="get_trusted_ssl_certificate">
- If specified, this [X509Certificate] will be the only one accepted when connecting to an SSL host. Any other certificate provided by the server will be regarded as invalid.
- [b]Note:[/b] Specifying a custom [code]trusted_ssl_certificate[/code] is not supported in HTML5 exports due to browsers restrictions.
- </member>
- <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
- If [code]true[/code], SSL certificate verification is enabled.
- [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported.
- </member>
- </members>
- <signals>
- <signal name="connection_closed">
- <argument index="0" name="was_clean_close" type="bool" />
- <description>
- Emitted when the connection to the server is closed. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly.
- </description>
- </signal>
- <signal name="connection_error">
- <description>
- Emitted when the connection to the server fails.
- </description>
- </signal>
- <signal name="connection_established">
- <argument index="0" name="protocol" type="String" />
- <description>
- Emitted when a connection with the server is established, [code]protocol[/code] will contain the sub-protocol agreed with the server.
- </description>
- </signal>
- <signal name="data_received">
- <description>
- Emitted when a WebSocket message is received.
- [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer.
- </description>
- </signal>
- <signal name="server_close_request">
- <argument index="0" name="code" type="int" />
- <argument index="1" name="reason" type="String" />
- <description>
- Emitted when the server requests a clean close. You should keep polling until you get a [signal connection_closed] signal to achieve the clean close. See [method WebSocketPeer.close] for more details.
- </description>
- </signal>
- </signals>
-</class>
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index 4a617f4c82..7e896a0ca3 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -10,34 +10,66 @@
<tutorials>
</tutorials>
<methods>
+ <method name="create_client">
+ <return type="int" enum="Error" />
+ <param index="0" name="url" type="String" />
+ <param index="1" name="verify_tls" type="bool" default="true" />
+ <param index="2" name="tls_certificate" type="X509Certificate" default="null" />
+ <description>
+ Starts a new multiplayer client connecting to the given [param url]. If [param verify_tls] is [code]false[/code] certificate validation will be disabled. If specified, the [param tls_certificate] will be used to verify the TLS host.
+ [b]Note[/b]: It is recommended to specify the scheme part of the URL, i.e. the [param url] should start with either [code]ws://[/code] or [code]wss://[/code].
+ </description>
+ </method>
+ <method name="create_server">
+ <return type="int" enum="Error" />
+ <param index="0" name="port" type="int" />
+ <param index="1" name="bind_address" type="String" default="&quot;*&quot;" />
+ <param index="2" name="tls_key" type="CryptoKey" default="null" />
+ <param index="3" name="tls_certificate" type="X509Certificate" default="null" />
+ <description>
+ Starts a new multiplayer server listening on the given [param port]. You can optionally specify a [param bind_address], and provide a [param tls_key] and [param tls_certificate] to use TLS.
+ </description>
+ </method>
<method name="get_peer" qualifiers="const">
<return type="WebSocketPeer" />
- <argument index="0" name="peer_id" type="int" />
+ <param index="0" name="peer_id" type="int" />
<description>
Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
</description>
</method>
- <method name="set_buffers">
- <return type="int" enum="Error" />
- <argument index="0" name="input_buffer_size_kb" type="int" />
- <argument index="1" name="input_max_packets" type="int" />
- <argument index="2" name="output_buffer_size_kb" type="int" />
- <argument index="3" name="output_max_packets" type="int" />
+ <method name="get_peer_address" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="id" type="int" />
<description>
- Configures the buffer sizes for this WebSocket peer. Default values can be specified in the Project Settings under [code]network/limits[/code]. For server, values are meant per connected peer.
- The first two parameters define the size and queued packets limits of the input buffer, the last two of the output buffer.
- Buffer sizes are expressed in KiB, so [code]4 = 2^12 = 4096 bytes[/code]. All parameters will be rounded up to the nearest power of two.
- [b]Note:[/b] HTML5 exports only use the input buffer since the output one is managed by browsers.
+ Returns the IP address of the given peer.
</description>
</method>
- </methods>
- <signals>
- <signal name="peer_packet">
- <argument index="0" name="peer_source" type="int" />
+ <method name="get_peer_port" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="id" type="int" />
<description>
- Emitted when a packet is received from a peer.
- [b]Note:[/b] This signal is only emitted when the client or server is configured to use Godot multiplayer API.
+ Returns the remote port of the given peer.
</description>
- </signal>
- </signals>
+ </method>
+ </methods>
+ <members>
+ <member name="handshake_headers" type="PackedStringArray" setter="set_handshake_headers" getter="get_handshake_headers" default="PackedStringArray()">
+ The extra headers to use during handshake. See [member WebSocketPeer.handshake_headers] for more details.
+ </member>
+ <member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0">
+ The maximum time each peer can stay in a connecting state before being dropped.
+ </member>
+ <member name="inbound_buffer_size" type="int" setter="set_inbound_buffer_size" getter="get_inbound_buffer_size" default="65535">
+ The inbound buffer size for connected peers. See [member WebSocketPeer.inbound_buffer_size] for more details.
+ </member>
+ <member name="max_queued_packets" type="int" setter="set_max_queued_packets" getter="get_max_queued_packets" default="2048">
+ The maximum number of queued packets for connected peers. See [member WebSocketPeer.max_queued_packets] for more details.
+ </member>
+ <member name="outbound_buffer_size" type="int" setter="set_outbound_buffer_size" getter="get_outbound_buffer_size" default="65535">
+ The outbound buffer size for connected peers. See [member WebSocketPeer.outbound_buffer_size] for more details.
+ </member>
+ <member name="supported_protocols" type="PackedStringArray" setter="set_supported_protocols" getter="get_supported_protocols" default="PackedStringArray()">
+ The supported WebSocket sub-protocols. See [member WebSocketPeer.supported_protocols] for more details.
+ </member>
+ </members>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index 6466654517..41d166a0f5 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -1,70 +1,147 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebSocketPeer" inherits="PacketPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A class representing a specific WebSocket connection.
+ A WebSocket connection.
</brief_description>
<description>
- This class represents a specific WebSocket connection, allowing you to do lower level operations with it.
- You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer.
+ This class represents WebSocket connection, and can be used as a WebSocket client (RFC 6455-compliant) or as a remote peer of a WebSocket server.
+ You can send WebSocket binary frames using [method PacketPeer.put_packet], and WebSocket text frames using [method send] (prefer text frames when interacting with text-based API). You can check the frame type of the last packet via [method was_string_packet].
+ To start a WebSocket client, first call [method connect_to_url], then regularly call [method poll] (e.g. during [Node] process). You can query the socket state via [method get_ready_state], get the number of pending packets using [method PacketPeer.get_available_packet_count], and retrieve them via [method PacketPeer.get_packet].
+ [codeblocks]
+ [gdscript]
+ extends Node
+
+ var socket = WebSocketPeer.new()
+
+ func _ready():
+ socket.connect_to_url("wss://example.com")
+
+ func _process(delta):
+ socket.poll()
+ var state = socket.get_ready_state()
+ if state == WebSocketPeer.STATE_OPEN:
+ while socket.get_available_packet_count():
+ print("Packet: ", socket.get_packet())
+ elif state == WebSocketPeer.STATE_CLOSING:
+ # Keep polling to achieve proper close.
+ pass
+ elif state == WebSocketPeer.STATE_CLOSED:
+ var code = socket.get_close_code()
+ var reason = socket.get_close_reason()
+ print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1])
+ set_process(false) # Stop processing.
+ [/gdscript]
+ [/codeblocks]
+ To use the peer as part of a WebSocket server refer to [method accept_stream] and the online tutorial.
</description>
<tutorials>
</tutorials>
<methods>
+ <method name="accept_stream">
+ <return type="int" enum="Error" />
+ <param index="0" name="stream" type="StreamPeer" />
+ <description>
+ Accepts a peer connection performing the HTTP handshake as a WebSocket server. The [param stream] must be a valid TCP stream retrieved via [method TCPServer.take_connection], or a TLS stream accepted via [method StreamPeerTLS.accept_stream].
+ [b]Note:[/b] Not supported in Web exports due to browsers' restrictions.
+ </description>
+ </method>
<method name="close">
<return type="void" />
- <argument index="0" name="code" type="int" default="1000" />
- <argument index="1" name="reason" type="String" default="&quot;&quot;" />
+ <param index="0" name="code" type="int" default="1000" />
+ <param index="1" name="reason" type="String" default="&quot;&quot;" />
<description>
- Closes this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [code]reason[/code] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes).
- [b]Note:[/b] To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received.
- [b]Note:[/b] The HTML5 export might not support all status codes. Please refer to browser-specific documentation for more details.
+ Closes this WebSocket connection. [param code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [param reason] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes). If [param code] is negative, the connection will be closed immediately without notifying the remote peer.
+ [b]Note:[/b] To achieve a clean close, you will need to keep polling until [constant STATE_CLOSED] is reached.
+ [b]Note:[/b] The Web export might not support all status codes. Please refer to browser-specific documentation for more details.
+ </description>
+ </method>
+ <method name="connect_to_url">
+ <return type="int" enum="Error" />
+ <param index="0" name="url" type="String" />
+ <param index="1" name="verify_tls" type="bool" default="true" />
+ <param index="2" name="trusted_tls_certificate" type="X509Certificate" default="null" />
+ <description>
+ Connects to the given URL. If [param verify_tls] is [code]false[/code] certificate validation will be disabled. If specified, the [param trusted_tls_certificate] will be the only one accepted when connecting to a TLS host.
+ [b]Note:[/b] To avoid mixed content warnings or errors in Web, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's TLS certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the TLS certificate.
+ </description>
+ </method>
+ <method name="get_close_code" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the received WebSocket close frame status code, or [code]-1[/code] when the connection was not cleanly closed. Only call this method when [method get_ready_state] returns [constant STATE_CLOSED].
+ </description>
+ </method>
+ <method name="get_close_reason" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the received WebSocket close frame status reason string. Only call this method when [method get_ready_state] returns [constant STATE_CLOSED].
</description>
</method>
<method name="get_connected_host" qualifiers="const">
<return type="String" />
<description>
Returns the IP address of the connected peer.
- [b]Note:[/b] Not available in the HTML5 export.
+ [b]Note:[/b] Not available in the Web export.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
<return type="int" />
<description>
Returns the remote port of the connected peer.
- [b]Note:[/b] Not available in the HTML5 export.
+ [b]Note:[/b] Not available in the Web export.
</description>
</method>
<method name="get_current_outbound_buffered_amount" qualifiers="const">
<return type="int" />
<description>
- Returns the current amount of data in the outbound websocket buffer. [b]Note:[/b] HTML5 exports use WebSocket.bufferedAmount, while other platforms use an internal buffer.
+ Returns the current amount of data in the outbound websocket buffer. [b]Note:[/b] Web exports use WebSocket.bufferedAmount, while other platforms use an internal buffer.
</description>
</method>
- <method name="get_write_mode" qualifiers="const">
- <return type="int" enum="WebSocketPeer.WriteMode" />
+ <method name="get_ready_state" qualifiers="const">
+ <return type="int" enum="WebSocketPeer.State" />
<description>
- Gets the current selected write mode. See [enum WriteMode].
+ Returns the ready state of the connection. See [enum State].
</description>
</method>
- <method name="is_connected_to_host" qualifiers="const">
- <return type="bool" />
+ <method name="get_requested_url" qualifiers="const">
+ <return type="String" />
<description>
- Returns [code]true[/code] if this peer is currently connected.
+ Returns the URL requested by this peer. The URL is derived from the [code]url[/code] passed to [method connect_to_url] or from the HTTP headers when acting as server (i.e. when using [method accept_stream]).
</description>
</method>
- <method name="set_no_delay">
+ <method name="get_selected_protocol" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the selected WebSocket sub-protocol for this connection or an empty string if the sub-protocol has not been selected yet.
+ </description>
+ </method>
+ <method name="poll">
<return type="void" />
- <argument index="0" name="enabled" type="bool" />
<description>
- Disable Nagle's algorithm on the underling TCP socket (default). See [method StreamPeerTCP.set_no_delay] for more information.
- [b]Note:[/b] Not available in the HTML5 export.
+ Updates the connection state and receive incoming packets. Call this function regularly to keep it in a clean state.
</description>
</method>
- <method name="set_write_mode">
+ <method name="send">
+ <return type="int" enum="Error" />
+ <param index="0" name="message" type="PackedByteArray" />
+ <param index="1" name="write_mode" type="int" enum="WebSocketPeer.WriteMode" default="1" />
+ <description>
+ Sends the given [param message] using the desired [param write_mode]. When sending a [String], prefer using [method send_text].
+ </description>
+ </method>
+ <method name="send_text">
+ <return type="int" enum="Error" />
+ <param index="0" name="message" type="String" />
+ <description>
+ Sends the given [param message] using WebSocket text mode. Prefer this method over [method PacketPeer.put_packet] when interacting with third-party text-based API (e.g. when using [JSON] formatted messages).
+ </description>
+ </method>
+ <method name="set_no_delay">
<return type="void" />
- <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode" />
+ <param index="0" name="enabled" type="bool" />
<description>
- Sets the socket to use the given [enum WriteMode].
+ Disable Nagle's algorithm on the underling TCP socket (default). See [method StreamPeerTCP.set_no_delay] for more information.
+ [b]Note:[/b] Not available in the Web export.
</description>
</method>
<method name="was_string_packet" qualifiers="const">
@@ -74,6 +151,24 @@
</description>
</method>
</methods>
+ <members>
+ <member name="handshake_headers" type="PackedStringArray" setter="set_handshake_headers" getter="get_handshake_headers" default="PackedStringArray()">
+ The extra HTTP headers to be sent during the WebSocket handshake.
+ [b]Note:[/b] Not supported in Web exports due to browsers' restrictions.
+ </member>
+ <member name="inbound_buffer_size" type="int" setter="set_inbound_buffer_size" getter="get_inbound_buffer_size" default="65535">
+ The size of the input buffer in bytes (roughly the maximum amount of memory that will be allocated for the inbound packets).
+ </member>
+ <member name="max_queued_packets" type="int" setter="set_max_queued_packets" getter="get_max_queued_packets" default="2048">
+ The maximum amount of packets that will be allowed in the queues (both inbound and outbound).
+ </member>
+ <member name="outbound_buffer_size" type="int" setter="set_outbound_buffer_size" getter="get_outbound_buffer_size" default="65535">
+ The size of the input buffer in bytes (roughly the maximum amount of memory that will be allocated for the outbound packets).
+ </member>
+ <member name="supported_protocols" type="PackedStringArray" setter="set_supported_protocols" getter="get_supported_protocols" default="PackedStringArray()">
+ The WebSocket sub-protocols allowed during the WebSocket handshake.
+ </member>
+ </members>
<constants>
<constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
Specifies that WebSockets messages should be transferred as text payload (only valid UTF-8 is allowed).
@@ -81,5 +176,17 @@
<constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
Specifies that WebSockets messages should be transferred as binary payload (any byte combination is allowed).
</constant>
+ <constant name="STATE_CONNECTING" value="0" enum="State">
+ Socket has been created. The connection is not yet open.
+ </constant>
+ <constant name="STATE_OPEN" value="1" enum="State">
+ The connection is open and ready to communicate.
+ </constant>
+ <constant name="STATE_CLOSING" value="2" enum="State">
+ The connection is in the process of closing. This means a close request has been sent to the remote peer but confirmation has not been received.
+ </constant>
+ <constant name="STATE_CLOSED" value="3" enum="State">
+ The connection is closed or couldn't be opened.
+ </constant>
</constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
deleted file mode 100644
index 46b0274de3..0000000000
--- a/modules/websocket/doc_classes/WebSocketServer.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketServer" inherits="WebSocketMultiplayerPeer" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
- <brief_description>
- A WebSocket server implementation.
- </brief_description>
- <description>
- This class implements a WebSocket server that can also support the high-level multiplayer API.
- After starting the server ([method listen]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
- [b]Note:[/b] Not available in HTML5 exports.
- [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="disconnect_peer">
- <return type="void" />
- <argument index="0" name="id" type="int" />
- <argument index="1" name="code" type="int" default="1000" />
- <argument index="2" name="reason" type="String" default="&quot;&quot;" />
- <description>
- Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information.
- </description>
- </method>
- <method name="get_peer_address" qualifiers="const">
- <return type="String" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns the IP address of the given peer.
- </description>
- </method>
- <method name="get_peer_port" qualifiers="const">
- <return type="int" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns the remote port of the given peer.
- </description>
- </method>
- <method name="has_peer" qualifiers="const">
- <return type="bool" />
- <argument index="0" name="id" type="int" />
- <description>
- Returns [code]true[/code] if a peer with the given ID is connected.
- </description>
- </method>
- <method name="is_listening" qualifiers="const">
- <return type="bool" />
- <description>
- Returns [code]true[/code] if the server is actively listening on a port.
- </description>
- </method>
- <method name="listen">
- <return type="int" enum="Error" />
- <argument index="0" name="port" type="int" />
- <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" />
- <argument index="2" name="gd_mp_api" type="bool" default="false" />
- <description>
- Starts listening on the given port.
- You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested.
- If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a multiplayer peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted.
- If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.), on the [WebSocketPeer] returned via [code]get_peer(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]).
- </description>
- </method>
- <method name="set_extra_headers">
- <return type="void" />
- <argument index="0" name="headers" type="PackedStringArray" default="PackedStringArray()" />
- <description>
- Sets additional headers to be sent to clients during the HTTP handshake.
- </description>
- </method>
- <method name="stop">
- <return type="void" />
- <description>
- Stops the server and clear its state.
- </description>
- </method>
- </methods>
- <members>
- <member name="bind_ip" type="String" setter="set_bind_ip" getter="get_bind_ip" default="&quot;*&quot;">
- When not set to [code]*[/code] will restrict incoming connections to the specified IP address. Setting [code]bind_ip[/code] to [code]127.0.0.1[/code] will cause the server to listen only to the local host.
- </member>
- <member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain">
- When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake.
- </member>
- <member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0">
- The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected.
- </member>
- <member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key">
- When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol).
- </member>
- <member name="ssl_certificate" type="X509Certificate" setter="set_ssl_certificate" getter="get_ssl_certificate">
- When set to a valid [X509Certificate] (along with [member private_key]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol).
- </member>
- </members>
- <signals>
- <signal name="client_close_request">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="code" type="int" />
- <argument index="2" name="reason" type="String" />
- <description>
- Emitted when a client requests a clean close. You should keep polling until you get a [signal client_disconnected] signal with the same [code]id[/code] to achieve the clean close. See [method WebSocketPeer.close] for more details.
- </description>
- </signal>
- <signal name="client_connected">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="protocol" type="String" />
- <argument index="2" name="resource_name" type="String" />
- <description>
- Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client, and "resource_name" will be the resource name of the URI the peer used.
- "resource_name" is a path (at the very least a single forward slash) and potentially a query string.
- </description>
- </signal>
- <signal name="client_disconnected">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="was_clean_close" type="bool" />
- <description>
- Emitted when a client disconnects. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly.
- </description>
- </signal>
- <signal name="data_received">
- <argument index="0" name="id" type="int" />
- <description>
- Emitted when a new message is received.
- [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer.
- </description>
- </signal>
- </signals>
-</class>
diff --git a/modules/websocket/editor/editor_debugger_server_websocket.cpp b/modules/websocket/editor/editor_debugger_server_websocket.cpp
index 0443147d98..35fc051bc3 100644
--- a/modules/websocket/editor/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor/editor_debugger_server_websocket.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* editor_debugger_server_websocket.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_debugger_server_websocket.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "editor_debugger_server_websocket.h"
@@ -38,18 +38,31 @@
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
-void EditorDebuggerServerWebSocket::_peer_connected(int p_id, String _protocol) {
- pending_peers.push_back(p_id);
-}
+void EditorDebuggerServerWebSocket::poll() {
+ if (pending_peer.is_null() && tcp_server->is_connection_available()) {
+ Ref<WebSocketPeer> peer = Ref<WebSocketPeer>(WebSocketPeer::create());
+ ERR_FAIL_COND(peer.is_null()); // Bug.
-void EditorDebuggerServerWebSocket::_peer_disconnected(int p_id, bool p_was_clean) {
- if (pending_peers.find(p_id)) {
- pending_peers.erase(p_id);
- }
-}
+ Vector<String> ws_protocols;
+ ws_protocols.push_back("binary"); // Compatibility for emscripten TCP-to-WebSocket.
+ peer->set_supported_protocols(ws_protocols);
-void EditorDebuggerServerWebSocket::poll() {
- server->poll();
+ Error err = peer->accept_stream(tcp_server->take_connection());
+ if (err == OK) {
+ pending_timer = OS::get_singleton()->get_ticks_msec();
+ pending_peer = peer;
+ }
+ }
+ if (pending_peer.is_valid() && pending_peer->get_ready_state() != WebSocketPeer::STATE_OPEN) {
+ pending_peer->poll();
+ WebSocketPeer::State ready_state = pending_peer->get_ready_state();
+ if (ready_state != WebSocketPeer::STATE_CONNECTING && ready_state != WebSocketPeer::STATE_OPEN) {
+ pending_peer.unref(); // Failed.
+ }
+ if (ready_state == WebSocketPeer::STATE_CONNECTING && OS::get_singleton()->get_ticks_msec() - pending_timer > 3000) {
+ pending_peer.unref(); // Timeout.
+ }
+ }
}
String EditorDebuggerServerWebSocket::get_uri() const {
@@ -58,8 +71,8 @@ String EditorDebuggerServerWebSocket::get_uri() const {
Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
// Default host and port
- String bind_host = (String)EditorSettings::get_singleton()->get("network/debug/remote_host");
- int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+ String bind_host = (String)EDITOR_GET("network/debug/remote_host");
+ int bind_port = (int)EDITOR_GET("network/debug/remote_port");
// Optionally override
if (!p_uri.is_empty() && p_uri != "ws://") {
@@ -69,15 +82,10 @@ Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
}
- // Set up the server
- server->set_bind_ip(bind_host);
- Vector<String> compatible_protocols;
- compatible_protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer.
-
// Try listening on ports
const int max_attempts = 5;
for (int attempt = 1;; ++attempt) {
- const Error err = server->listen(bind_port, compatible_protocols);
+ const Error err = tcp_server->listen(bind_port, bind_host);
if (err == OK) {
break;
}
@@ -96,31 +104,27 @@ Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
}
void EditorDebuggerServerWebSocket::stop() {
- server->stop();
- pending_peers.clear();
+ pending_peer.unref();
+ tcp_server->stop();
}
bool EditorDebuggerServerWebSocket::is_active() const {
- return server->is_listening();
+ return tcp_server->is_listening();
}
bool EditorDebuggerServerWebSocket::is_connection_available() const {
- return pending_peers.size() > 0;
+ return pending_peer.is_valid() && pending_peer->get_ready_state() == WebSocketPeer::STATE_OPEN;
}
Ref<RemoteDebuggerPeer> EditorDebuggerServerWebSocket::take_connection() {
ERR_FAIL_COND_V(!is_connection_available(), Ref<RemoteDebuggerPeer>());
- RemoteDebuggerPeer *peer = memnew(RemoteDebuggerPeerWebSocket(server->get_peer(pending_peers[0])));
- pending_peers.pop_front();
+ RemoteDebuggerPeer *peer = memnew(RemoteDebuggerPeerWebSocket(pending_peer));
+ pending_peer.unref();
return peer;
}
EditorDebuggerServerWebSocket::EditorDebuggerServerWebSocket() {
- server = Ref<WebSocketServer>(WebSocketServer::create());
- int max_pkts = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages");
- server->set_buffers(8192, max_pkts, 8192, max_pkts);
- server->connect("client_connected", callable_mp(this, &EditorDebuggerServerWebSocket::_peer_connected));
- server->connect("client_disconnected", callable_mp(this, &EditorDebuggerServerWebSocket::_peer_disconnected));
+ tcp_server.instantiate();
}
EditorDebuggerServerWebSocket::~EditorDebuggerServerWebSocket() {
diff --git a/modules/websocket/editor/editor_debugger_server_websocket.h b/modules/websocket/editor/editor_debugger_server_websocket.h
index 7c0705302d..aeca227c59 100644
--- a/modules/websocket/editor/editor_debugger_server_websocket.h
+++ b/modules/websocket/editor/editor_debugger_server_websocket.h
@@ -1,47 +1,50 @@
-/*************************************************************************/
-/* editor_debugger_server_websocket.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* editor_debugger_server_websocket.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EDITOR_DEBUGGER_SERVER_WEBSOCKET_H
#define EDITOR_DEBUGGER_SERVER_WEBSOCKET_H
#ifdef TOOLS_ENABLED
-#include "../websocket_server.h"
+#include "../websocket_peer.h"
+
+#include "core/io/tcp_server.h"
#include "editor/debugger/editor_debugger_server.h"
class EditorDebuggerServerWebSocket : public EditorDebuggerServer {
GDCLASS(EditorDebuggerServerWebSocket, EditorDebuggerServer);
private:
- Ref<WebSocketServer> server;
- List<int> pending_peers;
+ Ref<TCPServer> tcp_server;
+ Ref<WebSocketPeer> pending_peer;
+ uint64_t pending_timer = 0;
String endpoint;
public:
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
deleted file mode 100644
index e051a3b564..0000000000
--- a/modules/websocket/emws_client.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*************************************************************************/
-/* emws_client.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "emws_client.h"
-
-#include "core/config/project_settings.h"
-#include "core/io/ip.h"
-#include "emscripten.h"
-
-void EMWSClient::_esws_on_connect(void *obj, char *proto) {
- EMWSClient *client = static_cast<EMWSClient *>(obj);
- client->_is_connecting = false;
- client->_on_connect(String(proto));
-}
-
-void EMWSClient::_esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string) {
- EMWSClient *client = static_cast<EMWSClient *>(obj);
-
- Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
- if (err == OK) {
- client->_on_peer_packet();
- }
-}
-
-void EMWSClient::_esws_on_error(void *obj) {
- EMWSClient *client = static_cast<EMWSClient *>(obj);
- client->_is_connecting = false;
- client->_on_error();
-}
-
-void EMWSClient::_esws_on_close(void *obj, int code, const char *reason, int was_clean) {
- EMWSClient *client = static_cast<EMWSClient *>(obj);
- client->_on_close_request(code, String(reason));
- client->_is_connecting = false;
- client->disconnect_from_host();
- client->_on_disconnect(was_clean != 0);
-}
-
-Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
- if (_js_id) {
- godot_js_websocket_destroy(_js_id);
- _js_id = 0;
- }
-
- String proto_string;
- for (int i = 0; i < p_protocols.size(); i++) {
- if (i != 0) {
- proto_string += ",";
- }
- proto_string += p_protocols[i];
- }
-
- String str = "ws://";
-
- if (p_custom_headers.size()) {
- WARN_PRINT_ONCE("Custom headers are not supported in HTML5 platform.");
- }
- if (p_ssl) {
- str = "wss://";
- if (ssl_cert.is_valid()) {
- WARN_PRINT_ONCE("Custom SSL certificate is not supported in HTML5 platform.");
- }
- }
- str += p_host + ":" + itos(p_port) + p_path;
- _is_connecting = true;
-
- _js_id = godot_js_websocket_create(this, str.utf8().get_data(), proto_string.utf8().get_data(), &_esws_on_connect, &_esws_on_message, &_esws_on_error, &_esws_on_close);
- if (!_js_id) {
- return FAILED;
- }
-
- static_cast<Ref<EMWSPeer>>(_peer)->set_sock(_js_id, _in_buf_size, _in_pkt_size, _out_buf_size);
-
- return OK;
-}
-
-void EMWSClient::poll() {
-}
-
-Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const {
- return _peer;
-}
-
-MultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const {
- if (_peer->is_connected_to_host()) {
- if (_is_connecting) {
- return CONNECTION_CONNECTING;
- }
- return CONNECTION_CONNECTED;
- }
-
- return CONNECTION_DISCONNECTED;
-}
-
-void EMWSClient::disconnect_from_host(int p_code, String p_reason) {
- _peer->close(p_code, p_reason);
-}
-
-IPAddress EMWSClient::get_connected_host() const {
- ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
-}
-
-uint16_t EMWSClient::get_connected_port() const {
- ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
-}
-
-int EMWSClient::get_max_packet_size() const {
- return (1 << _in_buf_size) - PROTO_SIZE;
-}
-
-Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
- _in_pkt_size = nearest_shift(p_in_packets - 1);
- _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
- return OK;
-}
-
-EMWSClient::EMWSClient() {
- _peer = Ref<EMWSPeer>(memnew(EMWSPeer));
-}
-
-EMWSClient::~EMWSClient() {
- disconnect_from_host();
- _peer = Ref<EMWSPeer>();
- if (_js_id) {
- godot_js_websocket_destroy(_js_id);
- }
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
deleted file mode 100644
index b71fd78124..0000000000
--- a/modules/websocket/emws_client.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*************************************************************************/
-/* emws_client.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 EMWS_CLIENT_H
-#define EMWS_CLIENT_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/error/error_list.h"
-#include "emws_peer.h"
-#include "websocket_client.h"
-
-class EMWSClient : public WebSocketClient {
- GDCIIMPL(EMWSClient, WebSocketClient);
-
-private:
- int _js_id = 0;
- bool _is_connecting = false;
- int _in_buf_size = DEF_BUF_SHIFT;
- int _in_pkt_size = DEF_PKT_SHIFT;
- int _out_buf_size = DEF_BUF_SHIFT;
-
- static void _esws_on_connect(void *obj, char *proto);
- static void _esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string);
- static void _esws_on_error(void *obj);
- static void _esws_on_close(void *obj, int code, const char *reason, int was_clean);
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override;
- Ref<WebSocketPeer> get_peer(int p_peer_id) const override;
- void disconnect_from_host(int p_code = 1000, String p_reason = "") override;
- IPAddress get_connected_host() const override;
- uint16_t get_connected_port() const override;
- virtual ConnectionStatus get_connection_status() const override;
- int get_max_packet_size() const override;
- virtual void poll() override;
- EMWSClient();
- ~EMWSClient();
-};
-
-#endif // JAVASCRIPT_ENABLED
-
-#endif // EMWS_CLIENT_H
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 86169f88e9..1ec557427f 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -1,88 +1,153 @@
-/*************************************************************************/
-/* emws_peer.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
+/**************************************************************************/
+/* emws_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef WEB_ENABLED
#include "emws_peer.h"
#include "core/io/ip.h"
-void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size) {
- peer_sock = p_sock;
- _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
- _packet_buffer.resize((1 << p_in_buf_size));
- _out_buf_size = p_out_buf_size;
+void EMWSPeer::_esws_on_connect(void *p_obj, char *p_proto) {
+ EMWSPeer *peer = static_cast<EMWSPeer *>(p_obj);
+ peer->ready_state = STATE_OPEN;
+ peer->selected_protocol.parse_utf8(p_proto);
}
-void EMWSPeer::set_write_mode(WriteMode p_mode) {
- write_mode = p_mode;
+void EMWSPeer::_esws_on_message(void *p_obj, const uint8_t *p_data, int p_data_size, int p_is_string) {
+ EMWSPeer *peer = static_cast<EMWSPeer *>(p_obj);
+ uint8_t is_string = p_is_string ? 1 : 0;
+ peer->in_buffer.write_packet(p_data, p_data_size, &is_string);
}
-EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
- return write_mode;
+void EMWSPeer::_esws_on_error(void *p_obj) {
+ EMWSPeer *peer = static_cast<EMWSPeer *>(p_obj);
+ peer->ready_state = STATE_CLOSED;
}
-Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- uint8_t is_string = p_is_string ? 1 : 0;
- return _in_buffer.write_packet(p_data, p_size, &is_string);
+void EMWSPeer::_esws_on_close(void *p_obj, int p_code, const char *p_reason, int p_was_clean) {
+ EMWSPeer *peer = static_cast<EMWSPeer *>(p_obj);
+ peer->close_code = p_code;
+ peer->close_reason.parse_utf8(p_reason);
+ peer->ready_state = STATE_CLOSED;
}
-Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
+Error EMWSPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate) {
+ ERR_FAIL_COND_V(ready_state != STATE_CLOSED, ERR_ALREADY_IN_USE);
+ _clear();
- int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0;
+ String host;
+ String path;
+ String scheme;
+ int port = 0;
+ Error err = p_url.parse_url(scheme, host, port, path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
+
+ if (scheme.is_empty()) {
+ scheme = "ws://";
+ }
+ ERR_FAIL_COND_V_MSG(scheme != "ws://" && scheme != "wss://", ERR_INVALID_PARAMETER, vformat("Invalid protocol: \"%s\" (must be either \"ws://\" or \"wss://\").", scheme));
+
+ String proto_string;
+ for (int i = 0; i < supported_protocols.size(); i++) {
+ if (i != 0) {
+ proto_string += ",";
+ }
+ proto_string += supported_protocols[i];
+ }
- if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin) != 0) {
+ if (handshake_headers.size()) {
+ WARN_PRINT_ONCE("Custom headers are not supported in Web platform.");
+ }
+ if (p_tls_certificate.is_valid()) {
+ WARN_PRINT_ONCE("Custom SSL certificates are not supported in Web platform.");
+ }
+
+ requested_url = scheme + host;
+
+ if (port && ((scheme == "ws://" && port != 80) || (scheme == "wss://" && port != 443))) {
+ requested_url += ":" + String::num(port);
+ }
+
+ if (!path.is_empty()) {
+ requested_url += path;
+ }
+
+ peer_sock = godot_js_websocket_create(this, requested_url.utf8().get_data(), proto_string.utf8().get_data(), &_esws_on_connect, &_esws_on_message, &_esws_on_error, &_esws_on_close);
+ if (peer_sock == -1) {
return FAILED;
}
+ in_buffer.resize(nearest_shift(inbound_buffer_size), max_queued_packets);
+ packet_buffer.resize(inbound_buffer_size);
+ ready_state = STATE_CONNECTING;
+ return OK;
+}
+Error EMWSPeer::accept_stream(Ref<StreamPeer> p_stream) {
+ WARN_PRINT_ONCE("Acting as WebSocket server is not supported in Web platforms.");
+ return ERR_UNAVAILABLE;
+}
+
+Error EMWSPeer::_send(const uint8_t *p_buffer, int p_buffer_size, bool p_binary) {
+ ERR_FAIL_COND_V(outbound_buffer_size > 0 && (get_current_outbound_buffered_amount() + p_buffer_size >= outbound_buffer_size), ERR_OUT_OF_MEMORY);
+
+ if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, p_binary ? 1 : 0) != 0) {
+ return FAILED;
+ }
return OK;
}
+Error EMWSPeer::send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) {
+ return _send(p_buffer, p_buffer_size, p_mode == WRITE_MODE_BINARY);
+}
+
+Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ return _send(p_buffer, p_buffer_size, true);
+}
+
Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- if (_in_buffer.packets_left() == 0) {
+ if (in_buffer.packets_left() == 0) {
return ERR_UNAVAILABLE;
}
int read = 0;
- Error err = _in_buffer.read_packet(_packet_buffer.ptrw(), _packet_buffer.size(), &_is_string, read);
+ Error err = in_buffer.read_packet(packet_buffer.ptrw(), packet_buffer.size(), &was_string, read);
ERR_FAIL_COND_V(err != OK, err);
- *r_buffer = _packet_buffer.ptr();
+ *r_buffer = packet_buffer.ptr();
r_buffer_size = read;
return OK;
}
int EMWSPeer::get_available_packet_count() const {
- return _in_buffer.packets_left();
+ return in_buffer.packets_left();
}
int EMWSPeer::get_current_outbound_buffered_amount() const {
@@ -93,40 +158,85 @@ int EMWSPeer::get_current_outbound_buffered_amount() const {
}
bool EMWSPeer::was_string_packet() const {
- return _is_string;
+ return was_string;
}
-bool EMWSPeer::is_connected_to_host() const {
- return peer_sock != -1;
+void EMWSPeer::_clear() {
+ if (peer_sock != -1) {
+ godot_js_websocket_destroy(peer_sock);
+ peer_sock = -1;
+ }
+ ready_state = STATE_CLOSED;
+ was_string = 0;
+ close_code = -1;
+ close_reason.clear();
+ selected_protocol.clear();
+ requested_url.clear();
+ in_buffer.clear();
+ packet_buffer.clear();
}
void EMWSPeer::close(int p_code, String p_reason) {
- if (peer_sock != -1) {
- godot_js_websocket_close(peer_sock, p_code, p_reason.utf8().get_data());
+ if (p_code < 0) {
+ if (peer_sock != -1) {
+ godot_js_websocket_destroy(peer_sock);
+ peer_sock = -1;
+ }
+ ready_state = STATE_CLOSED;
+ }
+ if (ready_state == STATE_CONNECTING || ready_state == STATE_OPEN) {
+ ready_state = STATE_CLOSING;
+ if (peer_sock != -1) {
+ godot_js_websocket_close(peer_sock, p_code, p_reason.utf8().get_data());
+ } else {
+ ready_state = STATE_CLOSED;
+ }
}
- _is_string = 0;
- _in_buffer.clear();
- peer_sock = -1;
+ in_buffer.clear();
+ packet_buffer.clear();
+}
+
+void EMWSPeer::poll() {
+ // Automatically polled by the navigator.
+}
+
+WebSocketPeer::State EMWSPeer::get_ready_state() const {
+ return ready_state;
+}
+
+int EMWSPeer::get_close_code() const {
+ return close_code;
+}
+
+String EMWSPeer::get_close_reason() const {
+ return close_reason;
+}
+
+String EMWSPeer::get_selected_protocol() const {
+ return selected_protocol;
+}
+
+String EMWSPeer::get_requested_url() const {
+ return requested_url;
}
IPAddress EMWSPeer::get_connected_host() const {
- ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
+ ERR_FAIL_V_MSG(IPAddress(), "Not supported in Web export.");
}
uint16_t EMWSPeer::get_connected_port() const {
- ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
+ ERR_FAIL_V_MSG(0, "Not supported in Web export.");
}
void EMWSPeer::set_no_delay(bool p_enabled) {
- ERR_FAIL_MSG("'set_no_delay' is not supported in HTML5 export.");
+ ERR_FAIL_MSG("'set_no_delay' is not supported in Web export.");
}
EMWSPeer::EMWSPeer() {
- close();
}
EMWSPeer::~EMWSPeer() {
- close();
+ _clear();
}
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index f52f615c35..66c5283d5c 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -1,37 +1,37 @@
-/*************************************************************************/
-/* emws_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* emws_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 EMWS_PEER_H
#define EMWS_PEER_H
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
@@ -54,33 +54,53 @@ extern void godot_js_websocket_destroy(int p_id);
}
class EMWSPeer : public WebSocketPeer {
- GDCIIMPL(EMWSPeer, WebSocketPeer);
-
private:
int peer_sock = -1;
- WriteMode write_mode = WRITE_MODE_BINARY;
- Vector<uint8_t> _packet_buffer;
- PacketBuffer<uint8_t> _in_buffer;
- uint8_t _is_string = 0;
- int _out_buf_size = 0;
+ State ready_state = STATE_CLOSED;
+ Vector<uint8_t> packet_buffer;
+ PacketBuffer<uint8_t> in_buffer;
+ uint8_t was_string = 0;
+ int close_code = -1;
+ String close_reason;
+ String selected_protocol;
+ String requested_url;
+
+ static WebSocketPeer *_create() { return memnew(EMWSPeer); }
+ static void _esws_on_connect(void *obj, char *proto);
+ static void _esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string);
+ static void _esws_on_error(void *obj);
+ static void _esws_on_close(void *obj, int code, const char *reason, int was_clean);
+
+ void _clear();
+ Error _send(const uint8_t *p_buffer, int p_buffer_size, bool p_binary);
public:
- Error read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string);
- void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size);
+ static void initialize() { WebSocketPeer::_create = EMWSPeer::_create; }
+
+ // PacketPeer
virtual int get_available_packet_count() const override;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const override { return _packet_buffer.size(); };
- virtual int get_current_outbound_buffered_amount() const override;
+ virtual int get_max_packet_size() const override { return packet_buffer.size(); };
+ // WebSocketPeer
+ virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) override;
+ virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) override;
+ virtual Error accept_stream(Ref<StreamPeer> p_stream) override;
virtual void close(int p_code = 1000, String p_reason = "") override;
- virtual bool is_connected_to_host() const override;
+ virtual void poll() override;
+
+ virtual State get_ready_state() const override;
+ virtual int get_close_code() const override;
+ virtual String get_close_reason() const override;
+ virtual int get_current_outbound_buffered_amount() const override;
+
virtual IPAddress get_connected_host() const override;
virtual uint16_t get_connected_port() const override;
+ virtual String get_selected_protocol() const override;
+ virtual String get_requested_url() const override;
- virtual WriteMode get_write_mode() const override;
- virtual void set_write_mode(WriteMode p_mode) override;
virtual bool was_string_packet() const override;
virtual void set_no_delay(bool p_enabled) override;
@@ -88,6 +108,6 @@ public:
~EMWSPeer();
};
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
#endif // EMWS_PEER_H
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
deleted file mode 100644
index 2033098cad..0000000000
--- a/modules/websocket/emws_server.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*************************************************************************/
-/* emws_server.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "emws_server.h"
-#include "core/os/os.h"
-
-void EMWSServer::set_extra_headers(const Vector<String> &p_headers) {
-}
-
-Error EMWSServer::listen(int p_port, Vector<String> p_protocols, bool gd_mp_api) {
- return FAILED;
-}
-
-bool EMWSServer::is_listening() const {
- return false;
-}
-
-void EMWSServer::stop() {
-}
-
-bool EMWSServer::has_peer(int p_id) const {
- return false;
-}
-
-Ref<WebSocketPeer> EMWSServer::get_peer(int p_id) const {
- return nullptr;
-}
-
-Vector<String> EMWSServer::get_protocols() const {
- Vector<String> out;
-
- return out;
-}
-
-IPAddress EMWSServer::get_peer_address(int p_peer_id) const {
- return IPAddress();
-}
-
-int EMWSServer::get_peer_port(int p_peer_id) const {
- return 0;
-}
-
-void EMWSServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
-}
-
-void EMWSServer::poll() {
-}
-
-int EMWSServer::get_max_packet_size() const {
- return 0;
-}
-
-Error EMWSServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- return OK;
-}
-
-EMWSServer::EMWSServer() {
-}
-
-EMWSServer::~EMWSServer() {
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
deleted file mode 100644
index 14a9449605..0000000000
--- a/modules/websocket/emws_server.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************/
-/* emws_server.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 EMWS_SERVER_H
-#define EMWS_SERVER_H
-
-#ifdef JAVASCRIPT_ENABLED
-
-#include "core/object/ref_counted.h"
-#include "emws_peer.h"
-#include "websocket_server.h"
-
-class EMWSServer : public WebSocketServer {
- GDCIIMPL(EMWSServer, WebSocketServer);
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- void set_extra_headers(const Vector<String> &p_headers) override;
- Error listen(int p_port, Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override;
- void stop() override;
- bool is_listening() const override;
- bool has_peer(int p_id) const override;
- Ref<WebSocketPeer> get_peer(int p_id) const override;
- IPAddress get_peer_address(int p_peer_id) const override;
- int get_peer_port(int p_peer_id) const override;
- void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override;
- int get_max_packet_size() const override;
- virtual void poll() override;
- virtual Vector<String> get_protocols() const;
-
- EMWSServer();
- ~EMWSServer();
-};
-
-#endif
-
-#endif // EMWS_SERVER_H
diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js
index 57f1f10b02..ed01c69725 100644
--- a/modules/websocket/library_godot_websocket.js
+++ b/modules/websocket/library_godot_websocket.js
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* library_godot_websocket.js */
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* library_godot_websocket.js */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
const GodotWebSocket = {
// Our socket implementation that forwards events to C++.
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
index 7b4a164576..25e1b1f15a 100644
--- a/modules/websocket/packet_buffer.h
+++ b/modules/websocket/packet_buffer.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* packet_buffer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* packet_buffer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 PACKET_BUFFER_H
#define PACKET_BUFFER_H
@@ -41,32 +41,29 @@ private:
T info;
} _Packet;
- RingBuffer<_Packet> _packets;
+ Vector<_Packet> _packets;
+ int _queued = 0;
+ int _write_pos = 0;
+ int _read_pos = 0;
RingBuffer<uint8_t> _payload;
public:
Error write_packet(const uint8_t *p_payload, uint32_t p_size, const T *p_info) {
-#ifdef TOOLS_ENABLED
- // Verbose buffer warnings
- if (p_payload && _payload.space_left() < (int32_t)p_size) {
- ERR_PRINT("Buffer payload full! Dropping data.");
- ERR_FAIL_V(ERR_OUT_OF_MEMORY);
- }
- if (p_info && _packets.space_left() < 1) {
- ERR_PRINT("Too many packets in queue! Dropping data.");
- ERR_FAIL_V(ERR_OUT_OF_MEMORY);
- }
-#else
- ERR_FAIL_COND_V(p_payload && (uint32_t)_payload.space_left() < p_size, ERR_OUT_OF_MEMORY);
- ERR_FAIL_COND_V(p_info && _packets.space_left() < 1, ERR_OUT_OF_MEMORY);
-#endif
+ ERR_FAIL_COND_V_MSG(p_payload && (uint32_t)_payload.space_left() < p_size, ERR_OUT_OF_MEMORY, "Buffer payload full! Dropping data.");
+ ERR_FAIL_COND_V_MSG(p_info && _queued >= _packets.size(), ERR_OUT_OF_MEMORY, "Too many packets in queue! Dropping data.");
// If p_info is nullptr, only the payload is written
if (p_info) {
+ ERR_FAIL_COND_V(_write_pos > _packets.size(), ERR_OUT_OF_MEMORY);
_Packet p;
p.size = p_size;
- memcpy(&p.info, p_info, sizeof(T));
- _packets.write(p);
+ p.info = *p_info;
+ _packets.write[_write_pos] = p;
+ _queued += 1;
+ _write_pos++;
+ if (_write_pos >= _packets.size()) {
+ _write_pos = 0;
+ }
}
// If p_payload is nullptr, only the packet information is written.
@@ -78,9 +75,14 @@ public:
}
Error read_packet(uint8_t *r_payload, int p_bytes, T *r_info, int &r_read) {
- ERR_FAIL_COND_V(_packets.data_left() < 1, ERR_UNAVAILABLE);
- _Packet p;
- _packets.read(&p, 1);
+ ERR_FAIL_COND_V(_queued < 1, ERR_UNAVAILABLE);
+ _Packet p = _packets[_read_pos];
+ _read_pos += 1;
+ if (_read_pos >= _packets.size()) {
+ _read_pos = 0;
+ }
+ _queued -= 1;
+
ERR_FAIL_COND_V(_payload.data_left() < (int)p.size, ERR_BUG);
ERR_FAIL_COND_V(p_bytes < (int)p.size, ERR_OUT_OF_MEMORY);
@@ -90,22 +92,24 @@ public:
return OK;
}
- void discard_payload(int p_size) {
- _packets.decrease_write(p_size);
- }
-
- void resize(int p_pkt_shift, int p_buf_shift) {
- _packets.resize(p_pkt_shift);
+ void resize(int p_buf_shift, int p_max_packets) {
_payload.resize(p_buf_shift);
+ _packets.resize(p_max_packets);
+ _read_pos = 0;
+ _write_pos = 0;
+ _queued = 0;
}
int packets_left() const {
- return _packets.data_left();
+ return _queued;
}
void clear() {
_payload.resize(0);
_packets.resize(0);
+ _read_pos = 0;
+ _write_pos = 0;
+ _queued = 0;
}
PacketBuffer() {
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index f562de111f..08935626d0 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -1,46 +1,48 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/config/project_settings.h"
+#include "core/debugger/engine_debugger.h"
#include "core/error/error_macros.h"
-#ifdef JAVASCRIPT_ENABLED
-#include "emscripten.h"
-#include "emws_client.h"
+#include "websocket_multiplayer_peer.h"
+#include "websocket_peer.h"
+
+#include "remote_debugger_peer_websocket.h"
+
+#ifdef WEB_ENABLED
#include "emws_peer.h"
-#include "emws_server.h"
#else
-#include "wsl_client.h"
-#include "wsl_server.h"
+#include "wsl_peer.h"
#endif
#ifdef TOOLS_ENABLED
@@ -56,21 +58,18 @@ static void _editor_init_callback() {
#endif
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();
+ if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) {
+#ifdef WEB_ENABLED
+ EMWSPeer::initialize();
#else
- WSLPeer::make_default();
- WSLClient::make_default();
- WSLServer::make_default();
+ WSLPeer::initialize();
#endif
- GDREGISTER_ABSTRACT_CLASS(WebSocketMultiplayerPeer);
- ClassDB::register_custom_instance_class<WebSocketServer>();
- ClassDB::register_custom_instance_class<WebSocketClient>();
+ GDREGISTER_CLASS(WebSocketMultiplayerPeer);
ClassDB::register_custom_instance_class<WebSocketPeer>();
+
+ EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
+ EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
}
#ifdef TOOLS_ENABLED
@@ -81,7 +80,10 @@ void initialize_websocket_module(ModuleInitializationLevel p_level) {
}
void uninitialize_websocket_module(ModuleInitializationLevel p_level) {
- if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_CORE) {
return;
}
+#ifndef WEB_ENABLED
+ WSLPeer::deinitialize();
+#endif
}
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
index dab42d6ed9..320878eceb 100644
--- a/modules/websocket/register_types.h
+++ b/modules/websocket/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBSOCKET_REGISTER_TYPES_H
#define WEBSOCKET_REGISTER_TYPES_H
diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp
index 6319c3c664..791b6d9ec9 100644
--- a/modules/websocket/remote_debugger_peer_websocket.cpp
+++ b/modules/websocket/remote_debugger_peer_websocket.cpp
@@ -1,62 +1,71 @@
-/*************************************************************************/
-/* remote_debugger_peer_websocket.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* remote_debugger_peer_websocket.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "remote_debugger_peer_websocket.h"
#include "core/config/project_settings.h"
Error RemoteDebuggerPeerWebSocket::connect_to_host(const String &p_uri) {
+ ws_peer = Ref<WebSocketPeer>(WebSocketPeer::create());
+ ERR_FAIL_COND_V(ws_peer.is_null(), ERR_BUG);
+
Vector<String> protocols;
protocols.push_back("binary"); // Compatibility for emscripten TCP-to-WebSocket.
- ws_client->connect_to_url(p_uri, protocols);
- ws_client->poll();
+ ws_peer->set_supported_protocols(protocols);
+ ws_peer->set_max_queued_packets(max_queued_messages);
+ ws_peer->set_inbound_buffer_size((1 << 23) - 1);
+ ws_peer->set_outbound_buffer_size((1 << 23) - 1);
+
+ Error err = ws_peer->connect_to_url(p_uri);
+ ERR_FAIL_COND_V(err != OK, err);
- if (ws_client->get_connection_status() == WebSocketClient::CONNECTION_DISCONNECTED) {
- ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(ws_client->get_connection_status()) + ".");
+ ws_peer->poll();
+ WebSocketPeer::State ready_state = ws_peer->get_ready_state();
+ if (ready_state != WebSocketPeer::STATE_CONNECTING && ready_state != WebSocketPeer::STATE_OPEN) {
+ ERR_PRINT(vformat("Remote Debugger: Unable to connect. State: %s.", ws_peer->get_ready_state()));
return FAILED;
}
- ws_peer = ws_client->get_peer(1);
-
return OK;
}
bool RemoteDebuggerPeerWebSocket::is_peer_connected() {
- return ws_peer.is_valid() && ws_peer->is_connected_to_host();
+ return ws_peer.is_valid() && (ws_peer->get_ready_state() == WebSocketPeer::STATE_OPEN || ws_peer->get_ready_state() == WebSocketPeer::STATE_CONNECTING);
}
void RemoteDebuggerPeerWebSocket::poll() {
- ws_client->poll();
+ ERR_FAIL_COND(ws_peer.is_null());
+ ws_peer->poll();
- while (ws_peer->is_connected_to_host() && ws_peer->get_available_packet_count() > 0 && in_queue.size() < max_queued_messages) {
+ while (ws_peer->get_ready_state() == WebSocketPeer::STATE_OPEN && ws_peer->get_available_packet_count() > 0 && in_queue.size() < max_queued_messages) {
Variant var;
Error err = ws_peer->get_var(var);
ERR_CONTINUE(err != OK);
@@ -64,7 +73,7 @@ void RemoteDebuggerPeerWebSocket::poll() {
in_queue.push_back(var);
}
- while (ws_peer->is_connected_to_host() && out_queue.size() > 0) {
+ while (ws_peer->get_ready_state() == WebSocketPeer::STATE_OPEN && out_queue.size() > 0) {
Array var = out_queue[0];
Error err = ws_peer->put_var(var);
ERR_BREAK(err != OK); // Peer buffer full?
@@ -73,7 +82,8 @@ void RemoteDebuggerPeerWebSocket::poll() {
}
int RemoteDebuggerPeerWebSocket::get_max_message_size() const {
- return 8 << 20; // 8 Mib
+ ERR_FAIL_COND_V(ws_peer.is_null(), 0);
+ return ws_peer->get_max_packet_size();
}
bool RemoteDebuggerPeerWebSocket::has_message() {
@@ -99,11 +109,10 @@ void RemoteDebuggerPeerWebSocket::close() {
if (ws_peer.is_valid()) {
ws_peer.unref();
}
- ws_client->disconnect_from_host();
}
bool RemoteDebuggerPeerWebSocket::can_block() const {
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
return false;
#else
return true;
@@ -111,14 +120,13 @@ bool RemoteDebuggerPeerWebSocket::can_block() const {
}
RemoteDebuggerPeerWebSocket::RemoteDebuggerPeerWebSocket(Ref<WebSocketPeer> p_peer) {
-#ifdef JAVASCRIPT_ENABLED
- ws_client = Ref<WebSocketClient>(memnew(EMWSClient));
-#else
- ws_client = Ref<WebSocketClient>(memnew(WSLClient));
-#endif
max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages");
- ws_client->set_buffers(8192, max_queued_messages, 8192, max_queued_messages);
ws_peer = p_peer;
+ if (ws_peer.is_valid()) {
+ ws_peer->set_max_queued_packets(max_queued_messages);
+ ws_peer->set_inbound_buffer_size((1 << 23) - 1);
+ ws_peer->set_outbound_buffer_size((1 << 23) - 1);
+ }
}
RemoteDebuggerPeer *RemoteDebuggerPeerWebSocket::create(const String &p_uri) {
diff --git a/modules/websocket/remote_debugger_peer_websocket.h b/modules/websocket/remote_debugger_peer_websocket.h
index 3227065ded..5d26e076c7 100644
--- a/modules/websocket/remote_debugger_peer_websocket.h
+++ b/modules/websocket/remote_debugger_peer_websocket.h
@@ -1,46 +1,41 @@
-/*************************************************************************/
-/* remote_debugger_peer_websocket.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* remote_debugger_peer_websocket.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 REMOTE_DEBUGGER_PEER_WEBSOCKET_H
#define REMOTE_DEBUGGER_PEER_WEBSOCKET_H
#include "core/debugger/remote_debugger_peer.h"
-#ifdef JAVASCRIPT_ENABLED
-#include "emws_client.h"
-#else
-#include "wsl_client.h"
-#endif
+#include "websocket_peer.h"
class RemoteDebuggerPeerWebSocket : public RemoteDebuggerPeer {
- Ref<WebSocketClient> ws_client;
Ref<WebSocketPeer> ws_peer;
List<Array> in_queue;
List<Array> out_queue;
@@ -51,6 +46,7 @@ public:
static RemoteDebuggerPeer *create(const String &p_uri);
Error connect_to_host(const String &p_uri);
+
bool is_peer_connected() override;
int get_max_message_size() const override;
bool has_message() override;
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
deleted file mode 100644
index 2734b4b88f..0000000000
--- a/modules/websocket/websocket_client.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/*************************************************************************/
-/* websocket_client.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 "websocket_client.h"
-
-GDCINULL(WebSocketClient);
-
-WebSocketClient::WebSocketClient() {
-}
-
-WebSocketClient::~WebSocketClient() {
-}
-
-Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_protocols, bool gd_mp_api, const Vector<String> p_custom_headers) {
- _is_multiplayer = gd_mp_api;
-
- String host = p_url;
- String path;
- String scheme;
- int port = 0;
- Error err = p_url.parse_url(scheme, host, port, path);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
-
- bool ssl = false;
- if (scheme == "wss://") {
- ssl = true;
- }
- if (port == 0) {
- port = ssl ? 443 : 80;
- }
- if (path.is_empty()) {
- path = "/";
- }
- return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
-}
-
-void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) {
- verify_ssl = p_verify_ssl;
-}
-
-bool WebSocketClient::is_verify_ssl_enabled() const {
- return verify_ssl;
-}
-
-Ref<X509Certificate> WebSocketClient::get_trusted_ssl_certificate() const {
- return ssl_cert;
-}
-
-void WebSocketClient::set_trusted_ssl_certificate(Ref<X509Certificate> p_cert) {
- ERR_FAIL_COND(get_connection_status() != CONNECTION_DISCONNECTED);
- ssl_cert = p_cert;
-}
-
-bool WebSocketClient::is_server() const {
- return false;
-}
-
-void WebSocketClient::_on_peer_packet() {
- if (_is_multiplayer) {
- _process_multiplayer(get_peer(1), 1);
- } else {
- emit_signal(SNAME("data_received"));
- }
-}
-
-void WebSocketClient::_on_connect(String p_protocol) {
- if (_is_multiplayer) {
- // need to wait for ID confirmation...
- } else {
- emit_signal(SNAME("connection_established"), p_protocol);
- }
-}
-
-void WebSocketClient::_on_close_request(int p_code, String p_reason) {
- emit_signal(SNAME("server_close_request"), p_code, p_reason);
-}
-
-void WebSocketClient::_on_disconnect(bool p_was_clean) {
- if (_is_multiplayer) {
- emit_signal(SNAME("connection_failed"));
- } else {
- emit_signal(SNAME("connection_closed"), p_was_clean);
- }
-}
-
-void WebSocketClient::_on_error() {
- if (_is_multiplayer) {
- emit_signal(SNAME("connection_failed"));
- } else {
- emit_signal(SNAME("connection_error"));
- }
-}
-
-void WebSocketClient::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api", "custom_headers"), &WebSocketClient::connect_to_url, DEFVAL(Vector<String>()), DEFVAL(false), DEFVAL(Vector<String>()));
- ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL(""));
- ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketClient::get_connected_host);
- ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketClient::get_connected_port);
- ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
- ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
-
- ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate);
- ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate", "cert"), &WebSocketClient::set_trusted_ssl_certificate);
-
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate");
-
- ADD_SIGNAL(MethodInfo("data_received"));
- ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
- ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
- ADD_SIGNAL(MethodInfo("connection_closed", PropertyInfo(Variant::BOOL, "was_clean_close")));
- ADD_SIGNAL(MethodInfo("connection_error"));
-}
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
deleted file mode 100644
index d6c072ae16..0000000000
--- a/modules/websocket/websocket_client.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*************************************************************************/
-/* websocket_client.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 WEBSOCKET_CLIENT_H
-#define WEBSOCKET_CLIENT_H
-
-#include "core/crypto/crypto.h"
-#include "core/error/error_list.h"
-#include "websocket_multiplayer_peer.h"
-#include "websocket_peer.h"
-
-class WebSocketClient : public WebSocketMultiplayerPeer {
- GDCLASS(WebSocketClient, WebSocketMultiplayerPeer);
- GDCICLASS(WebSocketClient);
-
-protected:
- Ref<WebSocketPeer> _peer;
- bool verify_ssl = true;
- Ref<X509Certificate> ssl_cert;
-
- static void _bind_methods();
-
-public:
- Error connect_to_url(String p_url, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false, const Vector<String> p_custom_headers = Vector<String>());
-
- void set_verify_ssl_enabled(bool p_verify_ssl);
- bool is_verify_ssl_enabled() const;
- Ref<X509Certificate> get_trusted_ssl_certificate() const;
- void set_trusted_ssl_certificate(Ref<X509Certificate> p_cert);
-
- virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0;
- virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0;
- virtual IPAddress get_connected_host() const = 0;
- virtual uint16_t get_connected_port() const = 0;
-
- virtual bool is_server() const override;
-
- void _on_peer_packet();
- void _on_connect(String p_protocol);
- void _on_close_request(int p_code, String p_reason);
- void _on_disconnect(bool p_was_clean);
- void _on_error();
-
- WebSocketClient();
- ~WebSocketClient();
-};
-
-#endif // WEBSOCKET_CLIENT_H
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
deleted file mode 100644
index a01ae65c56..0000000000
--- a/modules/websocket/websocket_macros.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*************************************************************************/
-/* websocket_macros.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 WEBSOCKET_MACROS_H
-#define WEBSOCKET_MACROS_H
-
-// Defaults per peer buffers, 1024 packets with a shared 65536 bytes payload.
-#define DEF_PKT_SHIFT 10
-#define DEF_BUF_SHIFT 16
-
-/* clang-format off */
-#define GDCICLASS(CNAME) \
-public:\
- static CNAME *(*_create)();\
-\
- static Ref<CNAME > create_ref() {\
-\
- if (!_create)\
- return Ref<CNAME >();\
- return Ref<CNAME >(_create());\
- }\
-\
- static CNAME *create() {\
-\
- if (!_create)\
- return nullptr;\
- return _create();\
- }\
-protected:\
-
-#define GDCINULL(CNAME) \
-CNAME *(*CNAME::_create)() = nullptr;
-
-#define GDCIIMPL(IMPNAME, CNAME) \
-public:\
- static CNAME *_create() { return memnew(IMPNAME); }\
- static void make_default() { CNAME::_create = IMPNAME::_create; }\
-protected:\
-/* clang-format on */
-
-#endif // WEBSOCKET_MACROS_H
diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index 7a3bbf1c47..36b4215f8c 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -1,104 +1,160 @@
-/*************************************************************************/
-/* websocket_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* websocket_multiplayer_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "websocket_multiplayer_peer.h"
#include "core/os/os.h"
WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() {
+ peer_config = Ref<WebSocketPeer>(WebSocketPeer::create());
}
WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() {
_clear();
}
+Ref<WebSocketPeer> WebSocketMultiplayerPeer::_create_peer() {
+ Ref<WebSocketPeer> peer = Ref<WebSocketPeer>(WebSocketPeer::create());
+ peer->set_supported_protocols(get_supported_protocols());
+ peer->set_handshake_headers(get_handshake_headers());
+ peer->set_inbound_buffer_size(get_inbound_buffer_size());
+ peer->set_outbound_buffer_size(get_outbound_buffer_size());
+ peer->set_max_queued_packets(get_max_queued_packets());
+ return peer;
+}
+
void WebSocketMultiplayerPeer::_clear() {
- _peer_map.clear();
- if (_current_packet.data != nullptr) {
- memfree(_current_packet.data);
+ connection_status = CONNECTION_DISCONNECTED;
+ unique_id = 0;
+ peers_map.clear();
+ use_tls = false;
+ tcp_server.unref();
+ pending_peers.clear();
+ tls_certificate.unref();
+ tls_key.unref();
+ if (current_packet.data != nullptr) {
+ memfree(current_packet.data);
+ current_packet.data = nullptr;
}
- for (Packet &E : _incoming_packets) {
+ for (Packet &E : incoming_packets) {
memfree(E.data);
E.data = nullptr;
}
- _incoming_packets.clear();
+ incoming_packets.clear();
}
void WebSocketMultiplayerPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_buffers", "input_buffer_size_kb", "input_max_packets", "output_buffer_size_kb", "output_max_packets"), &WebSocketMultiplayerPeer::set_buffers);
+ ClassDB::bind_method(D_METHOD("create_client", "url", "verify_tls", "tls_certificate"), &WebSocketMultiplayerPeer::create_client, DEFVAL(true), DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("create_server", "port", "bind_address", "tls_key", "tls_certificate"), &WebSocketMultiplayerPeer::create_server, DEFVAL("*"), DEFVAL(Ref<CryptoKey>()), DEFVAL(Ref<X509Certificate>()));
+
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebSocketMultiplayerPeer::get_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketMultiplayerPeer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketMultiplayerPeer::get_peer_port);
+
+ ClassDB::bind_method(D_METHOD("get_supported_protocols"), &WebSocketMultiplayerPeer::get_supported_protocols);
+ ClassDB::bind_method(D_METHOD("set_supported_protocols", "protocols"), &WebSocketMultiplayerPeer::set_supported_protocols);
+
+ ClassDB::bind_method(D_METHOD("get_handshake_headers"), &WebSocketMultiplayerPeer::get_handshake_headers);
+ ClassDB::bind_method(D_METHOD("set_handshake_headers", "protocols"), &WebSocketMultiplayerPeer::set_handshake_headers);
+
+ ClassDB::bind_method(D_METHOD("get_inbound_buffer_size"), &WebSocketMultiplayerPeer::get_inbound_buffer_size);
+ ClassDB::bind_method(D_METHOD("set_inbound_buffer_size", "buffer_size"), &WebSocketMultiplayerPeer::set_inbound_buffer_size);
+
+ ClassDB::bind_method(D_METHOD("get_outbound_buffer_size"), &WebSocketMultiplayerPeer::get_outbound_buffer_size);
+ ClassDB::bind_method(D_METHOD("set_outbound_buffer_size", "buffer_size"), &WebSocketMultiplayerPeer::set_outbound_buffer_size);
+
+ ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketMultiplayerPeer::get_handshake_timeout);
+ ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketMultiplayerPeer::set_handshake_timeout);
+
+ ClassDB::bind_method(D_METHOD("set_max_queued_packets", "max_queued_packets"), &WebSocketMultiplayerPeer::set_max_queued_packets);
+ ClassDB::bind_method(D_METHOD("get_max_queued_packets"), &WebSocketMultiplayerPeer::get_max_queued_packets);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "supported_protocols"), "set_supported_protocols", "get_supported_protocols");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "handshake_headers"), "set_handshake_headers", "get_handshake_headers");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "inbound_buffer_size"), "set_inbound_buffer_size", "get_inbound_buffer_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outbound_buffer_size"), "set_outbound_buffer_size", "get_outbound_buffer_size");
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout");
- ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "peer_source")));
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_queued_packets"), "set_max_queued_packets", "get_max_queued_packets");
}
//
// PacketPeer
//
int WebSocketMultiplayerPeer::get_available_packet_count() const {
- ERR_FAIL_COND_V_MSG(!_is_multiplayer, 0, "Please use get_peer(ID).get_available_packet_count to get available packet count from peers when not using the MultiplayerAPI.");
-
- return _incoming_packets.size();
+ return incoming_packets.size();
}
Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V_MSG(!_is_multiplayer, ERR_UNCONFIGURED, "Please use get_peer(ID).get_packet/var to communicate with peers when not using the MultiplayerAPI.");
+ ERR_FAIL_COND_V(get_connection_status() != CONNECTION_CONNECTED, ERR_UNCONFIGURED);
r_buffer_size = 0;
- if (_current_packet.data != nullptr) {
- memfree(_current_packet.data);
- _current_packet.data = nullptr;
+ if (current_packet.data != nullptr) {
+ memfree(current_packet.data);
+ current_packet.data = nullptr;
}
- ERR_FAIL_COND_V(_incoming_packets.size() == 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, ERR_UNAVAILABLE);
- _current_packet = _incoming_packets.front()->get();
- _incoming_packets.pop_front();
+ current_packet = incoming_packets.front()->get();
+ incoming_packets.pop_front();
- *r_buffer = _current_packet.data;
- r_buffer_size = _current_packet.size;
+ *r_buffer = current_packet.data;
+ r_buffer_size = current_packet.size;
return OK;
}
Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V_MSG(!_is_multiplayer, ERR_UNCONFIGURED, "Please use get_peer(ID).put_packet/var to communicate with peers when not using the MultiplayerAPI.");
-
- Vector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), _target_peer, p_buffer, p_buffer_size);
+ ERR_FAIL_COND_V(get_connection_status() != CONNECTION_CONNECTED, ERR_UNCONFIGURED);
if (is_server()) {
- return _server_relay(1, _target_peer, &(buffer.ptr()[0]), buffer.size());
+ if (target_peer > 0) {
+ ERR_FAIL_COND_V_MSG(!peers_map.has(target_peer), ERR_INVALID_PARAMETER, "Peer not found: " + itos(target_peer));
+ get_peer(target_peer)->put_packet(p_buffer, p_buffer_size);
+ } else {
+ for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
+ if (target_peer && -target_peer == E.key) {
+ continue; // Excluded.
+ }
+ E.value->put_packet(p_buffer, p_buffer_size);
+ }
+ }
+ return OK;
} else {
- return get_peer(1)->put_packet(&(buffer.ptr()[0]), buffer.size());
+ return get_peer(1)->put_packet(p_buffer, p_buffer_size);
}
}
@@ -106,185 +162,338 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer
// MultiplayerPeer
//
void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) {
- _target_peer = p_target_peer;
+ target_peer = p_target_peer;
}
int WebSocketMultiplayerPeer::get_packet_peer() const {
- ERR_FAIL_COND_V_MSG(!_is_multiplayer, 1, "This function is not available when not using the MultiplayerAPI.");
- ERR_FAIL_COND_V(_incoming_packets.size() == 0, 1);
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
- return _incoming_packets.front()->get().source;
+ return incoming_packets.front()->get().source;
}
int WebSocketMultiplayerPeer::get_unique_id() const {
- return _peer_id;
+ return unique_id;
}
-void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) {
- ERR_FAIL_COND(!p_peer.is_valid());
- ERR_FAIL_COND(!p_peer->is_connected_to_host());
-
- Vector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4);
- p_peer->put_packet(&(message.ptr()[0]), message.size());
+int WebSocketMultiplayerPeer::get_max_packet_size() const {
+ return get_outbound_buffer_size() - PROTO_SIZE;
}
-Vector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) {
- Vector<uint8_t> out;
- out.resize(PROTO_SIZE + p_data_size);
-
- uint8_t *w = out.ptrw();
- memcpy(&w[0], &p_type, 1);
- memcpy(&w[1], &p_from, 4);
- memcpy(&w[5], &p_to, 4);
- memcpy(&w[PROTO_SIZE], p_data, p_data_size);
-
- return out;
+Error WebSocketMultiplayerPeer::create_server(int p_port, IPAddress p_bind_ip, Ref<CryptoKey> p_tls_key, Ref<X509Certificate> p_tls_certificate) {
+ ERR_FAIL_COND_V(get_connection_status() != CONNECTION_DISCONNECTED, ERR_ALREADY_IN_USE);
+ _clear();
+ tcp_server.instantiate();
+ Error err = tcp_server->listen(p_port, p_bind_ip);
+ if (err != OK) {
+ tcp_server.unref();
+ return err;
+ }
+ unique_id = 1;
+ connection_status = CONNECTION_CONNECTED;
+ // TLS config
+ tls_key = p_tls_key;
+ tls_certificate = p_tls_certificate;
+ if (tls_key.is_valid() && tls_certificate.is_valid()) {
+ use_tls = true;
+ }
+ return OK;
}
-void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
- // First of all, confirm the ID!
- _send_sys(get_peer(p_peer_id), SYS_ID, p_peer_id);
+Error WebSocketMultiplayerPeer::create_client(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate) {
+ ERR_FAIL_COND_V(get_connection_status() != CONNECTION_DISCONNECTED, ERR_ALREADY_IN_USE);
+ _clear();
+ Ref<WebSocketPeer> peer = _create_peer();
+ Error err = peer->connect_to_url(p_url, p_verify_tls, p_tls_certificate);
+ if (err != OK) {
+ return err;
+ }
+ PendingPeer pending;
+ pending.time = OS::get_singleton()->get_ticks_msec();
+ pending_peers[1] = pending;
+ peers_map[1] = peer;
+ connection_status = CONNECTION_CONNECTING;
+ return OK;
+}
- // Then send the server peer (which will trigger connection_succeded in client)
- _send_sys(get_peer(p_peer_id), SYS_ADD, 1);
+bool WebSocketMultiplayerPeer::is_server() const {
+ return tcp_server.is_valid();
+}
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- int32_t id = E.key;
- if (p_peer_id == id) {
- continue; // Skip the newly added peer (already confirmed)
+void WebSocketMultiplayerPeer::_poll_client() {
+ ERR_FAIL_COND(connection_status == CONNECTION_DISCONNECTED); // Bug.
+ ERR_FAIL_COND(!peers_map.has(1) || peers_map[1].is_null()); // Bug.
+ Ref<WebSocketPeer> peer = peers_map[1];
+ peer->poll(); // Update state and fetch packets.
+ WebSocketPeer::State ready_state = peer->get_ready_state();
+ if (ready_state == WebSocketPeer::STATE_OPEN) {
+ if (connection_status == CONNECTION_CONNECTING) {
+ if (peer->get_available_packet_count() > 0) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = peer->get_packet(&in_buffer, size);
+ if (err != OK || size != 4) {
+ peer->close(); // Will cause connection error on next poll.
+ ERR_FAIL_MSG("Invalid ID received from server");
+ }
+ unique_id = *((int32_t *)in_buffer);
+ if (unique_id < 2) {
+ peer->close(); // Will cause connection error on next poll.
+ ERR_FAIL_MSG("Invalid ID received from server");
+ }
+ connection_status = CONNECTION_CONNECTED;
+ emit_signal("peer_connected", 1);
+ emit_signal("connection_succeeded");
+ } else {
+ return; // Still waiting for an ID.
+ }
}
-
- // Send new peer to others
- _send_sys(get_peer(id), SYS_ADD, p_peer_id);
- // Send others to new peer
- _send_sys(get_peer(p_peer_id), SYS_ADD, id);
+ int pkts = peer->get_available_packet_count();
+ while (pkts > 0 && peer->get_ready_state() == WebSocketPeer::STATE_OPEN) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = peer->get_packet(&in_buffer, size);
+ ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(size <= 0);
+ Packet packet;
+ packet.data = (uint8_t *)memalloc(size);
+ memcpy(packet.data, in_buffer, size);
+ packet.size = size;
+ packet.source = 1;
+ incoming_packets.push_back(packet);
+ pkts--;
+ }
+ } else if (peer->get_ready_state() == WebSocketPeer::STATE_CLOSED) {
+ if (connection_status == CONNECTION_CONNECTED) {
+ emit_signal(SNAME("peer_disconnected"), 1);
+ }
+ _clear();
+ return;
}
-}
-
-void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- int32_t id = E.key;
- if (p_peer_id != id) {
- _send_sys(get_peer(id), SYS_DEL, p_peer_id);
+ if (connection_status == CONNECTION_CONNECTING) {
+ // Still connecting
+ ERR_FAIL_COND(!pending_peers.has(1)); // Bug.
+ if (OS::get_singleton()->get_ticks_msec() - pending_peers[1].time > handshake_timeout) {
+ print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", handshake_timeout * 0.001));
+ _clear();
+ return;
}
}
}
-void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) {
- Packet packet;
- packet.data = (uint8_t *)memalloc(p_data_size);
- packet.size = p_data_size;
- packet.source = p_source;
- packet.destination = p_dest;
- memcpy(packet.data, &p_data[PROTO_SIZE], p_data_size);
- _incoming_packets.push_back(packet);
- emit_signal(SNAME("peer_packet"), p_source);
-}
+void WebSocketMultiplayerPeer::_poll_server() {
+ ERR_FAIL_COND(connection_status != CONNECTION_CONNECTED); // Bug.
+ ERR_FAIL_COND(tcp_server.is_null() || !tcp_server->is_listening()); // Bug.
+
+ // Accept new connections.
+ if (!is_refusing_new_connections() && tcp_server->is_connection_available()) {
+ PendingPeer peer;
+ peer.time = OS::get_singleton()->get_ticks_msec();
+ peer.tcp = tcp_server->take_connection();
+ peer.connection = peer.tcp;
+ pending_peers[generate_unique_id()] = peer;
+ }
-Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) {
- if (p_to == 1) {
- return OK; // Will not send to self
+ // Process pending peers.
+ HashSet<int> to_remove;
+ for (KeyValue<int, PendingPeer> &E : pending_peers) {
+ PendingPeer &peer = E.value;
+ int id = E.key;
- } else if (p_to == 0) {
- for (KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- if (E.key != p_from) {
- E.value->put_packet(p_buffer, p_buffer_size);
- }
+ if (OS::get_singleton()->get_ticks_msec() - peer.time > handshake_timeout) {
+ print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", handshake_timeout * 0.001));
+ to_remove.insert(id);
+ continue;
}
- return OK; // Sent to all but sender
- } else if (p_to < 0) {
- for (KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- if (E.key != p_from && E.key != -p_to) {
- E.value->put_packet(p_buffer, p_buffer_size);
+ if (peer.ws.is_valid()) {
+ peer.ws->poll();
+ WebSocketPeer::State state = peer.ws->get_ready_state();
+ if (state == WebSocketPeer::STATE_OPEN) {
+ // Connected.
+ to_remove.insert(id);
+ if (is_refusing_new_connections()) {
+ // The user does not want new connections, dropping it.
+ continue;
+ }
+ int32_t peer_id = id;
+ Error err = peer.ws->put_packet((const uint8_t *)&peer_id, sizeof(peer_id));
+ if (err == OK) {
+ peers_map[id] = peer.ws;
+ emit_signal("peer_connected", id);
+ } else {
+ ERR_PRINT("Failed to send ID to newly connected peer.");
+ }
+ continue;
+ } else if (state == WebSocketPeer::STATE_CONNECTING) {
+ continue; // Still connecting.
+ }
+ to_remove.insert(id); // Error.
+ continue;
+ }
+ if (peer.tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ to_remove.insert(id); // Error.
+ continue;
+ }
+ if (!use_tls) {
+ peer.ws = _create_peer();
+ peer.ws->accept_stream(peer.tcp);
+ continue;
+ } else {
+ if (peer.connection == peer.tcp) {
+ Ref<StreamPeerTLS> tls = Ref<StreamPeerTLS>(StreamPeerTLS::create());
+ Error err = tls->accept_stream(peer.tcp, tls_key, tls_certificate);
+ if (err != OK) {
+ to_remove.insert(id);
+ continue;
+ }
+ }
+ Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(peer.connection);
+ tls->poll();
+ if (tls->get_status() == StreamPeerTLS::STATUS_CONNECTED) {
+ peer.ws = _create_peer();
+ peer.ws->accept_stream(peer.connection);
+ continue;
+ } else if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
+ // Still connecting.
+ continue;
+ } else {
+ // Error
+ to_remove.insert(id);
}
}
- return OK; // Sent to all but sender and excluded
+ }
- } else {
- ERR_FAIL_COND_V(p_to == p_from, FAILED);
+ // Remove disconnected pending peers.
+ for (const int &pid : to_remove) {
+ pending_peers.erase(pid);
+ }
+ to_remove.clear();
+
+ // Process connected peers.
+ for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) {
+ Ref<WebSocketPeer> ws = E.value;
+ int id = E.key;
+ ws->poll();
+ if (ws->get_ready_state() != WebSocketPeer::STATE_OPEN) {
+ to_remove.insert(id); // Disconnected.
+ continue;
+ }
+ // Fetch packets
+ int pkts = ws->get_available_packet_count();
+ while (pkts > 0 && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) {
+ const uint8_t *in_buffer;
+ int size = 0;
+ Error err = ws->get_packet(&in_buffer, size);
+ if (err != OK || size <= 0) {
+ break;
+ }
+ Packet packet;
+ packet.data = (uint8_t *)memalloc(size);
+ memcpy(packet.data, in_buffer, size);
+ packet.size = size;
+ packet.source = E.key;
+ incoming_packets.push_back(packet);
+ pkts--;
+ }
+ }
- Ref<WebSocketPeer> peer_to = get_peer(p_to);
- ERR_FAIL_COND_V(peer_to.is_null(), FAILED);
+ // Remove disconnected peers.
+ for (const int &pid : to_remove) {
+ emit_signal(SNAME("peer_disconnected"), pid);
+ peers_map.erase(pid);
+ }
+}
- return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
+void WebSocketMultiplayerPeer::poll() {
+ if (connection_status == CONNECTION_DISCONNECTED) {
+ return;
+ }
+ if (is_server()) {
+ _poll_server();
+ } else {
+ _poll_client();
}
}
-void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) {
- ERR_FAIL_COND(!p_peer.is_valid());
+MultiplayerPeer::ConnectionStatus WebSocketMultiplayerPeer::get_connection_status() const {
+ return connection_status;
+}
- const uint8_t *in_buffer;
- int size = 0;
- int data_size = 0;
+Ref<WebSocketPeer> WebSocketMultiplayerPeer::get_peer(int p_id) const {
+ ERR_FAIL_COND_V(!peers_map.has(p_id), Ref<WebSocketPeer>());
+ return peers_map[p_id];
+}
- Error err = p_peer->get_packet(&in_buffer, size);
+void WebSocketMultiplayerPeer::set_supported_protocols(const Vector<String> &p_protocols) {
+ peer_config->set_supported_protocols(p_protocols);
+}
- ERR_FAIL_COND(err != OK);
- ERR_FAIL_COND(size < PROTO_SIZE);
+Vector<String> WebSocketMultiplayerPeer::get_supported_protocols() const {
+ return peer_config->get_supported_protocols();
+}
- data_size = size - PROTO_SIZE;
+void WebSocketMultiplayerPeer::set_handshake_headers(const Vector<String> &p_headers) {
+ peer_config->set_handshake_headers(p_headers);
+}
- uint8_t type = 0;
- uint32_t from = 0;
- int32_t to = 0;
- memcpy(&type, in_buffer, 1);
- memcpy(&from, &in_buffer[1], 4);
- memcpy(&to, &in_buffer[5], 4);
+Vector<String> WebSocketMultiplayerPeer::get_handshake_headers() const {
+ return peer_config->get_handshake_headers();
+}
- if (is_server()) { // Server can resend
+void WebSocketMultiplayerPeer::set_outbound_buffer_size(int p_buffer_size) {
+ peer_config->set_outbound_buffer_size(p_buffer_size);
+}
- ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
- ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
+int WebSocketMultiplayerPeer::get_outbound_buffer_size() const {
+ return peer_config->get_outbound_buffer_size();
+}
- if (to == 1) { // This is for the server
+void WebSocketMultiplayerPeer::set_inbound_buffer_size(int p_buffer_size) {
+ peer_config->set_inbound_buffer_size(p_buffer_size);
+}
- _store_pkt(from, to, in_buffer, data_size);
+int WebSocketMultiplayerPeer::get_inbound_buffer_size() const {
+ return peer_config->get_inbound_buffer_size();
+}
- } else if (to == 0) {
- // Broadcast, for us too
- _store_pkt(from, to, in_buffer, data_size);
+void WebSocketMultiplayerPeer::set_max_queued_packets(int p_max_queued_packets) {
+ peer_config->set_max_queued_packets(p_max_queued_packets);
+}
- } else if (to < 0) {
- // All but one, for us if not excluded
- if (_peer_id != -(int32_t)p_peer_id) {
- _store_pkt(from, to, in_buffer, data_size);
- }
- }
- // Relay if needed (i.e. "to" includes a peer that is not the server)
- _server_relay(from, to, in_buffer, size);
+int WebSocketMultiplayerPeer::get_max_queued_packets() const {
+ return peer_config->get_max_queued_packets();
+}
- } else {
- if (type == SYS_NONE) { // Payload message
+float WebSocketMultiplayerPeer::get_handshake_timeout() const {
+ return handshake_timeout / 1000.0;
+}
- _store_pkt(from, to, in_buffer, data_size);
- return;
- }
+void WebSocketMultiplayerPeer::set_handshake_timeout(float p_timeout) {
+ ERR_FAIL_COND(p_timeout <= 0.0);
+ handshake_timeout = p_timeout * 1000;
+}
- // System message
- ERR_FAIL_COND(data_size < 4);
- int id = 0;
- memcpy(&id, &in_buffer[PROTO_SIZE], 4);
-
- switch (type) {
- case SYS_ADD: // Add peer
- _peer_map[id] = Ref<WebSocketPeer>();
- emit_signal(SNAME("peer_connected"), id);
- if (id == 1) { // We just connected to the server
- emit_signal(SNAME("connection_succeeded"));
- }
- break;
+IPAddress WebSocketMultiplayerPeer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!peers_map.has(p_peer_id), IPAddress());
+ return peers_map[p_peer_id]->get_connected_host();
+}
- case SYS_DEL: // Remove peer
- _peer_map.erase(id);
- emit_signal(SNAME("peer_disconnected"), id);
- break;
- case SYS_ID: // Hello, server assigned ID
- _peer_id = id;
- break;
- default:
- ERR_FAIL_MSG("Invalid multiplayer message.");
- break;
+int WebSocketMultiplayerPeer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!peers_map.has(p_peer_id), 0);
+ return peers_map[p_peer_id]->get_connected_port();
+}
+
+void WebSocketMultiplayerPeer::disconnect_peer(int p_peer_id, bool p_force) {
+ ERR_FAIL_COND(!peers_map.has(p_peer_id));
+ peers_map[p_peer_id]->close();
+ if (p_force) {
+ peers_map.erase(p_peer_id);
+ if (!is_server()) {
+ _clear();
}
}
}
+
+void WebSocketMultiplayerPeer::close() {
+ _clear();
+}
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 3259e78b3b..ea10e8799f 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -1,37 +1,39 @@
-/*************************************************************************/
-/* websocket_multiplayer_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* websocket_multiplayer_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBSOCKET_MULTIPLAYER_PEER_H
#define WEBSOCKET_MULTIPLAYER_PEER_H
#include "core/error/error_list.h"
+#include "core/io/stream_peer_tls.h"
+#include "core/io/tcp_server.h"
#include "core/templates/list.h"
#include "scene/main/multiplayer_peer.h"
#include "websocket_peer.h"
@@ -40,9 +42,7 @@ class WebSocketMultiplayerPeer : public MultiplayerPeer {
GDCLASS(WebSocketMultiplayerPeer, MultiplayerPeer);
private:
- Vector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
- void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size);
- Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size);
+ Ref<WebSocketPeer> _create_peer();
protected:
enum {
@@ -56,30 +56,56 @@ protected:
struct Packet {
int source = 0;
- int destination = 0;
uint8_t *data = nullptr;
uint32_t size = 0;
};
- List<Packet> _incoming_packets;
- HashMap<int, Ref<WebSocketPeer>> _peer_map;
- Packet _current_packet;
+ struct PendingPeer {
+ uint64_t time = 0;
+ Ref<StreamPeerTCP> tcp;
+ Ref<StreamPeer> connection;
+ Ref<WebSocketPeer> ws;
+ };
+
+ uint64_t handshake_timeout = 3000;
+ Ref<WebSocketPeer> peer_config;
+ HashMap<int, PendingPeer> pending_peers;
+ Ref<TCPServer> tcp_server;
+ bool use_tls = false;
+ Ref<X509Certificate> tls_certificate;
+ Ref<CryptoKey> tls_key;
+
+ ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
+
+ List<Packet> incoming_packets;
+ HashMap<int, Ref<WebSocketPeer>> peers_map;
+ Packet current_packet;
- bool _is_multiplayer = false;
- int _target_peer = 0;
- int _peer_id = 0;
+ int target_peer = 0;
+ int unique_id = 0;
static void _bind_methods();
- void _send_add(int32_t p_peer_id);
- void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id);
- void _send_del(int32_t p_peer_id);
+ void _poll_client();
+ void _poll_server();
+ void _clear();
public:
/* MultiplayerPeer */
- void set_target_peer(int p_target_peer) override;
- int get_packet_peer() const override;
- int get_unique_id() const override;
+ virtual void set_target_peer(int p_target_peer) override;
+ virtual int get_packet_peer() const override;
+ virtual int get_packet_channel() const override { return 0; }
+ virtual TransferMode get_packet_mode() const override { return TRANSFER_MODE_RELIABLE; }
+ virtual int get_unique_id() const override;
+ virtual bool is_server_relay_supported() const override { return true; }
+
+ virtual int get_max_packet_size() const override;
+ virtual bool is_server() const override;
+ virtual void poll() override;
+ virtual void close() override;
+ virtual void disconnect_peer(int p_peer_id, bool p_force = false) override;
+
+ virtual ConnectionStatus get_connection_status() const override;
/* PacketPeer */
virtual int get_available_packet_count() const override;
@@ -87,11 +113,31 @@ public:
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
/* WebSocketPeer */
- virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0;
- virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const = 0;
+ virtual Ref<WebSocketPeer> get_peer(int p_peer_id) const;
- void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id);
- void _clear();
+ Error create_client(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_tls_certificate);
+ Error create_server(int p_port, IPAddress p_bind_ip, Ref<CryptoKey> p_tls_key, Ref<X509Certificate> p_tls_certificate);
+
+ void set_supported_protocols(const Vector<String> &p_protocols);
+ Vector<String> get_supported_protocols() const;
+
+ void set_handshake_headers(const Vector<String> &p_headers);
+ Vector<String> get_handshake_headers() const;
+
+ void set_outbound_buffer_size(int p_buffer_size);
+ int get_outbound_buffer_size() const;
+
+ void set_inbound_buffer_size(int p_buffer_size);
+ int get_inbound_buffer_size() const;
+
+ float get_handshake_timeout() const;
+ void set_handshake_timeout(float p_timeout);
+
+ IPAddress get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+
+ void set_max_queued_packets(int p_max_queued_packets);
+ int get_max_queued_packets() const;
WebSocketMultiplayerPeer();
~WebSocketMultiplayerPeer();
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index a0af9303b8..d10315f64c 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -1,36 +1,36 @@
-/*************************************************************************/
-/* websocket_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* websocket_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "websocket_peer.h"
-GDCINULL(WebSocketPeer);
+WebSocketPeer *(*WebSocketPeer::_create)() = nullptr;
WebSocketPeer::WebSocketPeer() {
}
@@ -39,16 +39,115 @@ WebSocketPeer::~WebSocketPeer() {
}
void WebSocketPeer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("get_write_mode"), &WebSocketPeer::get_write_mode);
- ClassDB::bind_method(D_METHOD("set_write_mode", "mode"), &WebSocketPeer::set_write_mode);
- ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host);
+ ClassDB::bind_method(D_METHOD("connect_to_url", "url", "verify_tls", "trusted_tls_certificate"), &WebSocketPeer::connect_to_url, DEFVAL(true), DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &WebSocketPeer::accept_stream);
+ ClassDB::bind_method(D_METHOD("send", "message", "write_mode"), &WebSocketPeer::_send_bind, DEFVAL(WRITE_MODE_BINARY));
+ ClassDB::bind_method(D_METHOD("send_text", "message"), &WebSocketPeer::send_text);
ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet);
+ ClassDB::bind_method(D_METHOD("poll"), &WebSocketPeer::poll);
ClassDB::bind_method(D_METHOD("close", "code", "reason"), &WebSocketPeer::close, DEFVAL(1000), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
+ ClassDB::bind_method(D_METHOD("get_selected_protocol"), &WebSocketPeer::get_selected_protocol);
+ ClassDB::bind_method(D_METHOD("get_requested_url"), &WebSocketPeer::get_requested_url);
ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &WebSocketPeer::set_no_delay);
ClassDB::bind_method(D_METHOD("get_current_outbound_buffered_amount"), &WebSocketPeer::get_current_outbound_buffered_amount);
+ ClassDB::bind_method(D_METHOD("get_ready_state"), &WebSocketPeer::get_ready_state);
+ ClassDB::bind_method(D_METHOD("get_close_code"), &WebSocketPeer::get_close_code);
+ ClassDB::bind_method(D_METHOD("get_close_reason"), &WebSocketPeer::get_close_reason);
+
+ ClassDB::bind_method(D_METHOD("get_supported_protocols"), &WebSocketPeer::_get_supported_protocols);
+ ClassDB::bind_method(D_METHOD("set_supported_protocols", "protocols"), &WebSocketPeer::set_supported_protocols);
+ ClassDB::bind_method(D_METHOD("get_handshake_headers"), &WebSocketPeer::_get_handshake_headers);
+ ClassDB::bind_method(D_METHOD("set_handshake_headers", "protocols"), &WebSocketPeer::set_handshake_headers);
+
+ ClassDB::bind_method(D_METHOD("get_inbound_buffer_size"), &WebSocketPeer::get_inbound_buffer_size);
+ ClassDB::bind_method(D_METHOD("set_inbound_buffer_size", "buffer_size"), &WebSocketPeer::set_inbound_buffer_size);
+ ClassDB::bind_method(D_METHOD("get_outbound_buffer_size"), &WebSocketPeer::get_outbound_buffer_size);
+ ClassDB::bind_method(D_METHOD("set_outbound_buffer_size", "buffer_size"), &WebSocketPeer::set_outbound_buffer_size);
+
+ ClassDB::bind_method(D_METHOD("set_max_queued_packets", "buffer_size"), &WebSocketPeer::set_max_queued_packets);
+ ClassDB::bind_method(D_METHOD("get_max_queued_packets"), &WebSocketPeer::get_max_queued_packets);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "supported_protocols"), "set_supported_protocols", "get_supported_protocols");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "handshake_headers"), "set_handshake_headers", "get_handshake_headers");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "inbound_buffer_size"), "set_inbound_buffer_size", "get_inbound_buffer_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outbound_buffer_size"), "set_outbound_buffer_size", "get_outbound_buffer_size");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_queued_packets"), "set_max_queued_packets", "get_max_queued_packets");
+
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
+
+ BIND_ENUM_CONSTANT(STATE_CONNECTING);
+ BIND_ENUM_CONSTANT(STATE_OPEN);
+ BIND_ENUM_CONSTANT(STATE_CLOSING);
+ BIND_ENUM_CONSTANT(STATE_CLOSED);
+}
+
+Error WebSocketPeer::_send_bind(const PackedByteArray &p_message, WriteMode p_mode) {
+ return send(p_message.ptr(), p_message.size(), p_mode);
+}
+
+Error WebSocketPeer::send_text(const String &p_text) {
+ const CharString cs = p_text.utf8();
+ return send((const uint8_t *)cs.ptr(), cs.length(), WRITE_MODE_TEXT);
+}
+
+void WebSocketPeer::set_supported_protocols(const Vector<String> &p_protocols) {
+ // Strip edges from protocols.
+ supported_protocols.resize(p_protocols.size());
+ for (int i = 0; i < p_protocols.size(); i++) {
+ supported_protocols.write[i] = p_protocols[i].strip_edges();
+ }
+}
+
+const Vector<String> WebSocketPeer::get_supported_protocols() const {
+ return supported_protocols;
+}
+
+Vector<String> WebSocketPeer::_get_supported_protocols() const {
+ Vector<String> out;
+ out.append_array(supported_protocols);
+ return out;
+}
+
+void WebSocketPeer::set_handshake_headers(const Vector<String> &p_headers) {
+ handshake_headers = p_headers;
+}
+
+const Vector<String> WebSocketPeer::get_handshake_headers() const {
+ return handshake_headers;
+}
+
+Vector<String> WebSocketPeer::_get_handshake_headers() const {
+ Vector<String> out;
+ out.append_array(handshake_headers);
+ return out;
+}
+
+void WebSocketPeer::set_outbound_buffer_size(int p_buffer_size) {
+ outbound_buffer_size = p_buffer_size;
+}
+
+int WebSocketPeer::get_outbound_buffer_size() const {
+ return outbound_buffer_size;
+}
+
+void WebSocketPeer::set_inbound_buffer_size(int p_buffer_size) {
+ inbound_buffer_size = p_buffer_size;
+}
+
+int WebSocketPeer::get_inbound_buffer_size() const {
+ return inbound_buffer_size;
+}
+
+void WebSocketPeer::set_max_queued_packets(int p_max_queued_packets) {
+ max_queued_packets = p_max_queued_packets;
+}
+
+int WebSocketPeer::get_max_queued_packets() const {
+ return max_queued_packets;
}
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index 22099f7258..3a1527b769 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -1,70 +1,127 @@
-/*************************************************************************/
-/* websocket_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* websocket_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBSOCKET_PEER_H
#define WEBSOCKET_PEER_H
+#include "core/crypto/crypto.h"
#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
-#include "websocket_macros.h"
class WebSocketPeer : public PacketPeer {
GDCLASS(WebSocketPeer, PacketPeer);
- GDCICLASS(WebSocketPeer);
public:
+ enum State {
+ STATE_CONNECTING,
+ STATE_OPEN,
+ STATE_CLOSING,
+ STATE_CLOSED
+ };
+
enum WriteMode {
WRITE_MODE_TEXT,
WRITE_MODE_BINARY,
};
+ enum {
+ DEFAULT_BUFFER_SIZE = 65535,
+ };
+
+private:
+ virtual Error _send_bind(const PackedByteArray &p_data, WriteMode p_mode = WRITE_MODE_BINARY);
+
protected:
+ static WebSocketPeer *(*_create)();
+
static void _bind_methods();
+ Vector<String> supported_protocols;
+ Vector<String> handshake_headers;
+
+ Vector<String> _get_supported_protocols() const;
+ Vector<String> _get_handshake_headers() const;
+
+ int outbound_buffer_size = DEFAULT_BUFFER_SIZE;
+ int inbound_buffer_size = DEFAULT_BUFFER_SIZE;
+ int max_queued_packets = 2048;
+
public:
- virtual WriteMode get_write_mode() const = 0;
- virtual void set_write_mode(WriteMode p_mode) = 0;
+ static WebSocketPeer *create() {
+ if (!_create) {
+ return nullptr;
+ }
+ return _create();
+ }
+ virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) { return ERR_UNAVAILABLE; };
+ virtual Error accept_stream(Ref<StreamPeer> p_stream) = 0;
+
+ virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) = 0;
virtual void close(int p_code = 1000, String p_reason = "") = 0;
- virtual bool is_connected_to_host() const = 0;
virtual IPAddress get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
virtual bool was_string_packet() const = 0;
virtual void set_no_delay(bool p_enabled) = 0;
virtual int get_current_outbound_buffered_amount() const = 0;
+ virtual String get_selected_protocol() const = 0;
+ virtual String get_requested_url() const = 0;
+
+ virtual void poll() = 0;
+ virtual State get_ready_state() const = 0;
+ virtual int get_close_code() const = 0;
+ virtual String get_close_reason() const = 0;
+
+ Error send_text(const String &p_text);
+
+ void set_supported_protocols(const Vector<String> &p_protocols);
+ const Vector<String> get_supported_protocols() const;
+
+ void set_handshake_headers(const Vector<String> &p_headers);
+ const Vector<String> get_handshake_headers() const;
+
+ void set_outbound_buffer_size(int p_buffer_size);
+ int get_outbound_buffer_size() const;
+
+ void set_inbound_buffer_size(int p_buffer_size);
+ int get_inbound_buffer_size() const;
+
+ void set_max_queued_packets(int p_max_queued_packets);
+ int get_max_queued_packets() const;
WebSocketPeer();
~WebSocketPeer();
};
VARIANT_ENUM_CAST(WebSocketPeer::WriteMode);
+VARIANT_ENUM_CAST(WebSocketPeer::State);
#endif // WEBSOCKET_PEER_H
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
deleted file mode 100644
index b7851b02c4..0000000000
--- a/modules/websocket/websocket_server.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/*************************************************************************/
-/* websocket_server.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 "websocket_server.h"
-
-GDCINULL(WebSocketServer);
-
-WebSocketServer::WebSocketServer() {
- _peer_id = 1;
- bind_ip = IPAddress("*");
-}
-
-WebSocketServer::~WebSocketServer() {
-}
-
-void WebSocketServer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening);
- ClassDB::bind_method(D_METHOD("set_extra_headers", "headers"), &WebSocketServer::set_extra_headers, DEFVAL(Vector<String>()));
- ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(Vector<String>()), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
- ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
- ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address);
- ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port);
- ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL(""));
-
- ClassDB::bind_method(D_METHOD("get_bind_ip"), &WebSocketServer::get_bind_ip);
- ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &WebSocketServer::set_bind_ip);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "bind_ip"), "set_bind_ip", "get_bind_ip");
-
- ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key);
- ClassDB::bind_method(D_METHOD("set_private_key", "key"), &WebSocketServer::set_private_key);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", PROPERTY_USAGE_NONE), "set_private_key", "get_private_key");
-
- ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate);
- ClassDB::bind_method(D_METHOD("set_ssl_certificate", "cert"), &WebSocketServer::set_ssl_certificate);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ssl_certificate", "get_ssl_certificate");
-
- ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain);
- ClassDB::bind_method(D_METHOD("set_ca_chain", "ca_chain"), &WebSocketServer::set_ca_chain);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ca_chain", "get_ca_chain");
-
- ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout);
- ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout");
-
- ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
- ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close")));
- ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"), PropertyInfo(Variant::STRING, "resource_name")));
- ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id")));
-}
-
-IPAddress WebSocketServer::get_bind_ip() const {
- return bind_ip;
-}
-
-void WebSocketServer::set_bind_ip(const IPAddress &p_bind_ip) {
- ERR_FAIL_COND(is_listening());
- ERR_FAIL_COND(!p_bind_ip.is_valid() && !p_bind_ip.is_wildcard());
- bind_ip = p_bind_ip;
-}
-
-Ref<CryptoKey> WebSocketServer::get_private_key() const {
- return private_key;
-}
-
-void WebSocketServer::set_private_key(Ref<CryptoKey> p_key) {
- ERR_FAIL_COND(is_listening());
- private_key = p_key;
-}
-
-Ref<X509Certificate> WebSocketServer::get_ssl_certificate() const {
- return ssl_cert;
-}
-
-void WebSocketServer::set_ssl_certificate(Ref<X509Certificate> p_cert) {
- ERR_FAIL_COND(is_listening());
- ssl_cert = p_cert;
-}
-
-Ref<X509Certificate> WebSocketServer::get_ca_chain() const {
- return ca_chain;
-}
-
-void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) {
- ERR_FAIL_COND(is_listening());
- ca_chain = p_ca_chain;
-}
-
-float WebSocketServer::get_handshake_timeout() const {
- return handshake_timeout / 1000.0;
-}
-
-void WebSocketServer::set_handshake_timeout(float p_timeout) {
- ERR_FAIL_COND(p_timeout <= 0.0);
- handshake_timeout = p_timeout * 1000;
-}
-
-MultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
- if (is_listening()) {
- return CONNECTION_CONNECTED;
- }
-
- return CONNECTION_DISCONNECTED;
-}
-
-bool WebSocketServer::is_server() const {
- return true;
-}
-
-void WebSocketServer::_on_peer_packet(int32_t p_peer_id) {
- if (_is_multiplayer) {
- _process_multiplayer(get_peer(p_peer_id), p_peer_id);
- } else {
- emit_signal(SNAME("data_received"), p_peer_id);
- }
-}
-
-void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol, String p_resource_name) {
- if (_is_multiplayer) {
- // Send add to clients
- _send_add(p_peer_id);
- emit_signal(SNAME("peer_connected"), p_peer_id);
- } else {
- emit_signal(SNAME("client_connected"), p_peer_id, p_protocol, p_resource_name);
- }
-}
-
-void WebSocketServer::_on_disconnect(int32_t p_peer_id, bool p_was_clean) {
- if (_is_multiplayer) {
- // Send delete to clients
- _send_del(p_peer_id);
- emit_signal(SNAME("peer_disconnected"), p_peer_id);
- } else {
- emit_signal(SNAME("client_disconnected"), p_peer_id, p_was_clean);
- }
-}
-
-void WebSocketServer::_on_close_request(int32_t p_peer_id, int p_code, String p_reason) {
- emit_signal(SNAME("client_close_request"), p_peer_id, p_code, p_reason);
-}
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
deleted file mode 100644
index ac04c4e57e..0000000000
--- a/modules/websocket/websocket_server.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*************************************************************************/
-/* websocket_server.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 WEBSOCKET_SERVER_H
-#define WEBSOCKET_SERVER_H
-
-#include "core/crypto/crypto.h"
-#include "core/object/ref_counted.h"
-#include "websocket_multiplayer_peer.h"
-#include "websocket_peer.h"
-
-class WebSocketServer : public WebSocketMultiplayerPeer {
- GDCLASS(WebSocketServer, WebSocketMultiplayerPeer);
- GDCICLASS(WebSocketServer);
-
- IPAddress bind_ip;
-
-protected:
- static void _bind_methods();
-
- Ref<CryptoKey> private_key;
- Ref<X509Certificate> ssl_cert;
- Ref<X509Certificate> ca_chain;
- uint32_t handshake_timeout = 3000;
-
-public:
- virtual void set_extra_headers(const Vector<String> &p_headers) = 0;
- virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0;
- virtual void stop() = 0;
- virtual bool is_listening() const = 0;
- virtual bool has_peer(int p_id) const = 0;
- virtual bool is_server() const override;
- ConnectionStatus get_connection_status() const override;
-
- virtual IPAddress get_peer_address(int p_peer_id) const = 0;
- virtual int get_peer_port(int p_peer_id) const = 0;
- virtual void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") = 0;
-
- void _on_peer_packet(int32_t p_peer_id);
- void _on_connect(int32_t p_peer_id, String p_protocol, String p_resource_name);
- void _on_disconnect(int32_t p_peer_id, bool p_was_clean);
- void _on_close_request(int32_t p_peer_id, int p_code, String p_reason);
-
- IPAddress get_bind_ip() const;
- void set_bind_ip(const IPAddress &p_bind_ip);
-
- Ref<CryptoKey> get_private_key() const;
- void set_private_key(Ref<CryptoKey> p_key);
-
- Ref<X509Certificate> get_ssl_certificate() const;
- void set_ssl_certificate(Ref<X509Certificate> p_cert);
-
- Ref<X509Certificate> get_ca_chain() const;
- void set_ca_chain(Ref<X509Certificate> p_ca_chain);
-
- float get_handshake_timeout() const;
- void set_handshake_timeout(float p_timeout);
-
- WebSocketServer();
- ~WebSocketServer();
-};
-
-#endif // WEBSOCKET_SERVER_H
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
deleted file mode 100644
index 478dbb9d47..0000000000
--- a/modules/websocket/wsl_client.cpp
+++ /dev/null
@@ -1,407 +0,0 @@
-/*************************************************************************/
-/* wsl_client.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. */
-/*************************************************************************/
-
-#ifndef JAVASCRIPT_ENABLED
-
-#include "wsl_client.h"
-#include "core/config/project_settings.h"
-#include "core/io/ip.h"
-
-void WSLClient::_do_handshake() {
- if (_requested < _request.size() - 1) {
- int sent = 0;
- Error err = _connection->put_partial_data(((const uint8_t *)_request.get_data() + _requested), _request.size() - _requested - 1, sent);
- // Sending handshake failed
- if (err != OK) {
- disconnect_from_host();
- _on_error();
- return;
- }
- _requested += sent;
-
- } else {
- int read = 0;
- while (true) {
- if (_resp_pos >= WSL_MAX_HEADER_SIZE) {
- // Header is too big
- disconnect_from_host();
- _on_error();
- ERR_FAIL_MSG("Response headers too big.");
- }
- Error err = _connection->get_partial_data(&_resp_buf[_resp_pos], 1, read);
- if (err == ERR_FILE_EOF) {
- // We got a disconnect.
- disconnect_from_host();
- _on_error();
- return;
- } else if (err != OK) {
- // Got some error.
- disconnect_from_host();
- _on_error();
- return;
- } else if (read != 1) {
- // Busy, wait next poll.
- break;
- }
- // Check "\r\n\r\n" header terminator
- char *r = (char *)_resp_buf;
- int l = _resp_pos;
- if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
- r[l - 3] = '\0';
- String protocol;
- // Response is over, verify headers and create peer.
- if (!_verify_headers(protocol)) {
- disconnect_from_host();
- _on_error();
- ERR_FAIL_MSG("Invalid response headers.");
- }
- // Create peer.
- WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
- data->obj = this;
- data->conn = _connection;
- data->tcp = _tcp;
- data->is_server = false;
- 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;
- }
- _resp_pos += 1;
- }
- }
-}
-
-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.");
-
- Vector<String> req = psa[0].split(" ", false);
- 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", 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'.");
-
- 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] + ".");
- String name = header[0].to_lower();
- String value = header[1].strip_edges();
- if (headers.has(name)) {
- headers[name] += "," + value;
- } else {
- headers[name] = value;
- }
- }
-
-#define WSL_CHECK(NAME, VALUE) \
- ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
- "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define WSL_CHECK_NC(NAME, VALUE) \
- ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
- "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
- WSL_CHECK("connection", "upgrade");
- WSL_CHECK("upgrade", "websocket");
- WSL_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
-#undef WSL_CHECK_NC
-#undef WSL_CHECK
- if (_protocols.size() == 0) {
- // We didn't request a custom protocol
- ERR_FAIL_COND_V_MSG(headers.has("sec-websocket-protocol"), false, "Received unrequested sub-protocol -> " + headers["sec-websocket-protocol"]);
- } else {
- // 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++) {
- if (_protocols[i] != r_protocol) {
- continue;
- }
- valid = true;
- break;
- }
- if (!valid) {
- ERR_FAIL_V_MSG(false, "Received unrequested sub-protocol -> " + r_protocol);
- return false;
- }
- }
- return true;
-}
-
-Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
- ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
- ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
-
- _peer = Ref<WSLPeer>(memnew(WSLPeer));
-
- if (p_host.is_valid_ip_address()) {
- _ip_candidates.push_back(IPAddress(p_host));
- } else {
- // Queue hostname for resolution.
- _resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
- ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER);
- // Check if it was found in cache.
- IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
- if (ip_status == IP::RESOLVER_STATUS_DONE) {
- _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
- IP::get_singleton()->erase_resolve_item(_resolver_id);
- _resolver_id = IP::RESOLVER_INVALID_ID;
- }
- }
-
- // We assume OK while hostname resolution is pending.
- Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED;
- while (_ip_candidates.size()) {
- err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port);
- if (err == OK) {
- break;
- }
- }
- if (err != OK) {
- _tcp->disconnect_from_host();
- _on_error();
- return err;
- }
- _connection = _tcp;
- _use_ssl = p_ssl;
- _host = p_host;
- _port = p_port;
- // Strip edges from protocols.
- _protocols.resize(p_protocols.size());
- String *pw = _protocols.ptrw();
- for (int i = 0; i < p_protocols.size(); i++) {
- pw[i] = p_protocols[i].strip_edges();
- }
-
- _key = WSLPeer::generate_key();
- String request = "GET " + p_path + " HTTP/1.1\r\n";
- String port = "";
- if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
- port = ":" + itos(p_port);
- }
- request += "Host: " + p_host + port + "\r\n";
- request += "Upgrade: websocket\r\n";
- request += "Connection: Upgrade\r\n";
- request += "Sec-WebSocket-Key: " + _key + "\r\n";
- request += "Sec-WebSocket-Version: 13\r\n";
- if (p_protocols.size() > 0) {
- request += "Sec-WebSocket-Protocol: ";
- for (int i = 0; i < p_protocols.size(); i++) {
- if (i != 0) {
- request += ",";
- }
- request += p_protocols[i];
- }
- request += "\r\n";
- }
- for (int i = 0; i < p_custom_headers.size(); i++) {
- request += p_custom_headers[i] + "\r\n";
- }
- request += "\r\n";
- _request = request.utf8();
- _status = CONNECTION_CONNECTING;
-
- return OK;
-}
-
-int WSLClient::get_max_packet_size() const {
- return (1 << _out_buf_size) - PROTO_SIZE;
-}
-
-void WSLClient::poll() {
- if (_resolver_id != IP::RESOLVER_INVALID_ID) {
- IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
- if (ip_status == IP::RESOLVER_STATUS_WAITING) {
- return;
- }
- // Anything else is either a candidate or a failure.
- Error err = FAILED;
- if (ip_status == IP::RESOLVER_STATUS_DONE) {
- _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
- while (_ip_candidates.size()) {
- err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port);
- if (err == OK) {
- break;
- }
- }
- }
- IP::get_singleton()->erase_resolve_item(_resolver_id);
- _resolver_id = IP::RESOLVER_INVALID_ID;
- if (err != OK) {
- disconnect_from_host();
- _on_error();
- return;
- }
- }
- if (_peer->is_connected_to_host()) {
- _peer->poll();
- if (!_peer->is_connected_to_host()) {
- disconnect_from_host();
- _on_disconnect(_peer->close_code != -1);
- }
- return;
- }
-
- if (_connection.is_null()) {
- return; // Not connected.
- }
-
- _tcp->poll();
- switch (_tcp->get_status()) {
- case StreamPeerTCP::STATUS_NONE:
- // Clean close
- disconnect_from_host();
- _on_error();
- break;
- case StreamPeerTCP::STATUS_CONNECTED: {
- _ip_candidates.clear();
- Ref<StreamPeerSSL> ssl;
- if (_use_ssl) {
- if (_connection == _tcp) {
- // Start SSL handshake
- ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
- ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build.");
- ssl->set_blocking_handshake_enabled(false);
- if (ssl->connect_to_stream(_tcp, verify_ssl, _host, ssl_cert) != OK) {
- disconnect_from_host();
- _on_error();
- return;
- }
- _connection = ssl;
- } else {
- ssl = static_cast<Ref<StreamPeerSSL>>(_connection);
- ERR_FAIL_COND(ssl.is_null()); // Bug?
- ssl->poll();
- }
- if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) {
- return; // Need more polling.
- } else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
- disconnect_from_host();
- _on_error();
- return; // Error.
- }
- }
- // Do websocket handshake.
- _do_handshake();
- } break;
- case StreamPeerTCP::STATUS_ERROR:
- while (_ip_candidates.size() > 0) {
- _tcp->disconnect_from_host();
- if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) {
- return;
- }
- }
- disconnect_from_host();
- _on_error();
- break;
- case StreamPeerTCP::STATUS_CONNECTING:
- break; // Wait for connection
- }
-}
-
-Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const {
- ERR_FAIL_COND_V(p_peer_id != 1, nullptr);
-
- return _peer;
-}
-
-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;
- }
- 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 = "";
- _protocols.clear();
- _use_ssl = false;
-
- _request = "";
- _requested = 0;
-
- memset(_resp_buf, 0, sizeof(_resp_buf));
- _resp_pos = 0;
-
- if (_resolver_id != IP::RESOLVER_INVALID_ID) {
- IP::get_singleton()->erase_resolve_item(_resolver_id);
- _resolver_id = IP::RESOLVER_INVALID_ID;
- }
-
- _ip_candidates.clear();
-}
-
-IPAddress WSLClient::get_connected_host() const {
- ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IPAddress());
- return _peer->get_connected_host();
-}
-
-uint16_t WSLClient::get_connected_port() const {
- ERR_FAIL_COND_V(!_peer->is_connected_to_host(), 0);
- return _peer->get_connected_port();
-}
-
-Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- ERR_FAIL_COND_V_MSG(_connection.is_valid(), FAILED, "Buffers sizes can only be set before listening or connecting.");
-
- _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
- _in_pkt_size = nearest_shift(p_in_packets - 1);
- _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
- _out_pkt_size = nearest_shift(p_out_packets - 1);
- return OK;
-}
-
-WSLClient::WSLClient() {
- _peer.instantiate();
- _tcp.instantiate();
- disconnect_from_host();
-}
-
-WSLClient::~WSLClient() {
- _peer->close_now();
- _peer->invalidate();
- disconnect_from_host();
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
deleted file mode 100644
index 58b867fbe4..0000000000
--- a/modules/websocket/wsl_client.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* wsl_client.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 WSL_CLIENT_H
-#define WSL_CLIENT_H
-
-#ifndef JAVASCRIPT_ENABLED
-
-#include "core/error/error_list.h"
-#include "core/io/stream_peer_ssl.h"
-#include "core/io/stream_peer_tcp.h"
-#include "websocket_client.h"
-#include "wsl_peer.h"
-#include "wslay/wslay.h"
-
-class WSLClient : public WebSocketClient {
- GDCIIMPL(WSLClient, WebSocketClient);
-
-private:
- int _in_buf_size = DEF_BUF_SHIFT;
- int _in_pkt_size = DEF_PKT_SHIFT;
- int _out_buf_size = DEF_BUF_SHIFT;
- int _out_pkt_size = DEF_PKT_SHIFT;
-
- Ref<WSLPeer> _peer;
- Ref<StreamPeerTCP> _tcp;
- Ref<StreamPeer> _connection;
- ConnectionStatus _status = CONNECTION_DISCONNECTED;
-
- CharString _request;
- int _requested = 0;
-
- uint8_t _resp_buf[WSL_MAX_HEADER_SIZE];
- int _resp_pos = 0;
-
- String _key;
- String _host;
- uint16_t _port = 0;
- Array _ip_candidates;
- Vector<String> _protocols;
- bool _use_ssl = false;
- IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
-
- void _do_handshake();
- bool _verify_headers(String &r_protocol);
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) override;
- int get_max_packet_size() const override;
- Ref<WebSocketPeer> get_peer(int p_peer_id) const override;
- void disconnect_from_host(int p_code = 1000, String p_reason = "") override;
- IPAddress get_connected_host() const override;
- uint16_t get_connected_port() const override;
- virtual ConnectionStatus get_connection_status() const override;
- virtual void poll() override;
-
- WSLClient();
- ~WSLClient();
-};
-
-#endif // JAVASCRIPT_ENABLED
-
-#endif // WSL_CLIENT_H
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index 15df4d039c..9ba286d5ee 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -1,102 +1,568 @@
-/*************************************************************************/
-/* wsl_peer.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. */
-/*************************************************************************/
-
-#ifndef JAVASCRIPT_ENABLED
+/**************************************************************************/
+/* wsl_peer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEB_ENABLED
#include "wsl_peer.h"
-#include "wsl_client.h"
-#include "wsl_server.h"
+#include "wsl_peer.h"
-#include "core/crypto/crypto_core.h"
-#include "core/math/random_number_generator.h"
-#include "core/os/os.h"
+#include "core/io/stream_peer_tls.h"
-String WSLPeer::generate_key() {
- // Random key
- RandomNumberGenerator rng;
- rng.set_seed(OS::get_singleton()->get_unix_time());
- Vector<uint8_t> bkey;
- int len = 16; // 16 bytes, as per RFC
- bkey.resize(len);
- uint8_t *w = bkey.ptrw();
- for (int i = 0; i < len; i++) {
- w[i] = (uint8_t)rng.randi_range(0, 255);
+CryptoCore::RandomGenerator *WSLPeer::_static_rng = nullptr;
+
+void WSLPeer::initialize() {
+ WebSocketPeer::_create = WSLPeer::_create;
+ _static_rng = memnew(CryptoCore::RandomGenerator);
+ _static_rng->init();
+}
+
+void WSLPeer::deinitialize() {
+ if (_static_rng) {
+ memdelete(_static_rng);
+ _static_rng = nullptr;
}
- return CryptoCore::b64_encode_str(&w[0], len);
}
-String WSLPeer::compute_key_response(String p_key) {
- String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
- Vector<uint8_t> sha = key.sha1_buffer();
- return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
+///
+/// Resolver
+///
+void WSLPeer::Resolver::start(const String &p_host, int p_port) {
+ stop();
+
+ port = p_port;
+ if (p_host.is_valid_ip_address()) {
+ ip_candidates.push_back(IPAddress(p_host));
+ } else {
+ // Queue hostname for resolution.
+ resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
+ ERR_FAIL_COND(resolver_id == IP::RESOLVER_INVALID_ID);
+ // Check if it was found in cache.
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolver_id);
+ IP::get_singleton()->erase_resolve_item(resolver_id);
+ resolver_id = IP::RESOLVER_INVALID_ID;
+ }
+ }
}
-void WSLPeer::_wsl_destroy(struct PeerData **p_data) {
- if (!p_data || !(*p_data)) {
- return;
+void WSLPeer::Resolver::stop() {
+ if (resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::get_singleton()->erase_resolve_item(resolver_id);
+ resolver_id = IP::RESOLVER_INVALID_ID;
+ }
+ port = 0;
+}
+
+void WSLPeer::Resolver::try_next_candidate(Ref<StreamPeerTCP> &p_tcp) {
+ // Check if we still need resolving.
+ if (resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_WAITING) {
+ return;
+ }
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolver_id);
+ }
+ IP::get_singleton()->erase_resolve_item(resolver_id);
+ resolver_id = IP::RESOLVER_INVALID_ID;
}
- struct PeerData *data = *p_data;
- if (data->polling) {
- data->destroy = true;
+
+ // Try the current candidate if we have one.
+ if (p_tcp->get_status() != StreamPeerTCP::STATUS_NONE) {
+ p_tcp->poll();
+ StreamPeerTCP::Status status = p_tcp->get_status();
+ if (status == StreamPeerTCP::STATUS_CONNECTED) {
+ p_tcp->set_no_delay(true);
+ ip_candidates.clear();
+ return;
+ } else if (status == StreamPeerTCP::STATUS_CONNECTING) {
+ return; // Keep connecting.
+ } else {
+ p_tcp->disconnect_from_host();
+ }
+ }
+
+ // Keep trying next candidate.
+ while (ip_candidates.size()) {
+ Error err = p_tcp->connect_to_host(ip_candidates.pop_front(), port);
+ if (err == OK) {
+ return;
+ } else {
+ p_tcp->disconnect_from_host();
+ }
+ }
+}
+
+///
+/// Server functions
+///
+Error WSLPeer::accept_stream(Ref<StreamPeer> p_stream) {
+ ERR_FAIL_COND_V(wsl_ctx || tcp.is_valid(), ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_stream.is_null(), ERR_INVALID_PARAMETER);
+
+ _clear();
+
+ if (p_stream->is_class_ptr(StreamPeerTCP::get_class_ptr_static())) {
+ tcp = p_stream;
+ connection = p_stream;
+ use_tls = false;
+ } else if (p_stream->is_class_ptr(StreamPeerTLS::get_class_ptr_static())) {
+ Ref<StreamPeer> base_stream = static_cast<Ref<StreamPeerTLS>>(p_stream)->get_stream();
+ ERR_FAIL_COND_V(base_stream.is_null() || !base_stream->is_class_ptr(StreamPeerTCP::get_class_ptr_static()), ERR_INVALID_PARAMETER);
+ tcp = static_cast<Ref<StreamPeerTCP>>(base_stream);
+ connection = p_stream;
+ use_tls = true;
+ }
+ ERR_FAIL_COND_V(connection.is_null() || tcp.is_null(), ERR_INVALID_PARAMETER);
+ is_server = true;
+ ready_state = STATE_CONNECTING;
+ handshake_buffer->resize(WSL_MAX_HEADER_SIZE);
+ handshake_buffer->seek(0);
+ return OK;
+}
+
+bool WSLPeer::_parse_client_request() {
+ Vector<String> psa = String((const char *)handshake_buffer->get_data_array().ptr(), handshake_buffer->get_position() - 4).split("\r\n");
+ int len = psa.size();
+ 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.");
+
+ // Wrong protocol
+ ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
+
+ 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]);
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name)) {
+ headers[name] += "," + value;
+ } else {
+ headers[name] = value;
+ }
+ }
+ requested_host = headers.has("host") ? headers.get("host") : "";
+ requested_url = (use_tls ? "wss://" : "ws://") + requested_host + req[1];
+#define WSL_CHECK(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+#define WSL_CHECK_EX(NAME) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK("sec-websocket-version", "13");
+ WSL_CHECK_EX("sec-websocket-key");
+ WSL_CHECK_EX("connection");
+#undef WSL_CHECK_EX
+#undef WSL_CHECK
+ session_key = headers["sec-websocket-key"];
+ if (headers.has("sec-websocket-protocol")) {
+ Vector<String> protos = headers["sec-websocket-protocol"].split(",");
+ for (int i = 0; i < protos.size(); i++) {
+ String proto = protos[i].strip_edges();
+ // Check if we have the given protocol
+ for (int j = 0; j < supported_protocols.size(); j++) {
+ if (proto != supported_protocols[j]) {
+ continue;
+ }
+ selected_protocol = proto;
+ break;
+ }
+ // Found a protocol
+ if (!selected_protocol.is_empty()) {
+ break;
+ }
+ }
+ if (selected_protocol.is_empty()) { // Invalid protocol(s) requested
+ return false;
+ }
+ } else if (supported_protocols.size() > 0) { // No protocol requested, but we need one
+ return false;
+ }
+ return true;
+}
+
+Error WSLPeer::_do_server_handshake() {
+ if (use_tls) {
+ Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(connection);
+ if (tls.is_null()) {
+ ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerTLS for WebSocket handshake.");
+ close(-1);
+ return FAILED;
+ }
+ tls->poll();
+ if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
+ return OK; // Pending handshake
+ } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
+ print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerTLS status code %d).", tls->get_status()));
+ close(-1);
+ return FAILED;
+ }
+ }
+
+ if (pending_request) {
+ int read = 0;
+ while (true) {
+ ERR_FAIL_COND_V_MSG(handshake_buffer->get_available_bytes() < 1, ERR_OUT_OF_MEMORY, "WebSocket response headers are too big.");
+ int pos = handshake_buffer->get_position();
+ uint8_t byte;
+ Error err = connection->get_partial_data(&byte, 1, read);
+ if (err != OK) { // Got an error
+ print_verbose(vformat("WebSocket error while getting partial data (StreamPeer error code %d).", err));
+ close(-1);
+ return FAILED;
+ } else if (read != 1) { // Busy, wait next poll
+ return OK;
+ }
+ handshake_buffer->put_u8(byte);
+ const char *r = (const char *)handshake_buffer->get_data_array().ptr();
+ int l = pos;
+ if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
+ if (!_parse_client_request()) {
+ close(-1);
+ return FAILED;
+ }
+ String s = "HTTP/1.1 101 Switching Protocols\r\n";
+ s += "Upgrade: websocket\r\n";
+ s += "Connection: Upgrade\r\n";
+ s += "Sec-WebSocket-Accept: " + _compute_key_response(session_key) + "\r\n";
+ if (!selected_protocol.is_empty()) {
+ s += "Sec-WebSocket-Protocol: " + selected_protocol + "\r\n";
+ }
+ for (int i = 0; i < handshake_headers.size(); i++) {
+ s += handshake_headers[i] + "\r\n";
+ }
+ s += "\r\n";
+ CharString cs = s.utf8();
+ handshake_buffer->clear();
+ handshake_buffer->put_data((const uint8_t *)cs.get_data(), cs.length());
+ handshake_buffer->seek(0);
+ pending_request = false;
+ break;
+ }
+ }
+ }
+
+ if (pending_request) { // Still pending.
+ return OK;
+ }
+
+ int left = handshake_buffer->get_available_bytes();
+ if (left) {
+ Vector<uint8_t> data = handshake_buffer->get_data_array();
+ int pos = handshake_buffer->get_position();
+ int sent = 0;
+ Error err = connection->put_partial_data(data.ptr() + pos, left, sent);
+ if (err != OK) {
+ print_verbose(vformat("WebSocket error while putting partial data (StreamPeer error code %d).", err));
+ close(-1);
+ return err;
+ }
+ handshake_buffer->seek(pos + sent);
+ left -= sent;
+ if (left == 0) {
+ resolver.stop();
+ // Response sent, initialize wslay context.
+ wslay_event_context_server_init(&wsl_ctx, &_wsl_callbacks, this);
+ wslay_event_config_set_max_recv_msg_length(wsl_ctx, inbound_buffer_size);
+ in_buffer.resize(nearest_shift(inbound_buffer_size), max_queued_packets);
+ packet_buffer.resize(inbound_buffer_size);
+ ready_state = STATE_OPEN;
+ }
+ }
+
+ return OK;
+}
+
+///
+/// Client functions
+///
+void WSLPeer::_do_client_handshake() {
+ ERR_FAIL_COND(tcp.is_null());
+
+ // Try to connect to candidates.
+ if (resolver.has_more_candidates()) {
+ resolver.try_next_candidate(tcp);
+ if (resolver.has_more_candidates()) {
+ return; // Still pending.
+ }
+ }
+
+ tcp->poll();
+ if (tcp->get_status() == StreamPeerTCP::STATUS_CONNECTING) {
+ return; // Keep connecting.
+ } else if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+ close(-1); // Failed to connect.
return;
}
- wslay_event_context_free(data->ctx);
- memdelete(data);
- *p_data = nullptr;
+
+ if (use_tls) {
+ Ref<StreamPeerTLS> tls;
+ if (connection == tcp) {
+ // Start SSL handshake
+ tls = Ref<StreamPeerTLS>(StreamPeerTLS::create());
+ ERR_FAIL_COND_MSG(tls.is_null(), "SSL is not available in this build.");
+ tls->set_blocking_handshake_enabled(false);
+ if (tls->connect_to_stream(tcp, verify_tls, requested_host, tls_cert) != OK) {
+ close(-1);
+ return; // Error.
+ }
+ connection = tls;
+ } else {
+ tls = static_cast<Ref<StreamPeerTLS>>(connection);
+ ERR_FAIL_COND(tls.is_null());
+ tls->poll();
+ }
+ if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
+ return; // Need more polling.
+ } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
+ close(-1);
+ return; // Error.
+ }
+ }
+
+ // Do websocket handshake.
+ if (pending_request) {
+ int left = handshake_buffer->get_available_bytes();
+ int pos = handshake_buffer->get_position();
+ const Vector<uint8_t> data = handshake_buffer->get_data_array();
+ int sent = 0;
+ Error err = connection->put_partial_data(data.ptr() + pos, left, sent);
+ // Sending handshake failed
+ if (err != OK) {
+ close(-1);
+ return; // Error.
+ }
+ handshake_buffer->seek(pos + sent);
+ if (handshake_buffer->get_available_bytes() == 0) {
+ pending_request = false;
+ handshake_buffer->clear();
+ handshake_buffer->resize(WSL_MAX_HEADER_SIZE);
+ handshake_buffer->seek(0);
+ }
+ } else {
+ int read = 0;
+ while (true) {
+ int left = handshake_buffer->get_available_bytes();
+ int pos = handshake_buffer->get_position();
+ if (left == 0) {
+ // Header is too big
+ close(-1);
+ ERR_FAIL_MSG("Response headers too big.");
+ return;
+ }
+
+ uint8_t byte;
+ Error err = connection->get_partial_data(&byte, 1, read);
+ if (err != OK) {
+ // Got some error.
+ close(-1);
+ return;
+ } else if (read != 1) {
+ // Busy, wait next poll.
+ break;
+ }
+ handshake_buffer->put_u8(byte);
+
+ // Check "\r\n\r\n" header terminator
+ const char *r = (const char *)handshake_buffer->get_data_array().ptr();
+ int l = pos;
+ if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
+ // Response is over, verify headers and initialize wslay context/
+ if (!_verify_server_response()) {
+ close(-1);
+ ERR_FAIL_MSG("Invalid response headers.");
+ return;
+ }
+ wslay_event_context_client_init(&wsl_ctx, &_wsl_callbacks, this);
+ wslay_event_config_set_max_recv_msg_length(wsl_ctx, inbound_buffer_size);
+ in_buffer.resize(nearest_shift(inbound_buffer_size), max_queued_packets);
+ packet_buffer.resize(inbound_buffer_size);
+ ready_state = STATE_OPEN;
+ break;
+ }
+ }
+ }
}
-bool WSLPeer::_wsl_poll(struct PeerData *p_data) {
- p_data->polling = true;
- int err = 0;
- if ((err = wslay_event_recv(p_data->ctx)) != 0 || (err = wslay_event_send(p_data->ctx)) != 0) {
- print_verbose("Websocket (wslay) poll error: " + itos(err));
- p_data->destroy = true;
+bool WSLPeer::_verify_server_response() {
+ Vector<String> psa = String((const char *)handshake_buffer->get_data_array().ptr(), handshake_buffer->get_position() - 4).split("\r\n");
+ int len = psa.size();
+ 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. Got '" + psa[0] + "', expected 'HTTP/1.1 101'.");
+
+ // Wrong protocol
+ 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'.");
+
+ 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] + ".");
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name)) {
+ headers[name] += "," + value;
+ } else {
+ headers[name] = value;
+ }
}
- p_data->polling = false;
- if (p_data->destroy || (wslay_event_get_close_sent(p_data->ctx) && wslay_event_get_close_received(p_data->ctx))) {
- bool valid = p_data->valid;
- _wsl_destroy(&p_data);
- return valid;
+#define WSL_CHECK(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+#define WSL_CHECK_NC(NAME, VALUE) \
+ ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
+ "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
+ WSL_CHECK("connection", "upgrade");
+ WSL_CHECK("upgrade", "websocket");
+ WSL_CHECK_NC("sec-websocket-accept", _compute_key_response(session_key));
+#undef WSL_CHECK_NC
+#undef WSL_CHECK
+ if (supported_protocols.size() == 0) {
+ // We didn't request a custom protocol
+ ERR_FAIL_COND_V_MSG(headers.has("sec-websocket-protocol"), false, "Received unrequested sub-protocol -> " + headers["sec-websocket-protocol"]);
+ } else {
+ // 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.
+ selected_protocol = headers["sec-websocket-protocol"];
+ bool valid = false;
+ for (int i = 0; i < supported_protocols.size(); i++) {
+ if (supported_protocols[i] != selected_protocol) {
+ continue;
+ }
+ valid = true;
+ break;
+ }
+ if (!valid) {
+ ERR_FAIL_V_MSG(false, "Received unrequested sub-protocol -> " + selected_protocol);
+ return false;
+ }
+ }
+ return true;
+}
+
+Error WSLPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Certificate> p_cert) {
+ ERR_FAIL_COND_V(wsl_ctx || tcp.is_valid(), ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_url.is_empty(), ERR_INVALID_PARAMETER);
+
+ _clear();
+
+ String host;
+ String path;
+ String scheme;
+ int port = 0;
+ Error err = p_url.parse_url(scheme, host, port, path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
+ if (scheme.is_empty()) {
+ scheme = "ws://";
+ }
+ ERR_FAIL_COND_V_MSG(scheme != "ws://" && scheme != "wss://", ERR_INVALID_PARAMETER, vformat("Invalid protocol: \"%s\" (must be either \"ws://\" or \"wss://\").", scheme));
+
+ use_tls = false;
+ if (scheme == "wss://") {
+ use_tls = true;
+ }
+ if (port == 0) {
+ port = use_tls ? 443 : 80;
+ }
+ if (path.is_empty()) {
+ path = "/";
+ }
+
+ requested_url = p_url;
+ requested_host = host;
+ verify_tls = p_verify_tls;
+ tls_cert = p_cert;
+ tcp.instantiate();
+
+ resolver.start(host, port);
+ resolver.try_next_candidate(tcp);
+
+ if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTING && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED && !resolver.has_more_candidates()) {
+ _clear();
+ return FAILED;
+ }
+ connection = tcp;
+
+ // Prepare handshake request.
+ session_key = _generate_key();
+ String request = "GET " + path + " HTTP/1.1\r\n";
+ String port_string;
+ if ((port != 80 && !use_tls) || (port != 443 && use_tls)) {
+ port_string = ":" + itos(port);
+ }
+ request += "Host: " + host + port_string + "\r\n";
+ request += "Upgrade: websocket\r\n";
+ request += "Connection: Upgrade\r\n";
+ request += "Sec-WebSocket-Key: " + session_key + "\r\n";
+ request += "Sec-WebSocket-Version: 13\r\n";
+ if (supported_protocols.size() > 0) {
+ request += "Sec-WebSocket-Protocol: ";
+ for (int i = 0; i < supported_protocols.size(); i++) {
+ if (i != 0) {
+ request += ",";
+ }
+ request += supported_protocols[i];
+ }
+ request += "\r\n";
}
- return false;
+ for (int i = 0; i < handshake_headers.size(); i++) {
+ request += handshake_headers[i] + "\r\n";
+ }
+ request += "\r\n";
+ CharString cs = request.utf8();
+ handshake_buffer->put_data((const uint8_t *)cs.get_data(), cs.length());
+ handshake_buffer->seek(0);
+ ready_state = STATE_CONNECTING;
+ is_server = false;
+ return OK;
}
-ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
- struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
- if (!peer_data->valid) {
+///
+/// Callback functions.
+///
+ssize_t WSLPeer::_wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
+ WSLPeer *peer = (WSLPeer *)user_data;
+ Ref<StreamPeer> conn = peer->connection;
+ if (conn.is_null()) {
wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
return -1;
}
- Ref<StreamPeer> conn = peer_data->conn;
int read = 0;
Error err = conn->get_partial_data(data, len, read);
if (err != OK) {
@@ -111,13 +577,13 @@ ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len
return read;
}
-ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
- struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
- if (!peer_data->valid) {
+ssize_t WSLPeer::_wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
+ WSLPeer *peer = (WSLPeer *)user_data;
+ Ref<StreamPeer> conn = peer->connection;
+ if (conn.is_null()) {
wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
return -1;
}
- Ref<StreamPeer> conn = peer_data->conn;
int sent = 0;
Error err = conn->put_partial_data(data, len, sent);
if (err != OK) {
@@ -131,144 +597,142 @@ ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size
return sent;
}
-int wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
- RandomNumberGenerator rng;
- // TODO maybe use crypto in the future?
- rng.set_seed(OS::get_singleton()->get_unix_time());
- for (unsigned int i = 0; i < len; i++) {
- buf[i] = (uint8_t)rng.randi_range(0, 255);
- }
+int WSLPeer::_wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
+ ERR_FAIL_COND_V(!_static_rng, WSLAY_ERR_CALLBACK_FAILURE);
+ Error err = _static_rng->get_random_bytes(buf, len);
+ ERR_FAIL_COND_V(err != OK, WSLAY_ERR_CALLBACK_FAILURE);
return 0;
}
-void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
- struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
- if (!peer_data->valid || peer_data->closing) {
+void WSLPeer::_wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
+ WSLPeer *peer = (WSLPeer *)user_data;
+ uint8_t op = arg->opcode;
+
+ if (op == WSLAY_CONNECTION_CLOSE) {
+ // Close request or confirmation.
+ peer->close_code = arg->status_code;
+ size_t len = arg->msg_length;
+ peer->close_reason = "";
+ if (len > 2 /* first 2 bytes = close code */) {
+ peer->close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
+ }
+ if (peer->ready_state == STATE_OPEN) {
+ peer->ready_state = STATE_CLOSING;
+ }
return;
}
- WSLPeer *peer = static_cast<WSLPeer *>(peer_data->peer);
- if (peer->parse_message(arg) != OK) {
+ if (peer->ready_state == STATE_CLOSING) {
return;
}
- if (peer_data->is_server) {
- WSLServer *helper = static_cast<WSLServer *>(peer_data->obj);
- helper->_on_peer_packet(peer_data->id);
- } else {
- WSLClient *helper = static_cast<WSLClient *>(peer_data->obj);
- helper->_on_peer_packet();
+ if (op == WSLAY_TEXT_FRAME || op == WSLAY_BINARY_FRAME) {
+ // Message.
+ uint8_t is_string = arg->opcode == WSLAY_TEXT_FRAME ? 1 : 0;
+ peer->in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
}
+ // Ping or pong.
}
-wslay_event_callbacks wsl_callbacks = {
- wsl_recv_callback,
- wsl_send_callback,
- wsl_genmask_callback,
+wslay_event_callbacks WSLPeer::_wsl_callbacks = {
+ _wsl_recv_callback,
+ _wsl_send_callback,
+ _wsl_genmask_callback,
nullptr, /* on_frame_recv_start_callback */
nullptr, /* on_frame_recv_callback */
nullptr, /* on_frame_recv_end_callback */
- wsl_msg_recv_callback
+ _wsl_msg_recv_callback
};
-Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) {
- uint8_t is_string = 0;
- if (arg->opcode == WSLAY_TEXT_FRAME) {
- is_string = 1;
- } else if (arg->opcode == WSLAY_CONNECTION_CLOSE) {
- close_code = arg->status_code;
- size_t len = arg->msg_length;
- close_reason = "";
- if (len > 2 /* first 2 bytes = close code */) {
- close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
- }
- if (!wslay_event_get_close_sent(_data->ctx)) {
- if (_data->is_server) {
- WSLServer *helper = static_cast<WSLServer *>(_data->obj);
- helper->_on_close_request(_data->id, close_code, close_reason);
- } else {
- WSLClient *helper = static_cast<WSLClient *>(_data->obj);
- helper->_on_close_request(close_code, close_reason);
- }
- }
- return ERR_FILE_EOF;
- } else if (arg->opcode != WSLAY_BINARY_FRAME) {
- // Ping or pong
- return ERR_SKIP;
- }
- _in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
- return OK;
-}
-
-void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) {
- ERR_FAIL_COND(_data != nullptr);
- ERR_FAIL_COND(p_data == nullptr);
-
- _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
- _packet_buffer.resize(1 << p_in_buf_size);
- _out_buf_size = p_out_buf_size;
- _out_pkt_size = p_out_pkt_size;
-
- _data = p_data;
- _data->peer = this;
- _data->valid = true;
-
- if (_data->is_server) {
- wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data);
- } else {
- wslay_event_context_client_init(&(_data->ctx), &wsl_callbacks, _data);
- }
- wslay_event_config_set_max_recv_msg_length(_data->ctx, (1ULL << p_in_buf_size));
-}
-
-void WSLPeer::set_write_mode(WriteMode p_mode) {
- write_mode = p_mode;
+String WSLPeer::_generate_key() {
+ // Random key
+ Vector<uint8_t> bkey;
+ int len = 16; // 16 bytes, as per RFC
+ bkey.resize(len);
+ _wsl_genmask_callback(nullptr, bkey.ptrw(), len, nullptr);
+ return CryptoCore::b64_encode_str(bkey.ptrw(), len);
}
-WSLPeer::WriteMode WSLPeer::get_write_mode() const {
- return write_mode;
+String WSLPeer::_compute_key_response(String p_key) {
+ String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
+ Vector<uint8_t> sha = key.sha1_buffer();
+ return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
}
void WSLPeer::poll() {
- if (!_data) {
+ // Nothing to do.
+ if (ready_state == STATE_CLOSED) {
return;
}
- if (_wsl_poll(_data)) {
- _data = nullptr;
+ if (ready_state == STATE_CONNECTING) {
+ if (is_server) {
+ _do_server_handshake();
+ } else {
+ _do_client_handshake();
+ }
+ }
+
+ if (ready_state == STATE_OPEN || ready_state == STATE_CLOSING) {
+ ERR_FAIL_COND(!wsl_ctx);
+ int err = 0;
+ if ((err = wslay_event_recv(wsl_ctx)) != 0 || (err = wslay_event_send(wsl_ctx)) != 0) {
+ // Error close.
+ print_verbose("Websocket (wslay) poll error: " + itos(err));
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ }
+ if (wslay_event_get_close_sent(wsl_ctx) && wslay_event_get_close_received(wsl_ctx)) {
+ // Clean close.
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ }
}
}
-Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
- ERR_FAIL_COND_V(_out_pkt_size && (wslay_event_get_queued_msg_count(_data->ctx) >= (1ULL << _out_pkt_size)), ERR_OUT_OF_MEMORY);
- ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
+Error WSLPeer::_send(const uint8_t *p_buffer, int p_buffer_size, wslay_opcode p_opcode) {
+ ERR_FAIL_COND_V(ready_state != STATE_OPEN, FAILED);
+ ERR_FAIL_COND_V(wslay_event_get_queued_msg_count(wsl_ctx) >= (uint32_t)max_queued_packets, ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(outbound_buffer_size > 0 && (wslay_event_get_queued_msg_length(wsl_ctx) + p_buffer_size > (uint32_t)outbound_buffer_size), ERR_OUT_OF_MEMORY);
struct wslay_event_msg msg;
- msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
+ msg.opcode = p_opcode;
msg.msg = p_buffer;
msg.msg_length = p_buffer_size;
// Queue & send message.
- if (wslay_event_queue_msg(_data->ctx, &msg) != 0 || wslay_event_send(_data->ctx) != 0) {
- close_now();
+ if (wslay_event_queue_msg(wsl_ctx, &msg) != 0 || wslay_event_send(wsl_ctx) != 0) {
+ close(-1);
return FAILED;
}
return OK;
}
+Error WSLPeer::send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) {
+ wslay_opcode opcode = p_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
+ return _send(p_buffer, p_buffer_size, opcode);
+}
+
+Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ return _send(p_buffer, p_buffer_size, WSLAY_BINARY_FRAME);
+}
+
Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
r_buffer_size = 0;
- ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+ ERR_FAIL_COND_V(ready_state != STATE_OPEN, FAILED);
- if (_in_buffer.packets_left() == 0) {
+ if (in_buffer.packets_left() == 0) {
return ERR_UNAVAILABLE;
}
int read = 0;
- uint8_t *rw = _packet_buffer.ptrw();
- _in_buffer.read_packet(rw, _packet_buffer.size(), &_is_string, read);
+ uint8_t *rw = packet_buffer.ptrw();
+ in_buffer.read_packet(rw, packet_buffer.size(), &was_string, read);
*r_buffer = rw;
r_buffer_size = read;
@@ -277,75 +741,106 @@ Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
}
int WSLPeer::get_available_packet_count() const {
- if (!is_connected_to_host()) {
+ if (ready_state != STATE_OPEN) {
return 0;
}
- return _in_buffer.packets_left();
+ return in_buffer.packets_left();
}
int WSLPeer::get_current_outbound_buffered_amount() const {
- ERR_FAIL_COND_V(!_data, 0);
-
- return wslay_event_get_queued_msg_length(_data->ctx);
-}
-
-bool WSLPeer::was_string_packet() const {
- return _is_string;
-}
-
-bool WSLPeer::is_connected_to_host() const {
- return _data != nullptr;
-}
+ if (ready_state != STATE_OPEN) {
+ return 0;
+ }
-void WSLPeer::close_now() {
- close(1000, "");
- _wsl_destroy(&_data);
+ return wslay_event_get_queued_msg_length(wsl_ctx);
}
void WSLPeer::close(int p_code, String p_reason) {
- if (_data && !wslay_event_get_close_sent(_data->ctx)) {
+ if (p_code < 0) {
+ // Force immediate close.
+ ready_state = STATE_CLOSED;
+ }
+
+ if (ready_state == STATE_OPEN && !wslay_event_get_close_sent(wsl_ctx)) {
CharString cs = p_reason.utf8();
- wslay_event_queue_close(_data->ctx, p_code, (uint8_t *)cs.ptr(), cs.size());
- wslay_event_send(_data->ctx);
- _data->closing = true;
+ wslay_event_queue_close(wsl_ctx, p_code, (uint8_t *)cs.ptr(), cs.length());
+ wslay_event_send(wsl_ctx);
+ ready_state = STATE_CLOSING;
+ } else if (ready_state == STATE_CONNECTING || ready_state == STATE_CLOSED) {
+ ready_state = STATE_CLOSED;
+ connection.unref();
+ if (tcp.is_valid()) {
+ tcp->disconnect_from_host();
+ tcp.unref();
+ }
}
- _in_buffer.clear();
- _packet_buffer.resize(0);
+ in_buffer.clear();
+ packet_buffer.resize(0);
}
IPAddress WSLPeer::get_connected_host() const {
- ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IPAddress());
-
- return _data->tcp->get_connected_host();
+ ERR_FAIL_COND_V(tcp.is_null(), IPAddress());
+ return tcp->get_connected_host();
}
uint16_t WSLPeer::get_connected_port() const {
- ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), 0);
+ ERR_FAIL_COND_V(tcp.is_null(), 0);
+ return tcp->get_connected_port();
+}
+
+String WSLPeer::get_selected_protocol() const {
+ return selected_protocol;
+}
- return _data->tcp->get_connected_port();
+String WSLPeer::get_requested_url() const {
+ return requested_url;
}
void WSLPeer::set_no_delay(bool p_enabled) {
- ERR_FAIL_COND(!is_connected_to_host() || _data->tcp.is_null());
- _data->tcp->set_no_delay(p_enabled);
+ ERR_FAIL_COND(tcp.is_null());
+ tcp->set_no_delay(p_enabled);
}
-void WSLPeer::invalidate() {
- if (_data) {
- _data->valid = false;
+void WSLPeer::_clear() {
+ // Connection info.
+ ready_state = STATE_CLOSED;
+ is_server = false;
+ connection.unref();
+ if (tcp.is_valid()) {
+ tcp->disconnect_from_host();
+ tcp.unref();
}
+ if (wsl_ctx) {
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ }
+
+ resolver.stop();
+ requested_url.clear();
+ requested_host.clear();
+ pending_request = true;
+ handshake_buffer->clear();
+ selected_protocol.clear();
+ session_key.clear();
+
+ // Pending packets info.
+ was_string = 0;
+ in_buffer.clear();
+ packet_buffer.clear();
+
+ // Close code info.
+ close_code = -1;
+ close_reason.clear();
}
WSLPeer::WSLPeer() {
+ handshake_buffer.instantiate();
}
WSLPeer::~WSLPeer() {
- close();
- invalidate();
- _wsl_destroy(&_data);
- _data = nullptr;
+ close(-1);
}
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index aabd3fd43e..fc81d39a37 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -1,115 +1,159 @@
-/*************************************************************************/
-/* wsl_peer.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* wsl_peer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WSL_PEER_H
#define WSL_PEER_H
-#ifndef JAVASCRIPT_ENABLED
+#ifndef WEB_ENABLED
+#include "websocket_peer.h"
+
+#include "packet_buffer.h"
+
+#include "core/crypto/crypto_core.h"
#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
#include "core/io/stream_peer_tcp.h"
#include "core/templates/ring_buffer.h"
-#include "packet_buffer.h"
-#include "websocket_peer.h"
#include "wslay/wslay.h"
#define WSL_MAX_HEADER_SIZE 4096
class WSLPeer : public WebSocketPeer {
- GDCIIMPL(WSLPeer, WebSocketPeer);
-
-public:
- struct PeerData {
- bool polling = false;
- bool destroy = false;
- bool valid = false;
- bool is_server = false;
- bool closing = false;
- void *obj = nullptr;
- void *peer = nullptr;
- Ref<StreamPeer> conn;
- Ref<StreamPeerTCP> tcp;
- int id = 1;
- wslay_event_context_ptr ctx = nullptr;
+private:
+ static CryptoCore::RandomGenerator *_static_rng;
+ static WebSocketPeer *_create() { return memnew(WSLPeer); }
+
+ // Callbacks.
+ static ssize_t _wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data);
+ static ssize_t _wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data);
+ static int _wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data);
+ static void _wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
+
+ static wslay_event_callbacks _wsl_callbacks;
+
+ // Helpers
+ static String _compute_key_response(String p_key);
+ static String _generate_key();
+
+ // Client IP resolver.
+ class Resolver {
+ Array ip_candidates;
+ IP::ResolverID resolver_id = IP::RESOLVER_INVALID_ID;
+ int port = 0;
+
+ public:
+ bool has_more_candidates() {
+ return ip_candidates.size() > 0 || resolver_id != IP::RESOLVER_INVALID_ID;
+ }
+
+ void try_next_candidate(Ref<StreamPeerTCP> &p_tcp);
+ void start(const String &p_host, int p_port);
+ void stop();
+ Resolver() {}
};
- static String compute_key_response(String p_key);
- static String generate_key();
+ Resolver resolver;
-private:
- static bool _wsl_poll(struct PeerData *p_data);
- static void _wsl_destroy(struct PeerData **p_data);
+ // WebSocket connection state.
+ WebSocketPeer::State ready_state = WebSocketPeer::STATE_CLOSED;
+ bool is_server = false;
+ Ref<StreamPeerTCP> tcp;
+ Ref<StreamPeer> connection;
+ wslay_event_context_ptr wsl_ctx = nullptr;
+
+ String requested_url;
+ String requested_host;
+ bool pending_request = true;
+ Ref<StreamPeerBuffer> handshake_buffer;
+ String selected_protocol;
+ String session_key;
- struct PeerData *_data = nullptr;
- uint8_t _is_string = 0;
+ int close_code = -1;
+ String close_reason;
+ uint8_t was_string = 0;
+
+ // WebSocket configuration.
+ bool use_tls = true;
+ bool verify_tls = true;
+ Ref<X509Certificate> tls_cert;
+
+ // Packet buffers.
+ Vector<uint8_t> packet_buffer;
// Our packet info is just a boolean (is_string), using uint8_t for it.
- PacketBuffer<uint8_t> _in_buffer;
+ PacketBuffer<uint8_t> in_buffer;
- Vector<uint8_t> _packet_buffer;
+ Error _send(const uint8_t *p_buffer, int p_buffer_size, wslay_opcode p_opcode);
- WriteMode write_mode = WRITE_MODE_BINARY;
+ Error _do_server_handshake();
+ bool _parse_client_request();
- int _out_buf_size = 0;
- int _out_pkt_size = 0;
+ void _do_client_handshake();
+ bool _verify_server_response();
+
+ void _clear();
public:
- int close_code = -1;
- String close_reason;
- void poll(); // Used by client and server.
+ static void initialize();
+ static void deinitialize();
+ // PacketPeer
virtual int get_available_packet_count() const override;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const override { return _packet_buffer.size(); };
- virtual int get_current_outbound_buffered_amount() const override;
+ virtual int get_max_packet_size() const override { return packet_buffer.size(); };
- virtual void close_now();
+ // WebSocketPeer
+ virtual Error send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) override;
+ virtual Error connect_to_url(const String &p_url, bool p_verify_tls = true, Ref<X509Certificate> p_cert = Ref<X509Certificate>()) override;
+ virtual Error accept_stream(Ref<StreamPeer> p_stream) override;
virtual void close(int p_code = 1000, String p_reason = "") override;
- virtual bool is_connected_to_host() const override;
+ virtual void poll() override;
+
+ virtual State get_ready_state() const override { return ready_state; }
+ virtual int get_close_code() const override { return close_code; }
+ virtual String get_close_reason() const override { return close_reason; }
+ virtual int get_current_outbound_buffered_amount() const override;
+
virtual IPAddress get_connected_host() const override;
virtual uint16_t get_connected_port() const override;
+ virtual String get_selected_protocol() const override;
+ virtual String get_requested_url() const override;
- virtual WriteMode get_write_mode() const override;
- virtual void set_write_mode(WriteMode p_mode) override;
- virtual bool was_string_packet() const override;
+ virtual bool was_string_packet() const override { return was_string; }
virtual void set_no_delay(bool p_enabled) override;
- void make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size);
- Error parse_message(const wslay_event_on_msg_recv_arg *arg);
- void invalidate();
-
WSLPeer();
~WSLPeer();
};
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
#endif // WSL_PEER_H
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
deleted file mode 100644
index 517b9643f8..0000000000
--- a/modules/websocket/wsl_server.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*************************************************************************/
-/* wsl_server.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. */
-/*************************************************************************/
-
-#ifndef JAVASCRIPT_ENABLED
-
-#include "wsl_server.h"
-#include "core/config/project_settings.h"
-#include "core/os/os.h"
-
-bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, String &r_resource_name) {
- Vector<String> psa = String((char *)req_buf).split("\r\n");
- int len = psa.size();
- 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.");
-
- // Wrong protocol
- ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
-
- r_resource_name = req[1];
- 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]);
- String name = header[0].to_lower();
- String value = header[1].strip_edges();
- if (headers.has(name)) {
- headers[name] += "," + value;
- } else {
- headers[name] = value;
- }
- }
-#define WSL_CHECK(NAME, VALUE) \
- ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
- "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
-#define WSL_CHECK_EX(NAME) \
- ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
- WSL_CHECK("upgrade", "websocket");
- WSL_CHECK("sec-websocket-version", "13");
- WSL_CHECK_EX("sec-websocket-key");
- WSL_CHECK_EX("connection");
-#undef WSL_CHECK_EX
-#undef WSL_CHECK
- key = headers["sec-websocket-key"];
- if (headers.has("sec-websocket-protocol")) {
- Vector<String> protos = headers["sec-websocket-protocol"].split(",");
- for (int i = 0; i < protos.size(); i++) {
- String proto = protos[i].strip_edges();
- // Check if we have the given protocol
- for (int j = 0; j < p_protocols.size(); j++) {
- if (proto != p_protocols[j]) {
- continue;
- }
- protocol = proto;
- break;
- }
- // Found a protocol
- if (!protocol.is_empty()) {
- break;
- }
- }
- if (protocol.is_empty()) { // Invalid protocol(s) requested
- return false;
- }
- } else if (p_protocols.size() > 0) { // No protocol requested, but we need one
- return false;
- }
- return true;
-}
-
-Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name, const Vector<String> &p_extra_headers) {
- if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) {
- print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", p_timeout * 0.001));
- return ERR_TIMEOUT;
- }
-
- if (use_ssl) {
- Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL>>(connection);
- if (ssl.is_null()) {
- ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerSSL for WebSocket handshake.");
- }
- ssl->poll();
- if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) {
- return ERR_BUSY;
- } else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
- print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerSSL status code %d).", ssl->get_status()));
- return FAILED;
- }
- }
-
- if (!has_request) {
- int read = 0;
- while (true) {
- ERR_FAIL_COND_V_MSG(req_pos >= WSL_MAX_HEADER_SIZE, ERR_OUT_OF_MEMORY, "WebSocket response headers are too big.");
- Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
- if (err != OK) { // Got an error
- print_verbose(vformat("WebSocket error while getting partial data (StreamPeer error code %d).", err));
- return FAILED;
- } else if (read != 1) { // Busy, wait next poll
- return ERR_BUSY;
- }
- char *r = (char *)req_buf;
- int l = req_pos;
- if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
- r[l - 3] = '\0';
- if (!_parse_request(p_protocols, r_resource_name)) {
- return FAILED;
- }
- String s = "HTTP/1.1 101 Switching Protocols\r\n";
- s += "Upgrade: websocket\r\n";
- s += "Connection: Upgrade\r\n";
- s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n";
- if (!protocol.is_empty()) {
- s += "Sec-WebSocket-Protocol: " + protocol + "\r\n";
- }
- for (int i = 0; i < p_extra_headers.size(); i++) {
- s += p_extra_headers[i] + "\r\n";
- }
- s += "\r\n";
- response = s.utf8();
- has_request = true;
- break;
- }
- req_pos += 1;
- }
- }
-
- if (has_request && response_sent < response.size() - 1) {
- int sent = 0;
- Error err = connection->put_partial_data((const uint8_t *)response.get_data() + response_sent, response.size() - response_sent - 1, sent);
- if (err != OK) {
- print_verbose(vformat("WebSocket error while putting partial data (StreamPeer error code %d).", err));
- return err;
- }
- response_sent += sent;
- }
-
- if (response_sent < response.size() - 1) {
- return ERR_BUSY;
- }
-
- return OK;
-}
-
-void WSLServer::set_extra_headers(const Vector<String> &p_headers) {
- _extra_headers = p_headers;
-}
-
-Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp_api) {
- ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE);
-
- _is_multiplayer = gd_mp_api;
- // Strip edges from protocols.
- _protocols.resize(p_protocols.size());
- String *pw = _protocols.ptrw();
- for (int i = 0; i < p_protocols.size(); i++) {
- pw[i] = p_protocols[i].strip_edges();
- }
- return _server->listen(p_port, bind_ip);
-}
-
-void WSLServer::poll() {
- List<int> remove_ids;
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- Ref<WSLPeer> peer = const_cast<WSLPeer *>(static_cast<const WSLPeer *>(E.value.ptr()));
- peer->poll();
- if (!peer->is_connected_to_host()) {
- _on_disconnect(E.key, peer->close_code != -1);
- remove_ids.push_back(E.key);
- }
- }
- for (int &E : remove_ids) {
- _peer_map.erase(E);
- }
- remove_ids.clear();
-
- List<Ref<PendingPeer>> remove_peers;
- for (const Ref<PendingPeer> &E : _pending) {
- String resource_name;
- Ref<PendingPeer> ppeer = E;
- Error err = ppeer->do_handshake(_protocols, handshake_timeout, resource_name, _extra_headers);
- if (err == ERR_BUSY) {
- continue;
- } else if (err != OK) {
- remove_peers.push_back(ppeer);
- continue;
- }
- // Creating new peer
- int32_t id = generate_unique_id();
-
- WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
- data->obj = this;
- data->conn = ppeer->connection;
- data->tcp = ppeer->tcp;
- data->is_server = true;
- data->id = id;
-
- Ref<WSLPeer> ws_peer = memnew(WSLPeer);
- ws_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
- ws_peer->set_no_delay(true);
-
- _peer_map[id] = ws_peer;
- remove_peers.push_back(ppeer);
- _on_connect(id, ppeer->protocol, resource_name);
- }
- for (const Ref<PendingPeer> &E : remove_peers) {
- _pending.erase(E);
- }
- remove_peers.clear();
-
- if (!_server->is_listening()) {
- return;
- }
-
- while (_server->is_connection_available()) {
- Ref<StreamPeerTCP> conn = _server->take_connection();
- if (is_refusing_new_connections()) {
- continue; // Conn will go out-of-scope and be closed.
- }
-
- Ref<PendingPeer> peer = memnew(PendingPeer);
- if (private_key.is_valid() && ssl_cert.is_valid()) {
- Ref<StreamPeerSSL> ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
- ssl->set_blocking_handshake_enabled(false);
- ssl->accept_stream(conn, private_key, ssl_cert, ca_chain);
- peer->connection = ssl;
- peer->use_ssl = true;
- } else {
- peer->connection = conn;
- }
- peer->tcp = conn;
- peer->time = OS::get_singleton()->get_ticks_msec();
- _pending.push_back(peer);
- }
-}
-
-bool WSLServer::is_listening() const {
- return _server->is_listening();
-}
-
-int WSLServer::get_max_packet_size() const {
- return (1 << _out_buf_size) - PROTO_SIZE;
-}
-
-void WSLServer::stop() {
- _server->stop();
- for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
- Ref<WSLPeer> peer = const_cast<WSLPeer *>(static_cast<const WSLPeer *>(E.value.ptr()));
- peer->close_now();
- }
- _pending.clear();
- _peer_map.clear();
- _protocols.clear();
-}
-
-bool WSLServer::has_peer(int p_id) const {
- return _peer_map.has(p_id);
-}
-
-Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const {
- ERR_FAIL_COND_V(!has_peer(p_id), nullptr);
- return _peer_map[p_id];
-}
-
-IPAddress WSLServer::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), IPAddress());
-
- return _peer_map[p_peer_id]->get_connected_host();
-}
-
-int WSLServer::get_peer_port(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
-
- return _peer_map[p_peer_id]->get_connected_port();
-}
-
-void WSLServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
- ERR_FAIL_COND(!has_peer(p_peer_id));
-
- get_peer(p_peer_id)->close(p_code, p_reason);
-}
-
-Error WSLServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
- ERR_FAIL_COND_V_MSG(_server->is_listening(), FAILED, "Buffers sizes can only be set before listening or connecting.");
-
- _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
- _in_pkt_size = nearest_shift(p_in_packets - 1);
- _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
- _out_pkt_size = nearest_shift(p_out_packets - 1);
- return OK;
-}
-
-WSLServer::WSLServer() {
- _server.instantiate();
-}
-
-WSLServer::~WSLServer() {
- stop();
-}
-
-#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
deleted file mode 100644
index ec7567c732..0000000000
--- a/modules/websocket/wsl_server.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*************************************************************************/
-/* wsl_server.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 WSL_SERVER_H
-#define WSL_SERVER_H
-
-#ifndef JAVASCRIPT_ENABLED
-
-#include "websocket_server.h"
-#include "wsl_peer.h"
-
-#include "core/io/stream_peer_ssl.h"
-#include "core/io/stream_peer_tcp.h"
-#include "core/io/tcp_server.h"
-
-class WSLServer : public WebSocketServer {
- GDCIIMPL(WSLServer, WebSocketServer);
-
-private:
- class PendingPeer : public RefCounted {
- private:
- bool _parse_request(const Vector<String> p_protocols, String &r_resource_name);
-
- public:
- Ref<StreamPeerTCP> tcp;
- Ref<StreamPeer> connection;
- bool use_ssl = false;
-
- uint64_t time = 0;
- uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {};
- int req_pos = 0;
- String key;
- String protocol;
- bool has_request = false;
- CharString response;
- int response_sent = 0;
-
- Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name, const Vector<String> &p_extra_headers);
- };
-
- int _in_buf_size = DEF_BUF_SHIFT;
- int _in_pkt_size = DEF_PKT_SHIFT;
- int _out_buf_size = DEF_BUF_SHIFT;
- int _out_pkt_size = DEF_PKT_SHIFT;
-
- List<Ref<PendingPeer>> _pending;
- Ref<TCPServer> _server;
- Vector<String> _protocols;
- Vector<String> _extra_headers;
-
-public:
- Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) override;
- void set_extra_headers(const Vector<String> &p_headers) override;
- Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) override;
- void stop() override;
- bool is_listening() const override;
- int get_max_packet_size() const override;
- bool has_peer(int p_id) const override;
- Ref<WebSocketPeer> get_peer(int p_id) const override;
- IPAddress get_peer_address(int p_peer_id) const override;
- int get_peer_port(int p_peer_id) const override;
- void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") override;
- virtual void poll() override;
-
- WSLServer();
- ~WSLServer();
-};
-
-#endif // JAVASCRIPT_ENABLED
-
-#endif // WSL_SERVER_H
diff --git a/modules/webxr/SCsub b/modules/webxr/SCsub
index 0a96af0811..81caa4a279 100644
--- a/modules/webxr/SCsub
+++ b/modules/webxr/SCsub
@@ -3,7 +3,7 @@
Import("env")
Import("env_modules")
-if env["platform"] == "javascript":
+if env["platform"] == "web":
env.AddJSLibraries(["native/library_godot_webxr.js"])
env.AddJSExterns(["native/webxr.externs.js"])
diff --git a/modules/webxr/config.py b/modules/webxr/config.py
index f676ef3483..8d75e7f531 100644
--- a/modules/webxr/config.py
+++ b/modules/webxr/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return not env["disable_3d"]
+ return env["opengl3"] and not env["disable_3d"]
def configure(env):
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index 48447eb074..ba1750386f 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebXRInterface" inherits="XRInterface" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- AR/VR interface using WebXR.
+ XR interface using WebXR.
</brief_description>
<description>
WebXR is an open standard that allows creating VR and AR applications that run in the web browser.
- As such, this interface is only available when running in an HTML5 export.
+ As such, this interface is only available when running in Web exports.
WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones).
- Since WebXR is based on Javascript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other AR/VR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to initialize than other AR/VR interfaces.
+ Since WebXR is based on JavaScript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other XR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to initialize than other XR interfaces.
Here's the minimum code required to start an immersive VR session:
[codeblock]
extends Node3D
@@ -18,16 +18,16 @@
func _ready():
# We assume this node has a button as a child.
# This button is for the user to consent to entering immersive VR mode.
- $Button.connect("pressed", self, "_on_Button_pressed")
+ $Button.pressed.connect(self._on_button_pressed)
webxr_interface = XRServer.find_interface("WebXR")
if webxr_interface:
# WebXR uses a lot of asynchronous callbacks, so we connect to various
# signals in order to receive them.
- webxr_interface.connect("session_supported", self, "_webxr_session_supported")
- webxr_interface.connect("session_started", self, "_webxr_session_started")
- webxr_interface.connect("session_ended", self, "_webxr_session_ended")
- webxr_interface.connect("session_failed", self, "_webxr_session_failed")
+ webxr_interface.session_supported.connect(self._webxr_session_supported)
+ webxr_interface.session_started.connect(self._webxr_session_started)
+ webxr_interface.session_ended.connect(self._webxr_session_ended)
+ webxr_interface.session_failed.connect(self._webxr_session_failed)
# This returns immediately - our _webxr_session_supported() method
# (which we connected to the "session_supported" signal above) will
@@ -38,7 +38,7 @@
if session_mode == 'immersive-vr':
vr_supported = supported
- func _on_Button_pressed():
+ func _on_button_pressed():
if not vr_supported:
OS.alert("Your browser doesn't support VR")
return
@@ -69,7 +69,7 @@
func _webxr_session_started():
$Button.visible = false
# This tells Godot to start rendering to the headset.
- get_viewport().xr = true
+ get_viewport().use_xr = true
# This will be the reference space type you ultimately got, out of the
# types that you requested above. This is useful if you want the game to
# work a little differently in 'bounded-floor' versus 'local-floor'.
@@ -79,28 +79,35 @@
$Button.visible = true
# If the user exits immersive mode, then we tell Godot to render to the web
# page again.
- get_viewport().xr = false
+ get_viewport().use_xr = false
func _webxr_session_failed(message):
OS.alert("Failed to initialize: " + message)
[/codeblock]
- There are several ways to handle "controller" input:
- - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- - Using [method Node._unhandled_input] and [InputEventJoypadButton] or [InputEventJoypadMotion]. This works the same as normal joypads, except the [member InputEvent.device] starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself.
- You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
+ There are a couple ways to handle "controller" input:
+ - Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in XR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example.
+ - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional input sources like a tap on the screen, a spoken voice command or a button press on the device itself.
+ You can use both methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
</description>
<tutorials>
<link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link>
</tutorials>
<methods>
- <method name="get_controller" qualifiers="const">
+ <method name="get_input_source_target_ray_mode" qualifiers="const">
+ <return type="int" enum="WebXRInterface.TargetRayMode" />
+ <param index="0" name="input_source_id" type="int" />
+ <description>
+ Returns the target ray mode for the given [code]input_source_id[/code].
+ This can help interpret the input coming from that input source. See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource/targetRayMode]XRInputSource.targetRayMode[/url] for more information.
+ </description>
+ </method>
+ <method name="get_input_source_tracker" qualifiers="const">
<return type="XRPositionalTracker" />
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Gets an [XRPositionalTracker] for the given [code]controller_id[/code].
- In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
- Use this method to get information about the controller that triggered one of these signals:
+ Gets an [XRPositionalTracker] for the given [code]input_source_id[/code].
+ In the context of WebXR, an input source can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional input source is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
+ Use this method to get information about the input source that triggered one of these signals:
- [signal selectstart]
- [signal select]
- [signal selectend]
@@ -109,9 +116,16 @@
- [signal squeezestart]
</description>
</method>
+ <method name="is_input_source_active" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="input_source_id" type="int" />
+ <description>
+ Returns [code]true[/code] if there is an active input source with the given [code]input_source_id[/code].
+ </description>
+ </method>
<method name="is_session_supported">
<return type="void" />
- <argument index="0" name="session_mode" type="String" />
+ <param index="0" name="session_mode" type="String" />
<description>
Checks if the given [code]session_mode[/code] is supported by the user's browser.
Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code].
@@ -120,11 +134,6 @@
</method>
</methods>
<members>
- <member name="bounds_geometry" type="PackedVector3Array" setter="" getter="get_bounds_geometry">
- The vertices of a polygon which defines the boundaries of the user's play area.
- This will only be available if [member reference_space_type] is [code]"bounded-floor"[/code] and only on certain browsers and devices that support it.
- The [signal reference_space_reset] signal may indicate when this changes.
- </member>
<member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
A comma-seperated list of optional features used by [method XRInterface.initialize] when setting up the WebXR session.
If a user's browser or device doesn't support one of the given features, initialization will continue, but you won't be able to use the requested feature.
@@ -137,7 +146,7 @@
</member>
<member name="requested_reference_space_types" type="String" setter="set_requested_reference_space_types" getter="get_requested_reference_space_types">
A comma-seperated list of reference space types used by [method XRInterface.initialize] when setting up the WebXR session.
- The reference space types are requested in order, and the first on supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately used.
+ The reference space types are requested in order, and the first one supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately selected.
This doesn't have any effect on the interface when already initialized.
Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
</member>
@@ -161,39 +170,39 @@
<signal name="reference_space_reset">
<description>
Emitted to indicate that the reference space has been reset or reconfigured.
- When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [member bounds_geometry]) or pressed/held a button to recenter their position.
+ When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [method XRInterface.get_play_area]) or pressed/held a button to recenter their position.
See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpace/reset_event]WebXR's XRReferenceSpace reset event[/url] for more information.
</description>
</signal>
<signal name="select">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted after one of the "controllers" has finished its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted after one of the input sources has finished its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="selectend">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has finished its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has finished its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="selectstart">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has started its "primary action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input source has started its "primary action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="session_ended">
<description>
Emitted when the user ends the WebXR session (which can be done using UI from the browser or device).
- At this point, you should do [code]get_viewport().xr = false[/code] to instruct Godot to resume rendering to the screen.
+ At this point, you should do [code]get_viewport().use_xr = false[/code] to instruct Godot to resume rendering to the screen.
</description>
</signal>
<signal name="session_failed">
- <argument index="0" name="message" type="String" />
+ <param index="0" name="message" type="String" />
<description>
Emitted by [method XRInterface.initialize] if the session fails to start.
[code]message[/code] may optionally contain an error message from WebXR, or an empty string if no message is available.
@@ -202,35 +211,35 @@
<signal name="session_started">
<description>
Emitted by [method XRInterface.initialize] if the session is successfully started.
- At this point, it's safe to do [code]get_viewport().xr = true[/code] to instruct Godot to start rendering to the AR/VR device.
+ At this point, it's safe to do [code]get_viewport().use_xr = true[/code] to instruct Godot to start rendering to the XR device.
</description>
</signal>
<signal name="session_supported">
- <argument index="0" name="session_mode" type="String" />
- <argument index="1" name="supported" type="bool" />
+ <param index="0" name="session_mode" type="String" />
+ <param index="1" name="supported" type="bool" />
<description>
Emitted by [method is_session_supported] to indicate if the given [code]session_mode[/code] is supported or not.
</description>
</signal>
<signal name="squeeze">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted after one of the "controllers" has finished its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted after one of the input sources has finished its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="squeezeend">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has finished its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has finished its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="squeezestart">
- <argument index="0" name="controller_id" type="int" />
+ <param index="0" name="input_source_id" type="int" />
<description>
- Emitted when one of the "controllers" has started its "primary squeeze action".
- Use [method get_controller] to get more information about the controller.
+ Emitted when one of the input sources has started its "primary squeeze action".
+ Use [method get_input_source_tracker] and [method get_input_source_target_ray_mode] to get more information about the input source.
</description>
</signal>
<signal name="visibility_state_changed">
@@ -239,4 +248,18 @@
</description>
</signal>
</signals>
+ <constants>
+ <constant name="TARGET_RAY_MODE_UNKNOWN" value="0" enum="TargetRayMode">
+ We don't know the the target ray mode.
+ </constant>
+ <constant name="TARGET_RAY_MODE_GAZE" value="1" enum="TargetRayMode">
+ Target ray originates at the viewer's eyes and points in the direction they are looking.
+ </constant>
+ <constant name="TARGET_RAY_MODE_TRACKED_POINTER" value="2" enum="TargetRayMode">
+ Target ray from a handheld pointer, most likely a VR touch controller.
+ </constant>
+ <constant name="TARGET_RAY_MODE_SCREEN" value="3" enum="TargetRayMode">
+ Target ray from touch screen, mouse or other tactile input device.
+ </constant>
+ </constants>
</class>
diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h
index 52104895d4..c636be1576 100644
--- a/modules/webxr/godot_webxr.h
+++ b/modules/webxr/godot_webxr.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* godot_webxr.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* godot_webxr.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GODOT_WEBXR_H
#define GODOT_WEBXR_H
@@ -37,12 +37,18 @@ extern "C" {
#include "stddef.h"
+enum WebXRInputEvent {
+ WEBXR_INPUT_EVENT_SELECTSTART,
+ WEBXR_INPUT_EVENT_SELECTEND,
+ WEBXR_INPUT_EVENT_SQUEEZESTART,
+ WEBXR_INPUT_EVENT_SQUEEZEEND,
+};
+
typedef void (*GodotWebXRSupportedCallback)(char *p_session_mode, int p_supported);
typedef void (*GodotWebXRStartedCallback)(char *p_reference_space_type);
typedef void (*GodotWebXREndedCallback)();
typedef void (*GodotWebXRFailedCallback)(char *p_message);
-typedef void (*GodotWebXRControllerCallback)();
-typedef void (*GodotWebXRInputEventCallback)(char *p_signal_name, int p_controller_id);
+typedef void (*GodotWebXRInputEventCallback)(int p_event_type, int p_input_source_id);
typedef void (*GodotWebXRSimpleEventCallback)(char *p_signal_name);
extern int godot_webxr_is_supported();
@@ -56,27 +62,33 @@ extern void godot_webxr_initialize(
GodotWebXRStartedCallback p_on_session_started,
GodotWebXREndedCallback p_on_session_ended,
GodotWebXRFailedCallback p_on_session_failed,
- GodotWebXRControllerCallback p_on_controller_changed,
GodotWebXRInputEventCallback p_on_input_event,
GodotWebXRSimpleEventCallback p_on_simple_event);
extern void godot_webxr_uninitialize();
extern int godot_webxr_get_view_count();
-extern int *godot_webxr_get_render_target_size();
-extern float *godot_webxr_get_transform_for_eye(int p_eye);
-extern float *godot_webxr_get_projection_for_eye(int p_eye);
-extern int godot_webxr_get_external_texture_for_eye(int p_eye);
-extern void godot_webxr_commit_for_eye(int p_eye);
+extern bool godot_webxr_get_render_target_size(int *r_size);
+extern bool godot_webxr_get_transform_for_view(int p_view, float *r_transform);
+extern bool godot_webxr_get_projection_for_view(int p_view, float *r_transform);
+extern unsigned int godot_webxr_get_color_texture();
+extern unsigned int godot_webxr_get_depth_texture();
+extern unsigned int godot_webxr_get_velocity_texture();
-extern void godot_webxr_sample_controller_data();
-extern int godot_webxr_get_controller_count();
-extern int godot_webxr_is_controller_connected(int p_controller);
-extern float *godot_webxr_get_controller_transform(int p_controller);
-extern int *godot_webxr_get_controller_buttons(int p_controller);
-extern int *godot_webxr_get_controller_axes(int p_controller);
+extern bool godot_webxr_update_input_source(
+ int p_input_source_id,
+ float *r_target_pose,
+ int *r_target_ray_mode,
+ int *r_touch_index,
+ int *r_has_grip_pose,
+ float *r_grip_pose,
+ int *r_has_standard_mapping,
+ int *r_button_count,
+ float *r_buttons,
+ int *r_axes_count,
+ float *r_axes);
extern char *godot_webxr_get_visibility_state();
-extern int *godot_webxr_get_bounds_geometry();
+extern int godot_webxr_get_bounds_geometry(float **r_points);
#ifdef __cplusplus
}
diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js
index c4b21defce..1c00ebebb4 100644
--- a/modules/webxr/native/library_godot_webxr.js
+++ b/modules/webxr/native/library_godot_webxr.js
@@ -1,44 +1,47 @@
-/*************************************************************************/
-/* library_godot_webxr.js */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* library_godot_webxr.js */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
const GodotWebXR = {
- $GodotWebXR__deps: ['$Browser', '$GL', '$GodotRuntime'],
+ $GodotWebXR__deps: ['$Browser', '$GL', '$GodotRuntime', '$runtimeKeepalivePush', '$runtimeKeepalivePop'],
$GodotWebXR: {
gl: null,
- texture_ids: [null, null],
- textures: [null, null],
-
session: null,
+ gl_binding: null,
+ layer: null,
space: null,
frame: null,
pose: null,
+ view_count: 1,
+ input_sources: new Array(16),
+ touches: new Array(5),
// Monkey-patch the requestAnimationFrame() used by Emscripten for the main
// loop, so that we can swap it out for XRSession.requestAnimationFrame()
@@ -72,143 +75,135 @@ const GodotWebXR = {
// gets picked up automatically, however, in the Oculus Browser
// on the Quest, we need to pause and resume the main loop.
Browser.mainLoop.pause();
+ runtimeKeepalivePush(); // eslint-disable-line no-undef
window.setTimeout(function () {
+ runtimeKeepalivePop(); // eslint-disable-line no-undef
Browser.mainLoop.resume();
}, 0);
},
- // Some custom WebGL code for blitting our eye textures to the
- // framebuffer we get from WebXR.
- shaderProgram: null,
- programInfo: null,
- buffer: null,
- // Vertex shader source.
- vsSource: `
- const vec2 scale = vec2(0.5, 0.5);
- attribute vec4 aVertexPosition;
-
- varying highp vec2 vTextureCoord;
-
- void main () {
- gl_Position = aVertexPosition;
- vTextureCoord = aVertexPosition.xy * scale + scale;
- }
- `,
- // Fragment shader source.
- fsSource: `
- varying highp vec2 vTextureCoord;
+ getLayer: () => {
+ const new_view_count = (GodotWebXR.pose) ? GodotWebXR.pose.views.length : 1;
+ let layer = GodotWebXR.layer;
- uniform sampler2D uSampler;
+ // If the view count hasn't changed since creating this layer, then
+ // we can simply return it.
+ if (layer && GodotWebXR.view_count === new_view_count) {
+ return layer;
+ }
- void main() {
- gl_FragColor = texture2D(uSampler, vTextureCoord);
+ if (!GodotWebXR.session || !GodotWebXR.gl_binding) {
+ return null;
}
- `,
- initShaderProgram: (gl, vsSource, fsSource) => {
- const vertexShader = GodotWebXR.loadShader(gl, gl.VERTEX_SHADER, vsSource);
- const fragmentShader = GodotWebXR.loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
+ const gl = GodotWebXR.gl;
- const shaderProgram = gl.createProgram();
- gl.attachShader(shaderProgram, vertexShader);
- gl.attachShader(shaderProgram, fragmentShader);
- gl.linkProgram(shaderProgram);
+ layer = GodotWebXR.gl_binding.createProjectionLayer({
+ textureType: new_view_count > 1 ? 'texture-array' : 'texture',
+ colorFormat: gl.RGBA8,
+ depthFormat: gl.DEPTH_COMPONENT24,
+ });
+ GodotWebXR.session.updateRenderState({ layers: [layer] });
- if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
- GodotRuntime.error(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`);
+ GodotWebXR.layer = layer;
+ GodotWebXR.view_count = new_view_count;
+ return layer;
+ },
+
+ getSubImage: () => {
+ if (!GodotWebXR.pose) {
return null;
}
-
- return shaderProgram;
- },
- loadShader: (gl, type, source) => {
- const shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
-
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- GodotRuntime.error(`An error occurred compiling the shader: ${gl.getShaderInfoLog(shader)}`);
- gl.deleteShader(shader);
+ const layer = GodotWebXR.getLayer();
+ if (layer === null) {
return null;
}
- return shader;
+ // Because we always use "texture-array" for multiview and "texture"
+ // when there is only 1 view, it should be safe to only grab the
+ // subimage for the first view.
+ return GodotWebXR.gl_binding.getViewSubImage(layer, GodotWebXR.pose.views[0]);
},
- initBuffer: (gl) => {
- const positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- const positions = [
- -1.0, -1.0,
- 1.0, -1.0,
- -1.0, 1.0,
- 1.0, 1.0,
- ];
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
- return positionBuffer;
- },
- blitTexture: (gl, texture) => {
- if (GodotWebXR.shaderProgram === null) {
- GodotWebXR.shaderProgram = GodotWebXR.initShaderProgram(gl, GodotWebXR.vsSource, GodotWebXR.fsSource);
- GodotWebXR.programInfo = {
- program: GodotWebXR.shaderProgram,
- attribLocations: {
- vertexPosition: gl.getAttribLocation(GodotWebXR.shaderProgram, 'aVertexPosition'),
- },
- uniformLocations: {
- uSampler: gl.getUniformLocation(GodotWebXR.shaderProgram, 'uSampler'),
- },
- };
- GodotWebXR.buffer = GodotWebXR.initBuffer(gl);
- }
- const orig_program = gl.getParameter(gl.CURRENT_PROGRAM);
- gl.useProgram(GodotWebXR.shaderProgram);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, GodotWebXR.buffer);
- gl.vertexAttribPointer(GodotWebXR.programInfo.attribLocations.vertexPosition, 2, gl.FLOAT, false, 0, 0);
- gl.enableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition);
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.uniform1i(GodotWebXR.programInfo.uniformLocations.uSampler, 0);
+ getTextureId: (texture) => {
+ if (texture.name !== undefined) {
+ return texture.name;
+ }
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ const id = GL.getNewId(GL.textures);
+ texture.name = id;
+ GL.textures[id] = texture;
- // Restore state.
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.disableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition);
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
- gl.useProgram(orig_program);
+ return id;
},
- // Holds the controllers list between function calls.
- controllers: [],
-
- // Updates controllers array, where the left hand (or sole tracker) is
- // the first element, and the right hand is the second element, and any
- // others placed at the 3rd position and up.
- sampleControllers: () => {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
- return;
+ addInputSource: (input_source) => {
+ let name = -1;
+ if (input_source.targetRayMode === 'tracked-pointer' && input_source.handedness === 'left') {
+ name = 0;
+ } else if (input_source.targetRayMode === 'tracked-pointer' && input_source.handedness === 'right') {
+ name = 1;
+ } else {
+ for (let i = 2; i < 16; i++) {
+ if (!GodotWebXR.input_sources[i]) {
+ name = i;
+ break;
+ }
+ }
+ }
+ if (name >= 0) {
+ GodotWebXR.input_sources[name] = input_source;
+ input_source.name = name;
+
+ // Find a free touch index for screen sources.
+ if (input_source.targetRayMode === 'screen') {
+ let touch_index = -1;
+ for (let i = 0; i < 5; i++) {
+ if (!GodotWebXR.touches[i]) {
+ touch_index = i;
+ break;
+ }
+ }
+ if (touch_index >= 0) {
+ GodotWebXR.touches[touch_index] = input_source;
+ input_source.touch_index = touch_index;
+ }
+ }
}
+ return name;
+ },
+
+ removeInputSource: (input_source) => {
+ if (input_source.name !== undefined) {
+ const name = input_source.name;
+ if (name >= 0 && name < 16) {
+ GodotWebXR.input_sources[name] = null;
+ }
- let other_index = 2;
- const controllers = [];
- GodotWebXR.session.inputSources.forEach((input_source) => {
- if (input_source.targetRayMode === 'tracked-pointer') {
- if (input_source.handedness === 'right') {
- controllers[1] = input_source;
- } else if (input_source.handedness === 'left' || !controllers[0]) {
- controllers[0] = input_source;
+ if (input_source.touch_index !== undefined) {
+ const touch_index = input_source.touch_index;
+ if (touch_index >= 0 && touch_index < 5) {
+ GodotWebXR.touches[touch_index] = null;
}
- } else {
- controllers[other_index++] = input_source;
}
- });
- GodotWebXR.controllers = controllers;
+ return name;
+ }
+ return -1;
},
- getControllerId: (input_source) => GodotWebXR.controllers.indexOf(input_source),
+ getInputSourceId: (input_source) => {
+ if (input_source !== undefined) {
+ return input_source.name;
+ }
+ return -1;
+ },
+
+ getTouchIndex: (input_source) => {
+ if (input_source.touch_index !== undefined) {
+ return input_source.touch_index;
+ }
+ return -1;
+ },
},
godot_webxr_is_supported__proxy: 'sync',
@@ -237,8 +232,8 @@ const GodotWebXR = {
godot_webxr_initialize__deps: ['emscripten_webgl_get_current_context'],
godot_webxr_initialize__proxy: 'sync',
- godot_webxr_initialize__sig: 'viiiiiiiiii',
- godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_controller_changed, p_on_input_event, p_on_simple_event) {
+ godot_webxr_initialize__sig: 'viiiiiiiii',
+ godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_input_event, p_on_simple_event) {
GodotWebXR.monkeyPatchRequestAnimationFrame(true);
const session_mode = GodotRuntime.parseString(p_session_mode);
@@ -248,7 +243,6 @@ const GodotWebXR = {
const onstarted = GodotRuntime.get_func(p_on_session_started);
const onended = GodotRuntime.get_func(p_on_session_ended);
const onfailed = GodotRuntime.get_func(p_on_session_failed);
- const oncontroller = GodotRuntime.get_func(p_on_controller_changed);
const oninputevent = GodotRuntime.get_func(p_on_input_event);
const onsimpleevent = GodotRuntime.get_func(p_on_simple_event);
@@ -268,24 +262,18 @@ const GodotWebXR = {
});
session.addEventListener('inputsourceschange', function (evt) {
- let controller_changed = false;
- [evt.added, evt.removed].forEach((lst) => {
- lst.forEach((input_source) => {
- if (input_source.targetRayMode === 'tracked-pointer') {
- controller_changed = true;
- }
- });
- });
- if (controller_changed) {
- oncontroller();
- }
+ evt.added.forEach(GodotWebXR.addInputSource);
+ evt.removed.forEach(GodotWebXR.removeInputSource);
});
- ['selectstart', 'select', 'selectend', 'squeezestart', 'squeeze', 'squeezeend'].forEach((input_event) => {
+ ['selectstart', 'selectend', 'squeezestart', 'squeezeend'].forEach((input_event, index) => {
session.addEventListener(input_event, function (evt) {
- const c_str = GodotRuntime.allocString(input_event);
- oninputevent(c_str, GodotWebXR.getControllerId(evt.inputSource));
- GodotRuntime.free(c_str);
+ // Since this happens in-between normal frames, we need to
+ // grab the frame from the event in order to get poses for
+ // the input sources.
+ GodotWebXR.frame = evt.frame;
+ oninputevent(index, GodotWebXR.getInputSourceId(evt.inputSource));
+ GodotWebXR.frame = null;
});
});
@@ -300,9 +288,10 @@ const GodotWebXR = {
GodotWebXR.gl = gl;
gl.makeXRCompatible().then(function () {
- session.updateRenderState({
- baseLayer: new XRWebGLLayer(session, gl),
- });
+ GodotWebXR.gl_binding = new XRWebGLBinding(session, gl); // eslint-disable-line no-undef
+
+ // This will trigger the layer to get created.
+ GodotWebXR.getLayer();
function onReferenceSpaceSuccess(reference_space, reference_space_type) {
GodotWebXR.space = reference_space;
@@ -370,26 +359,15 @@ const GodotWebXR = {
.catch((e) => { });
}
- // Clean-up the textures we allocated for each view.
- const gl = GodotWebXR.gl;
- for (let i = 0; i < GodotWebXR.textures.length; i++) {
- const texture = GodotWebXR.textures[i];
- if (texture !== null) {
- gl.deleteTexture(texture);
- }
- GodotWebXR.textures[i] = null;
-
- const texture_id = GodotWebXR.texture_ids[i];
- if (texture_id !== null) {
- GL.textures[texture_id] = null;
- }
- GodotWebXR.texture_ids[i] = null;
- }
-
GodotWebXR.session = null;
+ GodotWebXR.gl_binding = null;
+ GodotWebXR.layer = null;
GodotWebXR.space = null;
GodotWebXR.frame = null;
GodotWebXR.pose = null;
+ GodotWebXR.view_count = 1;
+ GodotWebXR.input_sources = new Array(16);
+ GodotWebXR.touches = new Array(5);
// Disable the monkey-patched window.requestAnimationFrame() and
// pause/restart the main loop to activate it on all platforms.
@@ -401,234 +379,186 @@ const GodotWebXR = {
godot_webxr_get_view_count__sig: 'i',
godot_webxr_get_view_count: function () {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ return 1;
}
- return GodotWebXR.pose.views.length;
+ const view_count = GodotWebXR.pose.views.length;
+ return view_count > 0 ? view_count : 1;
},
godot_webxr_get_render_target_size__proxy: 'sync',
- godot_webxr_get_render_target_size__sig: 'i',
- godot_webxr_get_render_target_size: function () {
- if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ godot_webxr_get_render_target_size__sig: 'ii',
+ godot_webxr_get_render_target_size: function (r_size) {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
+ return false;
}
- const glLayer = GodotWebXR.session.renderState.baseLayer;
- const view = GodotWebXR.pose.views[0];
- const viewport = glLayer.getViewport(view);
+ GodotRuntime.setHeapValue(r_size + 0, subimage.viewport.width, 'i32');
+ GodotRuntime.setHeapValue(r_size + 4, subimage.viewport.height, 'i32');
- const buf = GodotRuntime.malloc(2 * 4);
- GodotRuntime.setHeapValue(buf + 0, viewport.width, 'i32');
- GodotRuntime.setHeapValue(buf + 4, viewport.height, 'i32');
- return buf;
+ return true;
},
- godot_webxr_get_transform_for_eye__proxy: 'sync',
- godot_webxr_get_transform_for_eye__sig: 'ii',
- godot_webxr_get_transform_for_eye: function (p_eye) {
+ godot_webxr_get_transform_for_view__proxy: 'sync',
+ godot_webxr_get_transform_for_view__sig: 'iii',
+ godot_webxr_get_transform_for_view: function (p_view, r_transform) {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ return false;
}
const views = GodotWebXR.pose.views;
let matrix;
- if (p_eye === 0) {
- matrix = GodotWebXR.pose.transform.matrix;
+ if (p_view >= 0) {
+ matrix = views[p_view].transform.matrix;
} else {
- matrix = views[p_eye - 1].transform.matrix;
+ // For -1 (or any other negative value) return the HMD transform.
+ matrix = GodotWebXR.pose.transform.matrix;
}
- const buf = GodotRuntime.malloc(16 * 4);
+
for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ GodotRuntime.setHeapValue(r_transform + (i * 4), matrix[i], 'float');
}
- return buf;
+
+ return true;
},
- godot_webxr_get_projection_for_eye__proxy: 'sync',
- godot_webxr_get_projection_for_eye__sig: 'ii',
- godot_webxr_get_projection_for_eye: function (p_eye) {
+ godot_webxr_get_projection_for_view__proxy: 'sync',
+ godot_webxr_get_projection_for_view__sig: 'iii',
+ godot_webxr_get_projection_for_view: function (p_view, r_transform) {
if (!GodotWebXR.session || !GodotWebXR.pose) {
- return 0;
+ return false;
}
- const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
- const matrix = GodotWebXR.pose.views[view_index].projectionMatrix;
- const buf = GodotRuntime.malloc(16 * 4);
+ const matrix = GodotWebXR.pose.views[p_view].projectionMatrix;
for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ GodotRuntime.setHeapValue(r_transform + (i * 4), matrix[i], 'float');
}
- return buf;
+
+ return true;
},
- godot_webxr_get_external_texture_for_eye__proxy: 'sync',
- godot_webxr_get_external_texture_for_eye__sig: 'ii',
- godot_webxr_get_external_texture_for_eye: function (p_eye) {
- if (!GodotWebXR.session) {
+ godot_webxr_get_color_texture__proxy: 'sync',
+ godot_webxr_get_color_texture__sig: 'i',
+ godot_webxr_get_color_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
return 0;
}
+ return GodotWebXR.getTextureId(subimage.colorTexture);
+ },
- const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
- if (GodotWebXR.texture_ids[view_index]) {
- return GodotWebXR.texture_ids[view_index];
- }
-
- // Check pose separately and after returning the cached texture id,
- // because we won't get a pose in some cases if we lose tracking, and
- // we don't want to return 0 just because tracking was lost.
- if (!GodotWebXR.pose) {
+ godot_webxr_get_depth_texture__proxy: 'sync',
+ godot_webxr_get_depth_texture__sig: 'i',
+ godot_webxr_get_depth_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
return 0;
}
-
- const glLayer = GodotWebXR.session.renderState.baseLayer;
- const view = GodotWebXR.pose.views[view_index];
- const viewport = glLayer.getViewport(view);
- const gl = GodotWebXR.gl;
-
- const texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.width, viewport.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
-
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- gl.bindTexture(gl.TEXTURE_2D, null);
-
- const texture_id = GL.getNewId(GL.textures);
- GL.textures[texture_id] = texture;
- GodotWebXR.textures[view_index] = texture;
- GodotWebXR.texture_ids[view_index] = texture_id;
- return texture_id;
- },
-
- godot_webxr_commit_for_eye__proxy: 'sync',
- godot_webxr_commit_for_eye__sig: 'vi',
- godot_webxr_commit_for_eye: function (p_eye) {
- if (!GodotWebXR.session || !GodotWebXR.pose) {
- return;
+ if (!subimage.depthStencilTexture) {
+ return 0;
}
-
- const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
- const glLayer = GodotWebXR.session.renderState.baseLayer;
- const view = GodotWebXR.pose.views[view_index];
- const viewport = glLayer.getViewport(view);
- const gl = GodotWebXR.gl;
-
- const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
- const orig_viewport = gl.getParameter(gl.VIEWPORT);
-
- // Bind to WebXR's framebuffer.
- gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
- gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
-
- GodotWebXR.blitTexture(gl, GodotWebXR.textures[view_index]);
-
- // Restore state.
- gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer);
- gl.viewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
+ return GodotWebXR.getTextureId(subimage.depthStencilTexture);
},
- godot_webxr_sample_controller_data__proxy: 'sync',
- godot_webxr_sample_controller_data__sig: 'v',
- godot_webxr_sample_controller_data: function () {
- GodotWebXR.sampleControllers();
- },
-
- godot_webxr_get_controller_count__proxy: 'sync',
- godot_webxr_get_controller_count__sig: 'i',
- godot_webxr_get_controller_count: function () {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
+ godot_webxr_get_velocity_texture__proxy: 'sync',
+ godot_webxr_get_velocity_texture__sig: 'i',
+ godot_webxr_get_velocity_texture: function () {
+ const subimage = GodotWebXR.getSubImage();
+ if (subimage === null) {
return 0;
}
- return GodotWebXR.controllers.length;
- },
-
- godot_webxr_is_controller_connected__proxy: 'sync',
- godot_webxr_is_controller_connected__sig: 'ii',
- godot_webxr_is_controller_connected: function (p_controller) {
- if (!GodotWebXR.session || !GodotWebXR.frame) {
- return false;
+ if (!subimage.motionVectorTexture) {
+ return 0;
}
- return !!GodotWebXR.controllers[p_controller];
+ return GodotWebXR.getTextureId(subimage.motionVectorTexture);
},
- godot_webxr_get_controller_transform__proxy: 'sync',
- godot_webxr_get_controller_transform__sig: 'ii',
- godot_webxr_get_controller_transform: function (p_controller) {
+ godot_webxr_update_input_source__proxy: 'sync',
+ godot_webxr_update_input_source__sig: 'iiiiiiiiiiii',
+ godot_webxr_update_input_source: function (p_input_source_id, r_target_pose, r_target_ray_mode, r_touch_index, r_has_grip_pose, r_grip_pose, r_has_standard_mapping, r_button_count, r_buttons, r_axes_count, r_axes) {
if (!GodotWebXR.session || !GodotWebXR.frame) {
return 0;
}
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller) {
- return 0;
+ if (p_input_source_id < 0 || p_input_source_id >= GodotWebXR.input_sources.length || !GodotWebXR.input_sources[p_input_source_id]) {
+ return false;
}
+ const input_source = GodotWebXR.input_sources[p_input_source_id];
const frame = GodotWebXR.frame;
const space = GodotWebXR.space;
- const pose = frame.getPose(controller.targetRaySpace, space);
- if (!pose) {
+ // Target pose.
+ const target_pose = frame.getPose(input_source.targetRaySpace, space);
+ if (!target_pose) {
// This can mean that the controller lost tracking.
- return 0;
+ return false;
}
- const matrix = pose.transform.matrix;
-
- const buf = GodotRuntime.malloc(16 * 4);
+ const target_pose_matrix = target_pose.transform.matrix;
for (let i = 0; i < 16; i++) {
- GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ GodotRuntime.setHeapValue(r_target_pose + (i * 4), target_pose_matrix[i], 'float');
}
- return buf;
- },
- godot_webxr_get_controller_buttons__proxy: 'sync',
- godot_webxr_get_controller_buttons__sig: 'ii',
- godot_webxr_get_controller_buttons: function (p_controller) {
- if (GodotWebXR.controllers.length === 0) {
- return 0;
- }
+ // Target ray mode.
+ let target_ray_mode = 0;
+ switch (input_source.targetRayMode) {
+ case 'gaze':
+ target_ray_mode = 1;
+ break;
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller || !controller.gamepad) {
- return 0;
- }
+ case 'tracked-pointer':
+ target_ray_mode = 2;
+ break;
- const button_count = controller.gamepad.buttons.length;
+ case 'screen':
+ target_ray_mode = 3;
+ break;
- const buf = GodotRuntime.malloc((button_count + 1) * 4);
- GodotRuntime.setHeapValue(buf, button_count, 'i32');
- for (let i = 0; i < button_count; i++) {
- GodotRuntime.setHeapValue(buf + 4 + (i * 4), controller.gamepad.buttons[i].value, 'float');
+ default:
}
- return buf;
- },
+ GodotRuntime.setHeapValue(r_target_ray_mode, target_ray_mode, 'i32');
- godot_webxr_get_controller_axes__proxy: 'sync',
- godot_webxr_get_controller_axes__sig: 'ii',
- godot_webxr_get_controller_axes: function (p_controller) {
- if (GodotWebXR.controllers.length === 0) {
- return 0;
- }
+ // Touch index.
+ GodotRuntime.setHeapValue(r_touch_index, GodotWebXR.getTouchIndex(input_source), 'i32');
- const controller = GodotWebXR.controllers[p_controller];
- if (!controller || !controller.gamepad) {
- return 0;
+ // Grip pose.
+ let has_grip_pose = false;
+ if (input_source.gripSpace) {
+ const grip_pose = frame.getPose(input_source.gripSpace, space);
+ if (grip_pose) {
+ const grip_pose_matrix = grip_pose.transform.matrix;
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(r_grip_pose + (i * 4), grip_pose_matrix[i], 'float');
+ }
+ has_grip_pose = true;
+ }
}
+ GodotRuntime.setHeapValue(r_has_grip_pose, has_grip_pose ? 1 : 0, 'i32');
+
+ // Gamepad data (mapping, buttons and axes).
+ let has_standard_mapping = false;
+ let button_count = 0;
+ let axes_count = 0;
+ if (input_source.gamepad) {
+ if (input_source.gamepad.mapping === 'xr-standard') {
+ has_standard_mapping = true;
+ }
- const axes_count = controller.gamepad.axes.length;
+ button_count = Math.min(input_source.gamepad.buttons.length, 10);
+ for (let i = 0; i < button_count; i++) {
+ GodotRuntime.setHeapValue(r_buttons + (i * 4), input_source.gamepad.buttons[i].value, 'float');
+ }
- const buf = GodotRuntime.malloc((axes_count + 1) * 4);
- GodotRuntime.setHeapValue(buf, axes_count, 'i32');
- for (let i = 0; i < axes_count; i++) {
- let value = controller.gamepad.axes[i];
- if (i === 1 || i === 3) {
- // Invert the Y-axis on thumbsticks and trackpads, in order to
- // match OpenXR and other XR platform SDKs.
- value *= -1.0;
+ axes_count = Math.min(input_source.gamepad.axes.length, 10);
+ for (let i = 0; i < axes_count; i++) {
+ GodotRuntime.setHeapValue(r_axes + (i * 4), input_source.gamepad.axes[i], 'float');
}
- GodotRuntime.setHeapValue(buf + 4 + (i * 4), value, 'float');
}
- return buf;
+ GodotRuntime.setHeapValue(r_has_standard_mapping, has_standard_mapping ? 1 : 0, 'i32');
+ GodotRuntime.setHeapValue(r_button_count, button_count, 'i32');
+ GodotRuntime.setHeapValue(r_axes_count, axes_count, 'i32');
+
+ return true;
},
godot_webxr_get_visibility_state__proxy: 'sync',
@@ -642,8 +572,8 @@ const GodotWebXR = {
},
godot_webxr_get_bounds_geometry__proxy: 'sync',
- godot_webxr_get_bounds_geometry__sig: 'i',
- godot_webxr_get_bounds_geometry: function () {
+ godot_webxr_get_bounds_geometry__sig: 'ii',
+ godot_webxr_get_bounds_geometry: function (r_points) {
if (!GodotWebXR.space || !GodotWebXR.space.boundsGeometry) {
return 0;
}
@@ -653,7 +583,7 @@ const GodotWebXR = {
return 0;
}
- const buf = GodotRuntime.malloc(((point_count * 3) + 1) * 4);
+ const buf = GodotRuntime.malloc(point_count * 3 * 4);
GodotRuntime.setHeapValue(buf, point_count, 'i32');
for (let i = 0; i < point_count; i++) {
const point = GodotWebXR.space.boundsGeometry[i];
@@ -661,8 +591,9 @@ const GodotWebXR = {
GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.y, 'float');
GodotRuntime.setHeapValue(buf + ((i * 3) + 3) * 4, point.z, 'float');
}
+ GodotRuntime.setHeapValue(r_points, buf, 'i32');
- return buf;
+ return point_count;
},
};
diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js
index 9ea105aa93..4b88820b19 100644
--- a/modules/webxr/native/webxr.externs.js
+++ b/modules/webxr/native/webxr.externs.js
@@ -1,3 +1,7 @@
+/*
+ * WebXR Device API
+ */
+
/**
* @type {XR}
*/
@@ -497,3 +501,681 @@ XRPose.prototype.transform;
* @type {boolean}
*/
XRPose.prototype.emulatedPosition;
+
+/*
+ * WebXR Layers API Level 1
+ */
+
+/**
+ * @constructor XRLayer
+ */
+function XRLayer() {}
+
+/**
+ * @constructor XRLayerEventInit
+ */
+function XRLayerEventInit() {}
+
+/**
+ * @type {XRLayer}
+ */
+XRLayerEventInit.prototype.layer;
+
+/**
+ * @constructor XRLayerEvent
+ *
+ * @param {string} type
+ * @param {XRLayerEventInit} init
+ */
+function XRLayerEvent(type, init) {};
+
+/**
+ * @type {XRLayer}
+ */
+XRLayerEvent.prototype.layer;
+
+/**
+ * @constructor XRCompositionLayer
+ * @extends {XRLayer}
+ */
+function XRCompositionLayer() {};
+
+/**
+ * @type {string}
+ */
+XRCompositionLayer.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.blendTextureAberrationCorrection;
+
+/**
+ * @type {?boolean}
+ */
+XRCompositionLayer.prototype.chromaticAberrationCorrection;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.forceMonoPresentation;
+
+/**
+ * @type {number}
+ */
+XRCompositionLayer.prototype.opacity;
+
+/**
+ * @type {number}
+ */
+XRCompositionLayer.prototype.mipLevels;
+
+/**
+ * @type {boolean}
+ */
+XRCompositionLayer.prototype.needsRedraw;
+
+/**
+ * @return {void}
+ */
+XRCompositionLayer.prototype.destroy = function () {};
+
+/**
+ * @constructor XRProjectionLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRProjectionLayer() {}
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureWidth;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureHeight;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayer.prototype.textureArrayLength;
+
+/**
+ * @type {boolean}
+ */
+XRProjectionLayer.prototype.ignoreDepthValues;
+
+/**
+ * @type {?number}
+ */
+XRProjectionLayer.prototype.fixedFoveation;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRProjectionLayer.prototype.deltaPose;
+
+/**
+ * @constructor XRQuadLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRQuadLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRQuadLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRQuadLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRQuadLayer.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRQuadLayer.prototype.height;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRQuadLayer.prototype.onredraw;
+
+/**
+ * @constructor XRCylinderLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRCylinderLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRCylinderLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRCylinderLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.centralAngle;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayer.prototype.aspectRatio;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRCylinderLayer.prototype.onredraw;
+
+/**
+ * @constructor XREquirectLayer
+ * @extends {XRCompositionLayer}
+ */
+function XREquirectLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XREquirectLayer.prototype.space;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XREquirectLayer.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayer.prototype.lowerVerticalAngle;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XREquirectLayer.prototype.onredraw;
+
+/**
+ * @constructor XRCubeLayer
+ * @extends {XRCompositionLayer}
+ */
+function XRCubeLayer() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRCubeLayer.prototype.space;
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRCubeLayer.prototype.orientation;
+
+/**
+ * @type {?function (XRLayerEvent)}
+ */
+XRCubeLayer.prototype.onredraw;
+
+/**
+ * @constructor XRSubImage
+ */
+function XRSubImage() {}
+
+/**
+ * @type {XRViewport}
+ */
+XRSubImage.prototype.viewport;
+
+/**
+ * @constructor XRWebGLSubImage
+ * @extends {XRSubImage}
+ */
+function XRWebGLSubImage () {}
+
+/**
+ * @type {WebGLTexture}
+ */
+XRWebGLSubImage.prototype.colorTexture;
+
+/**
+ * @type {?WebGLTexture}
+ */
+XRWebGLSubImage.prototype.depthStencilTexture;
+
+/**
+ * @type {?WebGLTexture}
+ */
+XRWebGLSubImage.prototype.motionVectorTexture;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.imageIndex;
+
+/**
+ * @type {number}
+ */
+XRWebGLSubImage.prototype.colorTextureWidth;
+
+/**
+ * @type {number}
+ */
+XRWebGLSubImage.prototype.colorTextureHeight;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.depthStencilTextureWidth;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.depthStencilTextureHeight;
+
+/**
+ * @type {?number}
+ */
+
+XRWebGLSubImage.prototype.motionVectorTextureWidth;
+
+/**
+ * @type {?number}
+ */
+XRWebGLSubImage.prototype.motionVectorTextureHeight;
+
+/**
+ * @constructor XRProjectionLayerInit
+ */
+function XRProjectionLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRProjectionLayerInit.prototype.textureType;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.colorFormat;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.depthFormat;
+
+/**
+ * @type {number}
+ */
+XRProjectionLayerInit.prototype.scaleFactor;
+
+/**
+ * @constructor XRLayerInit
+ */
+function XRLayerInit() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRLayerInit.prototype.space;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.colorFormat;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.depthFormat;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.mipLevels;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.viewPixelWidth;
+
+/**
+ * @type {number}
+ */
+XRLayerInit.prototype.viewPixelHeight;
+
+/**
+ * @type {string}
+ */
+XRLayerInit.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRLayerInit.prototype.isStatic;
+
+/**
+ * @constructor XRQuadLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRQuadLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRQuadLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XRQuadLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRQuadLayerInit.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRQuadLayerInit.prototype.height;
+
+/**
+ * @constructor XRCylinderLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRCylinderLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XRCylinderLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XRCylinderLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.centralAngle;
+
+/**
+ * @type {number}
+ */
+XRCylinderLayerInit.prototype.aspectRatio;
+
+/**
+ * @constructor XREquirectLayerInit
+ * @extends {XRLayerInit}
+ */
+function XREquirectLayerInit() {}
+
+/**
+ * @type {string}
+ */
+XREquirectLayerInit.prototype.textureType;
+
+/**
+ * @type {?XRRigidTransform}
+ */
+XREquirectLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XREquirectLayerInit.prototype.lowerVerticalAngle;
+
+/**
+ * @constructor XRCubeLayerInit
+ * @extends {XRLayerInit}
+ */
+function XRCubeLayerInit() {}
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRCubeLayerInit.prototype.orientation;
+
+/**
+ * @constructor XRWebGLBinding
+ *
+ * @param {XRSession} session
+ * @param {WebGLRenderContext|WebGL2RenderingContext} context
+ */
+function XRWebGLBinding(session, context) {}
+
+/**
+ * @type {number}
+ */
+XRWebGLBinding.prototype.nativeProjectionScaleFactor;
+
+/**
+ * @type {number}
+ */
+XRWebGLBinding.prototype.usesDepthValues;
+
+/**
+ * @param {XRProjectionLayerInit} init
+ * @return {XRProjectionLayer}
+ */
+XRWebGLBinding.prototype.createProjectionLayer = function (init) {};
+
+/**
+ * @param {XRQuadLayerInit} init
+ * @return {XRQuadLayer}
+ */
+XRWebGLBinding.prototype.createQuadLayer = function (init) {};
+
+/**
+ * @param {XRCylinderLayerInit} init
+ * @return {XRCylinderLayer}
+ */
+XRWebGLBinding.prototype.createCylinderLayer = function (init) {};
+
+/**
+ * @param {XREquirectLayerInit} init
+ * @return {XREquirectLayer}
+ */
+XRWebGLBinding.prototype.createEquirectLayer = function (init) {};
+
+/**
+ * @param {XRCubeLayerInit} init
+ * @return {XRCubeLayer}
+ */
+XRWebGLBinding.prototype.createCubeLayer = function (init) {};
+
+/**
+ * @param {XRCompositionLayer} layer
+ * @param {XRFrame} frame
+ * @param {string} eye
+ * @return {XRWebGLSubImage}
+ */
+XRWebGLBinding.prototype.getSubImage = function (layer, frame, eye) {};
+
+/**
+ * @param {XRProjectionLayer} layer
+ * @param {XRView} view
+ * @return {XRWebGLSubImage}
+ */
+XRWebGLBinding.prototype.getViewSubImage = function (layer, view) {};
+
+/**
+ * @constructor XRMediaLayerInit
+ */
+function XRMediaLayerInit() {}
+
+/**
+ * @type {XRSpace}
+ */
+XRMediaLayerInit.prototype.space;
+
+/**
+ * @type {string}
+ */
+XRMediaLayerInit.prototype.layout;
+
+/**
+ * @type {boolean}
+ */
+XRMediaLayerInit.prototype.invertStereo;
+
+/**
+ * @constructor XRMediaQuadLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaQuadLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaQuadLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaQuadLayerInit.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRMediaQuadLayerInit.prototype.height;
+
+/**
+ * @constructor XRMediaCylinderLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaCylinderLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaCylinderLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaCylinderLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRMediaCylinderLayerInit.prototype.centralAngle;
+
+/**
+ * @type {?number}
+ */
+XRMediaCylinderLayerInit.prototype.aspectRatio;
+
+/**
+ * @constructor XRMediaEquirectLayerInit
+ * @extends {XRMediaLayerInit}
+ */
+function XRMediaEquirectLayerInit() {}
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRMediaEquirectLayerInit.prototype.transform;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.radius;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.centralHorizontalAngle;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.upperVerticalAngle;
+
+/**
+ * @type {number}
+ */
+XRMediaEquirectLayerInit.prototype.lowerVerticalAngle;
+
+/**
+ * @constructor XRMediaBinding
+ *
+ * @param {XRSession} session
+ */
+function XRMediaBinding(session) {}
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaQuadLayerInit} init
+ * @return {XRQuadLayer}
+ */
+XRMediaBinding.prototype.createQuadLayer = function(video, init) {};
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaCylinderLayerInit} init
+ * @return {XRCylinderLayer}
+ */
+XRMediaBinding.prototype.createCylinderLayer = function(video, init) {};
+
+/**
+ * @param {HTMLVideoElement} video
+ * @param {XRMediaEquirectLayerInit} init
+ * @return {XREquirectLayer}
+ */
+XRMediaBinding.prototype.createEquirectLayer = function(video, init) {};
+
+/**
+ * @type {Array<XRLayer>}
+ */
+XRRenderState.prototype.layers;
diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp
index cd403a4996..cabff65c9b 100644
--- a/modules/webxr/register_types.cpp
+++ b/modules/webxr/register_types.cpp
@@ -1,39 +1,39 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webxr_interface.h"
#include "webxr_interface_js.h"
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
Ref<WebXRInterfaceJS> webxr;
#endif
@@ -44,7 +44,7 @@ void initialize_webxr_module(ModuleInitializationLevel p_level) {
GDREGISTER_ABSTRACT_CLASS(WebXRInterface);
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
webxr.instantiate();
XRServer::get_singleton()->add_interface(webxr);
#endif
@@ -55,9 +55,9 @@ void uninitialize_webxr_module(ModuleInitializationLevel p_level) {
return;
}
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
if (webxr.is_valid()) {
- // uninitialise our interface if it is initialised
+ // uninitialize our interface if it is initialized
if (webxr->is_initialized()) {
webxr->uninitialize();
}
diff --git a/modules/webxr/register_types.h b/modules/webxr/register_types.h
index 2ea9bc1169..30798c0731 100644
--- a/modules/webxr/register_types.h
+++ b/modules/webxr/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBXR_REGISTER_TYPES_H
#define WEBXR_REGISTER_TYPES_H
diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp
index b0ad53523a..d7f4247768 100644
--- a/modules/webxr/webxr_interface.cpp
+++ b/modules/webxr/webxr_interface.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webxr_interface.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "webxr_interface.h"
#include <stdlib.h>
@@ -42,9 +42,10 @@ void WebXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_reference_space_type"), &WebXRInterface::get_reference_space_type);
ClassDB::bind_method(D_METHOD("set_requested_reference_space_types", "requested_reference_space_types"), &WebXRInterface::set_requested_reference_space_types);
ClassDB::bind_method(D_METHOD("get_requested_reference_space_types"), &WebXRInterface::get_requested_reference_space_types);
- ClassDB::bind_method(D_METHOD("get_controller", "controller_id"), &WebXRInterface::get_controller);
+ ClassDB::bind_method(D_METHOD("is_input_source_active", "input_source_id"), &WebXRInterface::is_input_source_active);
+ ClassDB::bind_method(D_METHOD("get_input_source_tracker", "input_source_id"), &WebXRInterface::get_input_source_tracker);
+ ClassDB::bind_method(D_METHOD("get_input_source_target_ray_mode", "input_source_id"), &WebXRInterface::get_input_source_target_ray_mode);
ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
- ClassDB::bind_method(D_METHOD("get_bounds_geometry"), &WebXRInterface::get_bounds_geometry);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
@@ -52,20 +53,24 @@ void WebXRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "requested_reference_space_types", PROPERTY_HINT_NONE), "set_requested_reference_space_types", "get_requested_reference_space_types");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "reference_space_type", PROPERTY_HINT_NONE), "", "get_reference_space_type");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "visibility_state", PROPERTY_HINT_NONE), "", "get_visibility_state");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "bounds_geometry", PROPERTY_HINT_NONE), "", "get_bounds_geometry");
ADD_SIGNAL(MethodInfo("session_supported", PropertyInfo(Variant::STRING, "session_mode"), PropertyInfo(Variant::BOOL, "supported")));
ADD_SIGNAL(MethodInfo("session_started"));
ADD_SIGNAL(MethodInfo("session_ended"));
ADD_SIGNAL(MethodInfo("session_failed", PropertyInfo(Variant::STRING, "message")));
- ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "controller_id")));
- ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "input_source_id")));
+ ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "input_source_id")));
ADD_SIGNAL(MethodInfo("visibility_state_changed"));
ADD_SIGNAL(MethodInfo("reference_space_reset"));
+
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_UNKNOWN);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_GAZE);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_TRACKED_POINTER);
+ BIND_ENUM_CONSTANT(TARGET_RAY_MODE_SCREEN);
}
diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h
index 801643bfa6..f18b4b40e6 100644
--- a/modules/webxr/webxr_interface.h
+++ b/modules/webxr/webxr_interface.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* webxr_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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webxr_interface.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBXR_INTERFACE_H
#define WEBXR_INTERFACE_H
@@ -45,6 +45,13 @@ protected:
static void _bind_methods();
public:
+ enum TargetRayMode {
+ TARGET_RAY_MODE_UNKNOWN,
+ TARGET_RAY_MODE_GAZE,
+ TARGET_RAY_MODE_TRACKED_POINTER,
+ TARGET_RAY_MODE_SCREEN,
+ };
+
virtual void is_session_supported(const String &p_session_mode) = 0;
virtual void set_session_mode(String p_session_mode) = 0;
virtual String get_session_mode() const = 0;
@@ -55,9 +62,12 @@ public:
virtual void set_requested_reference_space_types(String p_requested_reference_space_types) = 0;
virtual String get_requested_reference_space_types() const = 0;
virtual String get_reference_space_type() const = 0;
- virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const = 0;
+ virtual bool is_input_source_active(int p_input_source_id) const = 0;
+ virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const = 0;
+ virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const = 0;
virtual String get_visibility_state() const = 0;
- virtual PackedVector3Array get_bounds_geometry() const = 0;
};
+VARIANT_ENUM_CAST(WebXRInterface::TargetRayMode);
+
#endif // WEBXR_INTERFACE_H
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 07e6760555..8485c62218 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -1,42 +1,46 @@
-/*************************************************************************/
-/* webxr_interface_js.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. */
-/*************************************************************************/
-
-#ifdef JAVASCRIPT_ENABLED
+/**************************************************************************/
+/* webxr_interface_js.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef WEB_ENABLED
#include "webxr_interface_js.h"
#include "core/input/input.h"
#include "core/os/os.h"
+#include "drivers/gles3/storage/texture_storage.h"
#include "emscripten.h"
#include "godot_webxr.h"
+#include "scene/main/scene_tree.h"
+#include "scene/main/window.h"
#include "servers/rendering/renderer_compositor.h"
+#include "servers/rendering/rendering_server_globals.h"
#include <stdlib.h>
@@ -87,25 +91,14 @@ void _emwebxr_on_session_failed(char *p_message) {
interface->emit_signal(SNAME("session_failed"), message);
}
-void _emwebxr_on_controller_changed() {
+extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(int p_event_type, int p_input_source_id) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
Ref<XRInterface> interface = xr_server->find_interface("WebXR");
ERR_FAIL_COND(interface.is_null());
- static_cast<WebXRInterfaceJS *>(interface.ptr())->_on_controller_changed();
-}
-
-extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(char *p_signal_name, int p_input_source) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- Ref<XRInterface> interface = xr_server->find_interface("WebXR");
- ERR_FAIL_COND(interface.is_null());
-
- StringName signal_name = StringName(p_signal_name);
- interface->emit_signal(signal_name, p_input_source + 1);
+ ((WebXRInterfaceJS *)interface.ptr())->_on_input_event(p_event_type, p_input_source_id);
}
extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) {
@@ -163,16 +156,22 @@ String WebXRInterfaceJS::get_reference_space_type() const {
return reference_space_type;
}
-Ref<XRPositionalTracker> WebXRInterfaceJS::get_controller(int p_controller_id) const {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, Ref<XRPositionalTracker>());
+bool WebXRInterfaceJS::is_input_source_active(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, false);
+ return input_sources[p_input_source_id].active;
+}
- // TODO support more then two controllers
- if (p_controller_id >= 0 && p_controller_id < 2) {
- return controllers[p_controller_id];
- };
+Ref<XRPositionalTracker> WebXRInterfaceJS::get_input_source_tracker(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, Ref<XRPositionalTracker>());
+ return input_sources[p_input_source_id].tracker;
+}
- return Ref<XRPositionalTracker>();
+WebXRInterface::TargetRayMode WebXRInterfaceJS::get_input_source_target_ray_mode(int p_input_source_id) const {
+ ERR_FAIL_INDEX_V(p_input_source_id, input_source_count, WebXRInterface::TARGET_RAY_MODE_UNKNOWN);
+ if (!input_sources[p_input_source_id].active) {
+ return WebXRInterface::TARGET_RAY_MODE_UNKNOWN;
+ }
+ return input_sources[p_input_source_id].target_ray_mode;
}
String WebXRInterfaceJS::get_visibility_state() const {
@@ -186,17 +185,18 @@ String WebXRInterfaceJS::get_visibility_state() const {
return String();
}
-PackedVector3Array WebXRInterfaceJS::get_bounds_geometry() const {
+PackedVector3Array WebXRInterfaceJS::get_play_area() const {
PackedVector3Array ret;
- int *js_bounds = godot_webxr_get_bounds_geometry();
- if (js_bounds) {
- ret.resize(js_bounds[0]);
- for (int i = 0; i < js_bounds[0]; i++) {
- float *js_vector3 = ((float *)js_bounds) + (i * 3) + 1;
+ float *points;
+ int point_count = godot_webxr_get_bounds_geometry(&points);
+ if (point_count > 0) {
+ ret.resize(point_count);
+ for (int i = 0; i < point_count; i++) {
+ float *js_vector3 = points + (i * 3);
ret.set(i, Vector3(js_vector3[0], js_vector3[1], js_vector3[2]));
}
- free(js_bounds);
+ free(points);
}
return ret;
@@ -207,7 +207,7 @@ StringName WebXRInterfaceJS::get_name() const {
};
uint32_t WebXRInterfaceJS::get_capabilities() const {
- return XRInterface::XR_STEREO | XRInterface::XR_MONO;
+ return XRInterface::XR_STEREO | XRInterface::XR_MONO | XRInterface::XR_VR | XRInterface::XR_AR;
};
uint32_t WebXRInterfaceJS::get_view_count() {
@@ -232,6 +232,8 @@ bool WebXRInterfaceJS::initialize() {
}
// we must create a tracker for our head
+ head_transform.basis = Basis();
+ head_transform.origin = Vector3();
head_tracker.instantiate();
head_tracker->set_tracker_type(XRServer::TRACKER_HEAD);
head_tracker->set_tracker_name("head");
@@ -257,7 +259,6 @@ bool WebXRInterfaceJS::initialize() {
&_emwebxr_on_session_started,
&_emwebxr_on_session_ended,
&_emwebxr_on_session_failed,
- &_emwebxr_on_controller_changed,
&_emwebxr_on_input_event,
&_emwebxr_on_simple_event);
};
@@ -283,6 +284,18 @@ void WebXRInterfaceJS::uninitialize() {
godot_webxr_uninitialize();
+ GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+ if (texture_storage != nullptr) {
+ for (KeyValue<unsigned int, RID> &E : texture_cache) {
+ // Forcibly mark as not part of a render target so we can free it.
+ GLES3::Texture *texture = texture_storage->get_texture(E.value);
+ texture->is_render_target = false;
+
+ texture_storage->texture_free(E.value);
+ }
+ }
+
+ texture_cache.clear();
reference_space_type = "";
initialized = false;
};
@@ -312,197 +325,410 @@ Size2 WebXRInterfaceJS::get_render_target_size() {
return render_targetsize;
}
- int *js_size = godot_webxr_get_render_target_size();
- if (!initialized || js_size == nullptr) {
- // As a temporary default (until WebXR is fully initialized), use half the window size.
- Size2 temp = DisplayServer::get_singleton()->window_get_size();
- temp.width /= 2.0;
- return temp;
- }
+ int js_size[2];
+ bool has_size = godot_webxr_get_render_target_size(js_size);
- render_targetsize.width = js_size[0];
- render_targetsize.height = js_size[1];
+ if (!initialized || !has_size) {
+ // As a temporary default (until WebXR is fully initialized), use the
+ // window size.
+ return DisplayServer::get_singleton()->window_get_size();
+ }
- free(js_size);
+ render_targetsize.width = (float)js_size[0];
+ render_targetsize.height = (float)js_size[1];
return render_targetsize;
};
Transform3D WebXRInterfaceJS::get_camera_transform() {
- Transform3D transform_for_eye;
+ Transform3D camera_transform;
XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+ ERR_FAIL_NULL_V(xr_server, camera_transform);
- float *js_matrix = godot_webxr_get_transform_for_eye(0);
- if (!initialized || js_matrix == nullptr) {
- return transform_for_eye;
- }
+ if (initialized) {
+ float world_scale = xr_server->get_world_scale();
+
+ // just scale our origin point of our transform
+ Transform3D _head_transform = head_transform;
+ _head_transform.origin *= world_scale;
- transform_for_eye = _js_matrix_to_transform(js_matrix);
- free(js_matrix);
+ camera_transform = (xr_server->get_reference_frame()) * _head_transform;
+ }
- return xr_server->get_reference_frame() * transform_for_eye;
+ return camera_transform;
};
Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
- Transform3D transform_for_eye;
-
XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+ ERR_FAIL_NULL_V(xr_server, p_cam_transform);
+ ERR_FAIL_COND_V(!initialized, p_cam_transform);
- float *js_matrix = godot_webxr_get_transform_for_eye(p_view + 1);
- if (!initialized || js_matrix == nullptr) {
- transform_for_eye = p_cam_transform;
- return transform_for_eye;
+ float js_matrix[16];
+ bool has_transform = godot_webxr_get_transform_for_view(p_view, js_matrix);
+ if (!has_transform) {
+ return p_cam_transform;
}
- transform_for_eye = _js_matrix_to_transform(js_matrix);
- free(js_matrix);
+ Transform3D transform_for_view = _js_matrix_to_transform(js_matrix);
+
+ float world_scale = xr_server->get_world_scale();
+ // Scale only the center point of our eye transform, so we don't scale the
+ // distance between the eyes.
+ Transform3D _head_transform = head_transform;
+ transform_for_view.origin -= _head_transform.origin;
+ _head_transform.origin *= world_scale;
+ transform_for_view.origin += _head_transform.origin;
- return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
+ return p_cam_transform * xr_server->get_reference_frame() * transform_for_view;
};
Projection WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
- Projection eye;
+ Projection view;
+
+ ERR_FAIL_COND_V(!initialized, view);
- float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
- if (!initialized || js_matrix == nullptr) {
- return eye;
+ float js_matrix[16];
+ bool has_projection = godot_webxr_get_projection_for_view(p_view, js_matrix);
+ if (!has_projection) {
+ return view;
}
int k = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- eye.matrix[i][j] = js_matrix[k++];
+ view.columns[i][j] = js_matrix[k++];
}
}
- free(js_matrix);
-
// Copied from godot_oculus_mobile's ovr_mobile_session.cpp
- eye.matrix[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near);
- eye.matrix[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near);
+ view.columns[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near);
+ view.columns[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near);
- return eye;
+ return view;
}
-Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
- Vector<BlitToScreen> blit_to_screen;
+bool WebXRInterfaceJS::pre_draw_viewport(RID p_render_target) {
+ GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+ if (texture_storage == nullptr) {
+ return false;
+ }
- if (!initialized) {
- return blit_to_screen;
+ GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target);
+ if (rt == nullptr) {
+ return false;
}
- // @todo Refactor this to be based on "views" rather than "eyes".
- godot_webxr_commit_for_eye(1);
- if (godot_webxr_get_view_count() > 1) {
- godot_webxr_commit_for_eye(2);
+ // Cache the resources so we don't have to get them from JS twice.
+ color_texture = _get_color_texture();
+ depth_texture = _get_depth_texture();
+
+ // Per the WebXR spec, it returns "opaque textures" to us, which may be the
+ // same WebGLTexture object (which would be the same GLuint in C++) but
+ // represent a different underlying resource (probably the next texture in
+ // the XR device's swap chain). In order to render to this texture, we need
+ // to re-attach it to the FBO, otherwise we get an "incomplete FBO" error.
+ //
+ // See: https://immersive-web.github.io/layers/#xropaquetextures
+ //
+ // This is why we're doing this sort of silly check: if the color and depth
+ // textures are the same this frame as last frame, we need to attach them
+ // again, despite the fact that the GLuint for them hasn't changed.
+ if (rt->overridden.is_overridden && rt->overridden.color == color_texture && rt->overridden.depth == depth_texture) {
+ GLES3::Config *config = GLES3::Config::get_singleton();
+ bool use_multiview = rt->view_count > 1 && config->multiview_supported;
+
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
+ if (use_multiview) {
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
+ } else {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
}
+ return true;
+}
+
+Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
+ Vector<BlitToScreen> blit_to_screen;
+
+ // We don't need to do anything here.
+
return blit_to_screen;
};
+RID WebXRInterfaceJS::_get_color_texture() {
+ unsigned int texture_id = godot_webxr_get_color_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
+
+RID WebXRInterfaceJS::_get_depth_texture() {
+ unsigned int texture_id = godot_webxr_get_depth_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
+
+RID WebXRInterfaceJS::_get_texture(unsigned int p_texture_id) {
+ RBMap<unsigned int, RID>::Element *cache = texture_cache.find(p_texture_id);
+ if (cache != nullptr) {
+ return cache->get();
+ }
+
+ GLES3::TextureStorage *texture_storage = dynamic_cast<GLES3::TextureStorage *>(RSG::texture_storage);
+ if (texture_storage == nullptr) {
+ return RID();
+ }
+
+ uint32_t view_count = godot_webxr_get_view_count();
+ Size2 texture_size = get_render_target_size();
+
+ RID texture = texture_storage->texture_create_external(
+ view_count == 1 ? GLES3::Texture::TYPE_2D : GLES3::Texture::TYPE_LAYERED,
+ Image::FORMAT_RGBA8,
+ p_texture_id,
+ (int)texture_size.width,
+ (int)texture_size.height,
+ 1,
+ view_count);
+
+ texture_cache.insert(p_texture_id, texture);
+
+ return texture;
+}
+
+RID WebXRInterfaceJS::get_color_texture() {
+ return color_texture;
+}
+
+RID WebXRInterfaceJS::get_depth_texture() {
+ return depth_texture;
+}
+
+RID WebXRInterfaceJS::get_velocity_texture() {
+ unsigned int texture_id = godot_webxr_get_velocity_texture();
+ if (texture_id == 0) {
+ return RID();
+ }
+
+ return _get_texture(texture_id);
+}
+
void WebXRInterfaceJS::process() {
if (initialized) {
- godot_webxr_sample_controller_data();
-
- if (head_tracker.is_valid()) {
- // TODO set default pose to our head location (i.e. get_camera_transform without world scale and reference frame applied)
- // head_tracker->set_pose("default", head_transform, Vector3(), Vector3());
+ // Get the "head" position.
+ float js_matrix[16];
+ if (godot_webxr_get_transform_for_view(-1, js_matrix)) {
+ head_transform = _js_matrix_to_transform(js_matrix);
}
-
- int controller_count = godot_webxr_get_controller_count();
- if (controller_count == 0) {
- return;
+ if (head_tracker.is_valid()) {
+ head_tracker->set_pose("default", head_transform, Vector3(), Vector3());
}
- for (int i = 0; i < controller_count; i++) {
- _update_tracker(i);
+ // Update all input sources.
+ for (int i = 0; i < input_source_count; i++) {
+ _update_input_source(i);
}
};
};
-void WebXRInterfaceJS::_update_tracker(int p_controller_id) {
+void WebXRInterfaceJS::_update_input_source(int p_input_source_id) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
- // need to support more then two controllers...
- if (p_controller_id < 0 || p_controller_id > 1) {
+ InputSource &input_source = input_sources[p_input_source_id];
+
+ float target_pose[16];
+ int tmp_target_ray_mode;
+ int touch_index;
+ int has_grip_pose;
+ float grip_pose[16];
+ int has_standard_mapping;
+ int button_count;
+ float buttons[10];
+ int axes_count;
+ float axes[10];
+
+ input_source.active = godot_webxr_update_input_source(
+ p_input_source_id,
+ target_pose,
+ &tmp_target_ray_mode,
+ &touch_index,
+ &has_grip_pose,
+ grip_pose,
+ &has_standard_mapping,
+ &button_count,
+ buttons,
+ &axes_count,
+ axes);
+
+ if (!input_source.active) {
+ if (input_source.tracker.is_valid()) {
+ xr_server->remove_tracker(input_source.tracker);
+ input_source.tracker.unref();
+ }
return;
}
- Ref<XRPositionalTracker> tracker = controllers[p_controller_id];
- if (godot_webxr_is_controller_connected(p_controller_id)) {
- if (tracker.is_null()) {
- tracker.instantiate();
- tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- // Controller id's 0 and 1 are always the left and right hands.
- if (p_controller_id < 2) {
- tracker->set_tracker_name(p_controller_id == 0 ? "left_hand" : "right_hand");
- tracker->set_tracker_desc(p_controller_id == 0 ? "Left hand controller" : "Right hand controller");
- tracker->set_tracker_hand(p_controller_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
- } else {
- char name[1024];
- sprintf(name, "tracker_%i", p_controller_id);
- tracker->set_tracker_name(name);
- tracker->set_tracker_desc(name);
- }
- xr_server->add_tracker(tracker);
+ input_source.target_ray_mode = (WebXRInterface::TargetRayMode)tmp_target_ray_mode;
+ input_source.touch_index = touch_index;
+
+ Ref<XRPositionalTracker> &tracker = input_source.tracker;
+
+ if (tracker.is_null()) {
+ tracker.instantiate();
+
+ StringName tracker_name;
+ if (input_source.target_ray_mode == WebXRInterface::TargetRayMode::TARGET_RAY_MODE_SCREEN) {
+ tracker_name = touch_names[touch_index];
+ } else {
+ tracker_name = tracker_names[p_input_source_id];
}
- float *tracker_matrix = godot_webxr_get_controller_transform(p_controller_id);
- if (tracker_matrix) {
- // Note, poses should NOT have world scale and our reference frame applied!
- Transform3D transform = _js_matrix_to_transform(tracker_matrix);
- tracker->set_pose("default", transform, Vector3(), Vector3());
- free(tracker_matrix);
+ // Input source id's 0 and 1 are always the left and right hands.
+ if (p_input_source_id < 2) {
+ tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
+ tracker->set_tracker_name(tracker_name);
+ tracker->set_tracker_desc(p_input_source_id == 0 ? "Left hand controller" : "Right hand controller");
+ tracker->set_tracker_hand(p_input_source_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
+ } else {
+ tracker->set_tracker_name(tracker_name);
+ tracker->set_tracker_desc(tracker_name);
}
+ xr_server->add_tracker(tracker);
+ }
- // TODO implement additional poses such as "aim" and "grip"
+ Transform3D aim_transform = _js_matrix_to_transform(target_pose);
+ tracker->set_pose(SNAME("default"), aim_transform, Vector3(), Vector3());
+ tracker->set_pose(SNAME("aim"), aim_transform, Vector3(), Vector3());
+ if (has_grip_pose) {
+ tracker->set_pose(SNAME("grip"), _js_matrix_to_transform(grip_pose), Vector3(), Vector3());
+ }
- int *buttons = godot_webxr_get_controller_buttons(p_controller_id);
- if (buttons) {
- // TODO buttons should be named properly, this is just a temporary fix
- for (int i = 0; i < buttons[0]; i++) {
- char name[1024];
- sprintf(name, "button_%i", i);
+ for (int i = 0; i < button_count; i++) {
+ StringName button_name = has_standard_mapping ? standard_button_names[i] : unknown_button_names[i];
+ StringName button_pressure_name = has_standard_mapping ? standard_button_pressure_names[i] : unknown_button_pressure_names[i];
+ float value = buttons[i];
+ bool state = value > 0.0;
+ tracker->set_input(button_name, state);
+ tracker->set_input(button_pressure_name, value);
+ }
- float value = *((float *)buttons + (i + 1));
- bool state = value > 0.0;
- tracker->set_input(name, state);
- }
- free(buttons);
+ for (int i = 0; i < axes_count; i++) {
+ StringName axis_name = has_standard_mapping ? standard_axis_names[i] : unknown_axis_names[i];
+ float value = axes[i];
+ if (has_standard_mapping && (i == 1 || i == 3)) {
+ // Invert the Y-axis on thumbsticks and trackpads, in order to
+ // match OpenXR and other XR platform SDKs.
+ value = -value;
}
+ tracker->set_input(axis_name, value);
+ }
- int *axes = godot_webxr_get_controller_axes(p_controller_id);
- if (axes) {
- // TODO again just a temporary fix, split these between proper float and vector2 inputs
- for (int i = 0; i < axes[0]; i++) {
- char name[1024];
- sprintf(name, "axis_%i", i);
+ // Also create Vector2's for the thumbstick and trackpad when we have the
+ // standard mapping.
+ if (has_standard_mapping) {
+ if (axes_count >= 2) {
+ tracker->set_input(standard_vector_names[0], Vector2(axes[0], -axes[1]));
+ }
+ if (axes_count >= 4) {
+ tracker->set_input(standard_vector_names[1], Vector2(axes[2], -axes[3]));
+ }
+ }
- float value = *((float *)axes + (i + 1));
- tracker->set_input(name, value);
+ if (input_source.target_ray_mode == WebXRInterface::TARGET_RAY_MODE_SCREEN) {
+ if (touch_index < 5 && axes_count >= 2) {
+ Vector2 joy_vector = Vector2(axes[0], axes[1]);
+ Vector2 position = _get_screen_position_from_joy_vector(joy_vector);
+
+ if (touches[touch_index].is_touching) {
+ Vector2 delta = position - touches[touch_index].position;
+
+ // If position has changed by at least 1 pixel, generate a drag event.
+ if (abs(delta.x) >= 1.0 || abs(delta.y) >= 1.0) {
+ Ref<InputEventScreenDrag> event;
+ event.instantiate();
+ event->set_index(touch_index);
+ event->set_position(position);
+ event->set_relative(delta);
+ Input::get_singleton()->parse_input_event(event);
+ }
}
- free(axes);
+
+ touches[touch_index].position = position;
}
- } else if (tracker.is_valid()) {
- xr_server->remove_tracker(tracker);
- controllers[p_controller_id].unref();
}
}
-void WebXRInterfaceJS::_on_controller_changed() {
- // Register "virtual" gamepads with Godot for the ones we get from WebXR.
- godot_webxr_sample_controller_data();
- for (int i = 0; i < 2; i++) {
- bool controller_connected = godot_webxr_is_controller_connected(i);
- if (controllers_state[i] != controller_connected) {
- // Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", "");
- controllers_state[i] = controller_connected;
+void WebXRInterfaceJS::_on_input_event(int p_event_type, int p_input_source_id) {
+ // Get the latest data for this input source. For transient input sources,
+ // we may not have any data at all yet!
+ _update_input_source(p_input_source_id);
+
+ if (p_event_type == WEBXR_INPUT_EVENT_SELECTSTART || p_event_type == WEBXR_INPUT_EVENT_SELECTEND) {
+ const InputSource &input_source = input_sources[p_input_source_id];
+ if (input_source.target_ray_mode == WebXRInterface::TARGET_RAY_MODE_SCREEN) {
+ int touch_index = input_source.touch_index;
+ if (touch_index >= 0 && touch_index < 5) {
+ touches[touch_index].is_touching = (p_event_type == WEBXR_INPUT_EVENT_SELECTSTART);
+
+ Ref<InputEventScreenTouch> event;
+ event.instantiate();
+ event->set_index(touch_index);
+ event->set_position(touches[touch_index].position);
+ event->set_pressed(p_event_type == WEBXR_INPUT_EVENT_SELECTSTART);
+
+ Input::get_singleton()->parse_input_event(event);
+ }
}
}
+
+ switch (p_event_type) {
+ case WEBXR_INPUT_EVENT_SELECTSTART:
+ emit_signal("selectstart", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SELECTEND:
+ emit_signal("selectend", p_input_source_id);
+ // Emit the 'select' event on our own (rather than intercepting the
+ // one from JavaScript) so that we don't have to needlessly call
+ // _update_input_source() a second time.
+ emit_signal("select", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SQUEEZESTART:
+ emit_signal("squeezestart", p_input_source_id);
+ break;
+
+ case WEBXR_INPUT_EVENT_SQUEEZEEND:
+ emit_signal("squeezeend", p_input_source_id);
+ // Again, we emit the 'squeeze' event on our own to avoid extra work.
+ emit_signal("squeeze", p_input_source_id);
+ break;
+ }
+}
+
+Vector2 WebXRInterfaceJS::_get_screen_position_from_joy_vector(const Vector2 &p_joy_vector) {
+ SceneTree *scene_tree = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
+ if (!scene_tree) {
+ return Vector2();
+ }
+
+ Window *viewport = scene_tree->get_root();
+
+ Vector2 position_percentage((p_joy_vector.x + 1.0f) / 2.0f, ((p_joy_vector.y) + 1.0f) / 2.0f);
+ Vector2 position = (Size2)viewport->get_size() * position_percentage;
+
+ return position;
}
WebXRInterfaceJS::WebXRInterfaceJS() {
@@ -518,4 +744,4 @@ WebXRInterfaceJS::~WebXRInterfaceJS() {
};
};
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index f1ffedba46..31d2e200c0 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -1,37 +1,37 @@
-/*************************************************************************/
-/* webxr_interface_js.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* webxr_interface_js.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WEBXR_INTERFACE_JS_H
#define WEBXR_INTERFACE_JS_H
-#ifdef JAVASCRIPT_ENABLED
+#ifdef WEB_ENABLED
#include "webxr_interface.h"
@@ -39,12 +39,17 @@
The WebXR interface is a VR/AR interface that can be used on the web.
*/
+namespace GLES3 {
+class TextureStorage;
+}
+
class WebXRInterfaceJS : public WebXRInterface {
GDCLASS(WebXRInterfaceJS, WebXRInterface);
private:
bool initialized;
Ref<XRPositionalTracker> head_tracker;
+ Transform3D head_transform;
String session_mode;
String required_features;
@@ -52,13 +57,32 @@ private:
String requested_reference_space_types;
String reference_space_type;
- // TODO maybe turn into a vector to support more then 2 controllers...
- bool controllers_state[2];
- Ref<XRPositionalTracker> controllers[2];
Size2 render_targetsize;
-
+ RBMap<unsigned int, RID> texture_cache;
+ struct Touch {
+ bool is_touching = false;
+ Vector2 position;
+ } touches[5];
+
+ static constexpr uint8_t input_source_count = 16;
+
+ struct InputSource {
+ Ref<XRPositionalTracker> tracker;
+ bool active = false;
+ TargetRayMode target_ray_mode;
+ int touch_index = -1;
+ } input_sources[input_source_count];
+
+ RID color_texture;
+ RID depth_texture;
+
+ RID _get_color_texture();
+ RID _get_depth_texture();
+ RID _get_texture(unsigned int p_texture_id);
Transform3D _js_matrix_to_transform(float *p_js_matrix);
- void _update_tracker(int p_controller_id);
+ void _update_input_source(int p_input_source_id);
+
+ Vector2 _get_screen_position_from_joy_vector(const Vector2 &p_joy_vector);
public:
virtual void is_session_supported(const String &p_session_mode) override;
@@ -72,9 +96,11 @@ public:
virtual String get_requested_reference_space_types() const override;
void _set_reference_space_type(String p_reference_space_type);
virtual String get_reference_space_type() const override;
- virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const override;
+ virtual bool is_input_source_active(int p_input_source_id) const override;
+ virtual Ref<XRPositionalTracker> get_input_source_tracker(int p_input_source_id) const override;
+ virtual TargetRayMode get_input_source_target_ray_mode(int p_input_source_id) const override;
virtual String get_visibility_state() const override;
- virtual PackedVector3Array get_bounds_geometry() const override;
+ virtual PackedVector3Array get_play_area() const override;
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
@@ -88,16 +114,131 @@ public:
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual Projection get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
+ virtual bool pre_draw_viewport(RID p_render_target) override;
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
+ virtual RID get_color_texture() override;
+ virtual RID get_depth_texture() override;
+ virtual RID get_velocity_texture() override;
virtual void process() override;
- void _on_controller_changed();
+ void _on_input_event(int p_event_type, int p_input_source_id);
WebXRInterfaceJS();
~WebXRInterfaceJS();
+
+private:
+ StringName tracker_names[16] = {
+ StringName("left_hand"),
+ StringName("right_hand"),
+ StringName("tracker_2"),
+ StringName("tracker_3"),
+ StringName("tracker_4"),
+ StringName("tracker_5"),
+ StringName("tracker_6"),
+ StringName("tracker_7"),
+ StringName("tracker_8"),
+ StringName("tracker_9"),
+ StringName("tracker_10"),
+ StringName("tracker_11"),
+ StringName("tracker_12"),
+ StringName("tracker_13"),
+ StringName("tracker_14"),
+ StringName("tracker_15"),
+ };
+
+ StringName touch_names[5] = {
+ StringName("touch_0"),
+ StringName("touch_1"),
+ StringName("touch_2"),
+ StringName("touch_3"),
+ StringName("touch_4"),
+ };
+
+ StringName standard_axis_names[10] = {
+ StringName("touchpad_x"),
+ StringName("touchpad_y"),
+ StringName("thumbstick_x"),
+ StringName("thumbstick_y"),
+ StringName("axis_4"),
+ StringName("axis_5"),
+ StringName("axis_6"),
+ StringName("axis_7"),
+ StringName("axis_8"),
+ StringName("axis_9"),
+ };
+
+ StringName standard_vector_names[2] = {
+ StringName("touchpad"),
+ StringName("thumbstick"),
+ };
+
+ StringName standard_button_names[10] = {
+ StringName("trigger_click"),
+ StringName("grip_click"),
+ StringName("touchpad_click"),
+ StringName("thumbstick_click"),
+ StringName("ax_button"),
+ StringName("by_button"),
+ StringName("button_6"),
+ StringName("button_7"),
+ StringName("button_8"),
+ StringName("button_9"),
+ };
+
+ StringName standard_button_pressure_names[10] = {
+ StringName("trigger"),
+ StringName("grip"),
+ StringName("touchpad_click_pressure"),
+ StringName("thumbstick_click_pressure"),
+ StringName("ax_button_pressure"),
+ StringName("by_button_pressure"),
+ StringName("button_pressure_6"),
+ StringName("button_pressure_7"),
+ StringName("button_pressure_8"),
+ StringName("button_pressure_9"),
+ };
+
+ StringName unknown_button_names[10] = {
+ StringName("button_0"),
+ StringName("button_1"),
+ StringName("button_2"),
+ StringName("button_3"),
+ StringName("button_4"),
+ StringName("button_5"),
+ StringName("button_6"),
+ StringName("button_7"),
+ StringName("button_8"),
+ StringName("button_9"),
+ };
+
+ StringName unknown_axis_names[10] = {
+ StringName("axis_0"),
+ StringName("axis_1"),
+ StringName("axis_2"),
+ StringName("axis_3"),
+ StringName("axis_4"),
+ StringName("axis_5"),
+ StringName("axis_6"),
+ StringName("axis_7"),
+ StringName("axis_8"),
+ StringName("axis_9"),
+ };
+
+ StringName unknown_button_pressure_names[10] = {
+ StringName("button_pressure_0"),
+ StringName("button_pressure_1"),
+ StringName("button_pressure_2"),
+ StringName("button_pressure_3"),
+ StringName("button_pressure_4"),
+ StringName("button_pressure_5"),
+ StringName("button_pressure_6"),
+ StringName("button_pressure_7"),
+ StringName("button_pressure_8"),
+ StringName("button_pressure_9"),
+ };
};
-#endif // JAVASCRIPT_ENABLED
+#endif // WEB_ENABLED
#endif // WEBXR_INTERFACE_JS_H
diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py
index 2e73c51626..ecc61c2d7e 100644
--- a/modules/xatlas_unwrap/config.py
+++ b/modules/xatlas_unwrap/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env["tools"] and platform not in ["android", "ios"]
+ return env.editor_build and platform not in ["android", "ios"]
def configure(env):
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index e9985984a6..c3da058b1d 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/crypto/crypto_core.h"
diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h
index f82b5912f4..360947d880 100644
--- a/modules/xatlas_unwrap/register_types.h
+++ b/modules/xatlas_unwrap/register_types.h
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* 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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 XATLAS_UNWRAP_REGISTER_TYPES_H
#define XATLAS_UNWRAP_REGISTER_TYPES_H
diff --git a/modules/zip/SCsub b/modules/zip/SCsub
new file mode 100644
index 0000000000..b7710123fd
--- /dev/null
+++ b/modules/zip/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_zip = env_modules.Clone()
+
+# Module files
+env_zip.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/zip/config.py b/modules/zip/config.py
new file mode 100644
index 0000000000..96cd2fc5bd
--- /dev/null
+++ b/modules/zip/config.py
@@ -0,0 +1,17 @@
+def can_build(env, platform):
+ return env["minizip"]
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "ZIPReader",
+ "ZIPPacker",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/zip/doc_classes/ZIPPacker.xml b/modules/zip/doc_classes/ZIPPacker.xml
new file mode 100644
index 0000000000..95d7ef50f9
--- /dev/null
+++ b/modules/zip/doc_classes/ZIPPacker.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ZIPPacker" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Allows the creation of zip files.
+ </brief_description>
+ <description>
+ This class implements a writer that allows storing the multiple blobs in a zip archive.
+ [codeblock]
+ func write_zip_file():
+ var writer := ZIPPacker.new()
+ var err := writer.open("user://archive.zip")
+ if err != OK:
+ return err
+ writer.start_file("hello.txt")
+ writer.write_file("Hello World".to_utf8_buffer())
+ writer.close_file()
+
+ writer.close()
+ return OK
+ [/codeblock]
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="close">
+ <return type="int" enum="Error" />
+ <description>
+ Closes the underlying resources used by this instance.
+ </description>
+ </method>
+ <method name="close_file">
+ <return type="int" enum="Error" />
+ <description>
+ Stops writing to a file within the archive.
+ It will fail if there is no open file.
+ </description>
+ </method>
+ <method name="open">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="append" type="int" enum="ZIPPacker.ZipAppend" default="0" />
+ <description>
+ Opens a zip file for writing at the given path using the specified write mode.
+ This must be called before everything else.
+ </description>
+ </method>
+ <method name="start_file">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Starts writing to a file within the archive. Only one file can be written at the same time.
+ Must be called after [method open].
+ </description>
+ </method>
+ <method name="write_file">
+ <return type="int" enum="Error" />
+ <param index="0" name="data" type="PackedByteArray" />
+ <description>
+ Write the given [param data] to the file.
+ Needs to be called after [method start_file].
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="APPEND_CREATE" value="0" enum="ZipAppend">
+ </constant>
+ <constant name="APPEND_CREATEAFTER" value="1" enum="ZipAppend">
+ </constant>
+ <constant name="APPEND_ADDINZIP" value="2" enum="ZipAppend">
+ </constant>
+ </constants>
+</class>
diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml
new file mode 100644
index 0000000000..055201b105
--- /dev/null
+++ b/modules/zip/doc_classes/ZIPReader.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ZIPReader" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Allows reading the content of a zip file.
+ </brief_description>
+ <description>
+ This class implements a reader that can extract the content of individual files inside a zip archive.
+ [codeblock]
+ func read_zip_file():
+ var reader := ZIPReader.new()
+ var err := reader.open("user://archive.zip")
+ if err != OK:
+ return PackedByteArray()
+ var res := reader.read_file("hello.txt")
+ reader.close()
+ return res
+ [/codeblock]
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="close">
+ <return type="int" enum="Error" />
+ <description>
+ Closes the underlying resources used by this instance.
+ </description>
+ </method>
+ <method name="get_files">
+ <return type="PackedStringArray" />
+ <description>
+ Returns the list of names of all files in the loaded archive.
+ Must be called after [method open].
+ </description>
+ </method>
+ <method name="open">
+ <return type="int" enum="Error" />
+ <param index="0" name="path" type="String" />
+ <description>
+ Opens the zip archive at the given [param path] and reads its file index.
+ </description>
+ </method>
+ <method name="read_file">
+ <return type="PackedByteArray" />
+ <param index="0" name="path" type="String" />
+ <param index="1" name="case_sensitive" type="bool" default="true" />
+ <description>
+ Loads the whole content of a file in the loaded zip archive into memory and returns it.
+ Must be called after [method open].
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/zip/register_types.cpp
index a8f9cfa3ca..2df72d0cda 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/zip/register_types.cpp
@@ -1,52 +1,50 @@
-/*************************************************************************/
-/* gd_mono_internals.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 GD_MONO_INTERNALS_H
-#define GD_MONO_INTERNALS_H
-
-#include <mono/jit/jit.h>
-
-#include "../utils/macros.h"
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "core/object/class_db.h"
-
-namespace GDMonoInternals {
-void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
-
-/**
- * Do not call this function directly.
- * Use GDMonoUtils::debug_unhandled_exception(MonoException *) instead.
- */
-void unhandled_exception(MonoException *p_exc);
-
-void gd_unhandled_exception_event(MonoException *p_exc);
-} // namespace GDMonoInternals
-
-#endif // GD_MONO_INTERNALS_H
+#include "zip_packer.h"
+#include "zip_reader.h"
+
+void initialize_zip_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
+ GDREGISTER_CLASS(ZIPPacker);
+ GDREGISTER_CLASS(ZIPReader);
+}
+
+void uninitialize_zip_module(ModuleInitializationLevel p_level) {
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+}
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/zip/register_types.h
index bf21283080..5a05dabcf4 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/zip/register_types.h
@@ -1,52 +1,39 @@
-/*************************************************************************/
-/* gd_mono_header.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. */
-/*************************************************************************/
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GD_MONO_HEADER_H
-#define GD_MONO_HEADER_H
+#ifndef ZIP_REGISTER_TYPES_H
+#define ZIP_REGISTER_TYPES_H
-#include <cstdint>
+#include "modules/register_module_types.h"
-#ifdef WIN32
-#define GD_MONO_STDCALL __stdcall
-#else
-#define GD_MONO_STDCALL
-#endif
+void initialize_zip_module(ModuleInitializationLevel p_level);
+void uninitialize_zip_module(ModuleInitializationLevel p_level);
-class GDMonoAssembly;
-class GDMonoClass;
-class GDMonoField;
-class GDMonoMethod;
-class GDMonoProperty;
-
-class IMonoClassMember;
-
-#include "managed_type.h"
-
-#endif // GD_MONO_HEADER_H
+#endif // ZIP_REGISTER_TYPES_H
diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp
new file mode 100644
index 0000000000..65f451a3f9
--- /dev/null
+++ b/modules/zip/zip_packer.cpp
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* zip_packer.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "zip_packer.h"
+
+#include "core/io/zip_io.h"
+#include "core/os/os.h"
+
+Error ZIPPacker::open(String p_path, ZipAppend p_append) {
+ if (fa.is_valid()) {
+ close();
+ }
+
+ zlib_filefunc_def io = zipio_create_io(&fa);
+ zf = zipOpen2(p_path.utf8().get_data(), p_append, NULL, &io);
+ return zf != NULL ? OK : FAILED;
+}
+
+Error ZIPPacker::close() {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker cannot be closed because it is not open.");
+
+ Error err = zipClose(zf, NULL) == ZIP_OK ? OK : FAILED;
+ if (err == OK) {
+ zf = NULL;
+ }
+ return err;
+}
+
+Error ZIPPacker::start_file(String p_path) {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use.");
+
+ zip_fileinfo zipfi;
+
+ OS::DateTime time = OS::get_singleton()->get_datetime();
+
+ zipfi.tmz_date.tm_hour = time.hour;
+ zipfi.tmz_date.tm_mday = time.day;
+ zipfi.tmz_date.tm_min = time.minute;
+ zipfi.tmz_date.tm_mon = time.month - 1;
+ zipfi.tmz_date.tm_sec = time.second;
+ zipfi.tmz_date.tm_year = time.year;
+ zipfi.dosDate = 0;
+ zipfi.external_fa = 0;
+ zipfi.internal_fa = 0;
+
+ int ret = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
+ return ret == ZIP_OK ? OK : FAILED;
+}
+
+Error ZIPPacker::write_file(Vector<uint8_t> p_data) {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use.");
+
+ return zipWriteInFileInZip(zf, p_data.ptr(), p_data.size()) == ZIP_OK ? OK : FAILED;
+}
+
+Error ZIPPacker::close_file() {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use.");
+
+ return zipCloseFileInZip(zf) == ZIP_OK ? OK : FAILED;
+}
+
+void ZIPPacker::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("open", "path", "append"), &ZIPPacker::open, DEFVAL(Variant(APPEND_CREATE)));
+ ClassDB::bind_method(D_METHOD("start_file", "path"), &ZIPPacker::start_file);
+ ClassDB::bind_method(D_METHOD("write_file", "data"), &ZIPPacker::write_file);
+ ClassDB::bind_method(D_METHOD("close_file"), &ZIPPacker::close_file);
+ ClassDB::bind_method(D_METHOD("close"), &ZIPPacker::close);
+
+ BIND_ENUM_CONSTANT(APPEND_CREATE);
+ BIND_ENUM_CONSTANT(APPEND_CREATEAFTER);
+ BIND_ENUM_CONSTANT(APPEND_ADDINZIP);
+}
+
+ZIPPacker::ZIPPacker() {}
+
+ZIPPacker::~ZIPPacker() {
+ if (fa.is_valid()) {
+ close();
+ }
+}
diff --git a/modules/zip/zip_packer.h b/modules/zip/zip_packer.h
new file mode 100644
index 0000000000..d9d2602d33
--- /dev/null
+++ b/modules/zip/zip_packer.h
@@ -0,0 +1,68 @@
+/**************************************************************************/
+/* zip_packer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ZIP_PACKER_H
+#define ZIP_PACKER_H
+
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+
+#include "thirdparty/minizip/zip.h"
+
+class ZIPPacker : public RefCounted {
+ GDCLASS(ZIPPacker, RefCounted);
+
+ Ref<FileAccess> fa;
+ zipFile zf;
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum ZipAppend {
+ APPEND_CREATE = 0,
+ APPEND_CREATEAFTER = 1,
+ APPEND_ADDINZIP = 2,
+ };
+
+ Error open(String p_path, ZipAppend p_append);
+ Error close();
+
+ Error start_file(String p_path);
+ Error write_file(Vector<uint8_t> p_data);
+ Error close_file();
+
+ ZIPPacker();
+ ~ZIPPacker();
+};
+
+VARIANT_ENUM_CAST(ZIPPacker::ZipAppend)
+
+#endif // ZIP_PACKER_H
diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp
new file mode 100644
index 0000000000..898c36a12d
--- /dev/null
+++ b/modules/zip/zip_reader.cpp
@@ -0,0 +1,123 @@
+/**************************************************************************/
+/* zip_reader.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "zip_reader.h"
+
+#include "core/error/error_macros.h"
+#include "core/io/zip_io.h"
+
+Error ZIPReader::open(String p_path) {
+ if (fa.is_valid()) {
+ close();
+ }
+
+ zlib_filefunc_def io = zipio_create_io(&fa);
+ uzf = unzOpen2(p_path.utf8().get_data(), &io);
+ return uzf != NULL ? OK : FAILED;
+}
+
+Error ZIPReader::close() {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPReader cannot be closed because it is not open.");
+
+ return unzClose(uzf) == UNZ_OK ? OK : FAILED;
+}
+
+PackedStringArray ZIPReader::get_files() {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), PackedStringArray(), "ZIPReader must be opened before use.");
+
+ List<String> s;
+
+ if (unzGoToFirstFile(uzf) != UNZ_OK) {
+ return PackedStringArray();
+ }
+
+ do {
+ unz_file_info64 file_info;
+ char filename[256]; // Note filename is a path !
+ int err = unzGetCurrentFileInfo64(uzf, &file_info, filename, sizeof(filename), NULL, 0, NULL, 0);
+ if (err == UNZ_OK) {
+ s.push_back(filename);
+ } else {
+ // Assume filename buffer was too small
+ char *long_filename_buff = (char *)memalloc(file_info.size_filename);
+ int err2 = unzGetCurrentFileInfo64(uzf, NULL, long_filename_buff, sizeof(long_filename_buff), NULL, 0, NULL, 0);
+ if (err2 == UNZ_OK) {
+ s.push_back(long_filename_buff);
+ memfree(long_filename_buff);
+ }
+ }
+ } while (unzGoToNextFile(uzf) == UNZ_OK);
+
+ PackedStringArray arr;
+ arr.resize(s.size());
+ int idx = 0;
+ for (const List<String>::Element *E = s.front(); E; E = E->next()) {
+ arr.set(idx++, E->get());
+ }
+ return arr;
+}
+
+PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) {
+ ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "ZIPReader must be opened before use.");
+
+ int cs = p_case_sensitive ? 1 : 2;
+ if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "File does not exist in zip archive: " + p_path);
+ }
+ if (unzOpenCurrentFile(uzf) != UNZ_OK) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "Could not open file within zip archive.");
+ }
+
+ unz_file_info info;
+ unzGetCurrentFileInfo(uzf, &info, NULL, 0, NULL, 0, NULL, 0);
+ PackedByteArray data;
+ data.resize(info.uncompressed_size);
+
+ uint8_t *w = data.ptrw();
+ unzReadCurrentFile(uzf, &w[0], info.uncompressed_size);
+
+ unzCloseCurrentFile(uzf);
+ return data;
+}
+
+ZIPReader::ZIPReader() {}
+
+ZIPReader::~ZIPReader() {
+ if (fa.is_valid()) {
+ close();
+ }
+}
+
+void ZIPReader::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("open", "path"), &ZIPReader::open);
+ ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close);
+ ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files);
+ ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true)));
+}
diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h
new file mode 100644
index 0000000000..8227208eed
--- /dev/null
+++ b/modules/zip/zip_reader.h
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* zip_reader.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 ZIP_READER_H
+#define ZIP_READER_H
+
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
+
+#include "thirdparty/minizip/unzip.h"
+
+class ZIPReader : public RefCounted {
+ GDCLASS(ZIPReader, RefCounted)
+
+ Ref<FileAccess> fa;
+ unzFile uzf;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Error open(String p_path);
+ Error close();
+
+ PackedStringArray get_files();
+ PackedByteArray read_file(String p_path, bool p_case_sensitive);
+
+ ZIPReader();
+ ~ZIPReader();
+};
+
+#endif // ZIP_READER_H