diff options
71 files changed, 1028 insertions, 723 deletions
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 71bbc271e2..602d0e4d0e 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -21,6 +21,9 @@ <member name="ClassDB" type="ClassDB" setter="" getter=""> The [ClassDB] singleton. </member> + <member name="DisplayServer" type="DisplayServer" setter="" getter=""> + The [DisplayServer] singleton. + </member> <member name="Engine" type="Engine" setter="" getter=""> The [Engine] singleton. </member> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 0c1317f19d..4190cbe6b9 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -29,14 +29,14 @@ </description> </method> <method name="get_overlapping_areas" qualifiers="const"> - <return type="Array"> + <return type="Area2D[]"> </return> <description> Returns a list of intersecting [Area2D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. </description> </method> <method name="get_overlapping_bodies" qualifiers="const"> - <return type="Array"> + <return type="Node2D[]"> </return> <description> Returns a list of intersecting [PhysicsBody2D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index 1adfc878e2..a94cecd879 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -28,14 +28,14 @@ </description> </method> <method name="get_overlapping_areas" qualifiers="const"> - <return type="Array"> + <return type="Area3D[]"> </return> <description> Returns a list of intersecting [Area3D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. </description> </method> <method name="get_overlapping_bodies" qualifiers="const"> - <return type="Array"> + <return type="Node3D[]"> </return> <description> Returns a list of intersecting [PhysicsBody3D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead. diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 9e742ea581..b45716544a 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -22,6 +22,8 @@ m.mesh = arr_mesh [/codeblock] The [MeshInstance3D] is ready to be added to the [SceneTree] to be shown. + See also [ImmediateGeometry3D], [MeshDataTool] and [SurfaceTool] for procedural geometry generation. + [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> <link>https://docs.godotengine.org/en/latest/tutorials/content/procedural_geometry/arraymesh.html</link> @@ -56,7 +58,6 @@ Surfaces are created to be rendered using a [code]primitive[/code], which may be any of the types defined in [enum Mesh.PrimitiveType]. (As a note, when using indices, it is recommended to only use points, lines or triangles.) [method Mesh.get_surface_count] will become the [code]surf_idx[/code] for this new surface. The [code]arrays[/code] argument is an array of arrays. See [enum ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array or be empty, except for [constant ARRAY_INDEX] if it is used. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data, and the index array defines the order of the vertices. - Godot uses clockwise winding order for front faces of triangle primitive modes. </description> </method> <method name="clear_blend_shapes"> diff --git a/doc/classes/CameraEffects.xml b/doc/classes/CameraEffects.xml index 23f0a1c7af..ea9ab85b80 100644 --- a/doc/classes/CameraEffects.xml +++ b/doc/classes/CameraEffects.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CameraEffects" inherits="Resource" version="4.0"> <brief_description> + Contains camera-specific effects such as depth of field and exposure override. </brief_description> <description> + Contains camera-specific effects such as depth of field and exposure override. + See also [Environment] for general 3D environment settings. </description> <tutorials> </tutorials> @@ -10,22 +13,31 @@ </methods> <members> <member name="dof_blur_amount" type="float" setter="set_dof_blur_amount" getter="get_dof_blur_amount" default="0.1"> + The amount of blur for both near and far depth-of-field effects. The amount of blur increases the radius of the blur effect, making the affected area blurrier. However, If the amount is too high, you might start to see lines appearing, especially when using a low quality blur. </member> <member name="dof_blur_far_distance" type="float" setter="set_dof_blur_far_distance" getter="get_dof_blur_far_distance" default="10.0"> + The distance from the camera where the far blur effect affects the rendering. </member> <member name="dof_blur_far_enabled" type="bool" setter="set_dof_blur_far_enabled" getter="is_dof_blur_far_enabled" default="false"> + If [code]true[/code], enables the depth-of-field far blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no far away objects. </member> <member name="dof_blur_far_transition" type="float" setter="set_dof_blur_far_transition" getter="get_dof_blur_far_transition" default="5.0"> + The length of the transition between the no-blur area and far blur. </member> <member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0"> + Distance from the camera where the near blur effect affects the rendering. </member> <member name="dof_blur_near_enabled" type="bool" setter="set_dof_blur_near_enabled" getter="is_dof_blur_near_enabled" default="false"> + If [code]true[/code], enables the depth-of-field near blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no nearby objects. </member> <member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0"> + The length of the transition between the near blur and no-blur area. </member> <member name="override_exposure" type="float" setter="set_override_exposure" getter="get_override_exposure" default="1.0"> + The exposure override value to use. Higher values will result in a brighter scene. Only effective if [member override_exposure_enable] is [code]true[/code]. </member> <member name="override_exposure_enable" type="bool" setter="set_override_exposure_enabled" getter="is_override_exposure_enabled" default="false"> + If [code]true[/code], overrides the manual or automatic exposure defined in the [Environment] with the value in [member override_exposure]. </member> </members> <constants> diff --git a/doc/classes/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml index 30e1de1f5e..9bb51af2d0 100644 --- a/doc/classes/EditorFileSystem.xml +++ b/doc/classes/EditorFileSystem.xml @@ -5,6 +5,7 @@ </brief_description> <description> This object holds information of all resources in the filesystem, their types, etc. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_resource_filesystem]. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 99a78d147c..2f62fe9e40 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -5,6 +5,7 @@ </brief_description> <description> The editor inspector is by default located on the right-hand side of the editor. It's used to edit the properties of the selected node. For example, you can select a node such as the Sprite2D then edit its transform through the inspector tool. The editor inspector is an essential tool in the game development workflow. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_inspector]. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 5e76f90fc4..499c3b8271 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -5,6 +5,7 @@ </brief_description> <description> EditorInterface gives you control over Godot editor's window. It allows customizing the window, saving and (re-)loading scenes, rendering mesh previews, inspecting and editing resources and objects, and provides access to [EditorSettings], [EditorFileSystem], [EditorResourcePreview], [ScriptEditor], the editor viewport, and information about scenes. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorPlugin.get_editor_interface]. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml index aac75c5c8e..0c1d969518 100644 --- a/doc/classes/EditorResourcePreview.xml +++ b/doc/classes/EditorResourcePreview.xml @@ -5,6 +5,7 @@ </brief_description> <description> This object is used to generate previews for resources of files. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_resource_previewer]. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorSelection.xml b/doc/classes/EditorSelection.xml index caafd3c15f..1ff9744b70 100644 --- a/doc/classes/EditorSelection.xml +++ b/doc/classes/EditorSelection.xml @@ -5,6 +5,7 @@ </brief_description> <description> This object manages the SceneTree selection in the editor. + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_selection]. </description> <tutorials> </tutorials> @@ -26,7 +27,7 @@ </description> </method> <method name="get_selected_nodes"> - <return type="Array"> + <return type="Node[]"> </return> <description> Gets the list of selected nodes. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 73ef807c5f..19921ff5c8 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -11,6 +11,7 @@ settings.get(prop) list_of_settings = settings.get_property_list() [/codeblock] + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_editor_settings]. </description> <tutorials> </tutorials> diff --git a/doc/classes/ImmediateGeometry3D.xml b/doc/classes/ImmediateGeometry3D.xml index 1c0831c922..d2d663847f 100644 --- a/doc/classes/ImmediateGeometry3D.xml +++ b/doc/classes/ImmediateGeometry3D.xml @@ -5,6 +5,9 @@ </brief_description> <description> Draws simple geometry from code. Uses a drawing mode similar to OpenGL 1.x. + See also [ArrayMesh], [MeshDataTool] and [SurfaceTool] for procedural geometry generation. + [b]Note:[/b] ImmediateGeometry3D is best suited to small amounts of mesh data that change every frame. It will be slow when handling large amounts of mesh data. If mesh data doesn't change often, use [ArrayMesh], [MeshDataTool] or [SurfaceTool] instead. + [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> </tutorials> diff --git a/doc/classes/MeshDataTool.xml b/doc/classes/MeshDataTool.xml index 81ff5969e3..dcc3bbf2a6 100644 --- a/doc/classes/MeshDataTool.xml +++ b/doc/classes/MeshDataTool.xml @@ -17,6 +17,8 @@ mesh.surface_remove(0) mdt.commit_to_surface(mesh) [/codeblock] + See also [ArrayMesh], [ImmediateGeometry3D] and [SurfaceTool] for procedural geometry generation. + [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> </tutorials> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 8c588f0373..1c745de7f0 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -221,7 +221,7 @@ </description> </method> <method name="get_children" qualifiers="const"> - <return type="Array"> + <return type="Node[]"> </return> <description> Returns an array of references to node's children. diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index 28d6ce7048..6afbd1ee8e 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -20,7 +20,7 @@ </description> </method> <method name="get_collision_exceptions"> - <return type="Array"> + <return type="PhysicsBody2D[]"> </return> <description> Returns an array of nodes that were added as collision exceptions for this body. diff --git a/doc/classes/PhysicsBody3D.xml b/doc/classes/PhysicsBody3D.xml index f0ba2a7f5f..2301a07a5c 100644 --- a/doc/classes/PhysicsBody3D.xml +++ b/doc/classes/PhysicsBody3D.xml @@ -20,7 +20,7 @@ </description> </method> <method name="get_collision_exceptions"> - <return type="Array"> + <return type="PhysicsBody3D[]"> </return> <description> Returns an array of nodes that were added as collision exceptions for this body. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 4eb089e511..feded45c27 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -806,6 +806,18 @@ <member name="memory/limits/multithreaded_server/rid_pool_prealloc" type="int" setter="" getter="" default="60"> This is used by servers when used in multi-threading mode (servers and visual). RIDs are preallocated to avoid stalling the server requesting them on threads. If servers get stalled too often when loading resources in a thread, increase this number. </member> + <member name="mono/debugger_agent/port" type="int" setter="" getter="" default="23685"> + </member> + <member name="mono/debugger_agent/wait_for_debugger" type="bool" setter="" getter="" default="false"> + </member> + <member name="mono/debugger_agent/wait_timeout" type="int" setter="" getter="" default="3000"> + </member> + <member name="mono/profiler/args" type="String" setter="" getter="" default=""log:calls,alloc,sample,output=output.mlpd""> + </member> + <member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false"> + </member> + <member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0"> + </member> <member name="network/limits/debugger/max_chars_per_second" type="int" setter="" getter="" default="32768"> Maximum amount of characters allowed to send as output from the debugger. Over this value, content is dropped. This helps not to stall the debugger connection. </member> diff --git a/doc/classes/RDPipelineColorBlendState.xml b/doc/classes/RDPipelineColorBlendState.xml index 7cb31f6986..adc6f1f6a3 100644 --- a/doc/classes/RDPipelineColorBlendState.xml +++ b/doc/classes/RDPipelineColorBlendState.xml @@ -7,40 +7,10 @@ <tutorials> </tutorials> <methods> - <method name="add_attachment"> - <return type="void"> - </return> - <argument index="0" name="atachment" type="RDPipelineColorBlendStateAttachment"> - </argument> - <description> - </description> - </method> - <method name="add_blend_mix_attachment"> - <return type="void"> - </return> - <description> - </description> - </method> - <method name="add_no_blend_attachment"> - <return type="void"> - </return> - <description> - </description> - </method> - <method name="clear_attachments"> - <return type="void"> - </return> - <description> - </description> - </method> - <method name="get_attachments" qualifiers="const"> - <return type="Array"> - </return> - <description> - </description> - </method> </methods> <members> + <member name="attachments" type="RDPipelineColorBlendStateAttachment[]" setter="set_attachments" getter="get_attachments" default="[ ]"> + </member> <member name="blend_constant" type="Color" setter="set_blend_constant" getter="get_blend_constant" default="Color( 0, 0, 0, 1 )"> </member> <member name="enable_logic_op" type="bool" setter="set_enable_logic_op" getter="get_enable_logic_op" default="false"> diff --git a/doc/classes/RDPipelineColorBlendStateAttachment.xml b/doc/classes/RDPipelineColorBlendStateAttachment.xml index a4caa622aa..7f118b5f0b 100644 --- a/doc/classes/RDPipelineColorBlendStateAttachment.xml +++ b/doc/classes/RDPipelineColorBlendStateAttachment.xml @@ -7,6 +7,12 @@ <tutorials> </tutorials> <methods> + <method name="set_as_mix"> + <return type="void"> + </return> + <description> + </description> + </method> </methods> <members> <member name="alpha_blend_op" type="int" setter="set_alpha_blend_op" getter="get_alpha_blend_op" enum="RenderingDevice.BlendOperation" default="0"> diff --git a/doc/classes/RDPipelineMultisampleState.xml b/doc/classes/RDPipelineMultisampleState.xml index b936ded4fe..4658c7d9ba 100644 --- a/doc/classes/RDPipelineMultisampleState.xml +++ b/doc/classes/RDPipelineMultisampleState.xml @@ -7,26 +7,6 @@ <tutorials> </tutorials> <methods> - <method name="add_sample_mask"> - <return type="void"> - </return> - <argument index="0" name="mask" type="int"> - </argument> - <description> - </description> - </method> - <method name="clear_sample_masks"> - <return type="void"> - </return> - <description> - </description> - </method> - <method name="get_sample_masks" qualifiers="const"> - <return type="PackedInt64Array"> - </return> - <description> - </description> - </method> </methods> <members> <member name="enable_alpha_to_coverage" type="bool" setter="set_enable_alpha_to_coverage" getter="get_enable_alpha_to_coverage" default="false"> @@ -39,6 +19,8 @@ </member> <member name="sample_count" type="int" setter="set_sample_count" getter="get_sample_count" enum="RenderingDevice.TextureSamples" default="0"> </member> + <member name="sample_masks" type="int[]" setter="set_sample_masks" getter="get_sample_masks" default="[ ]"> + </member> </members> <constants> </constants> diff --git a/doc/classes/RDVertexDescription.xml b/doc/classes/RDVertexAttribute.xml index 90e37ee023..56fe40b51d 100644 --- a/doc/classes/RDVertexDescription.xml +++ b/doc/classes/RDVertexAttribute.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RDVertexDescription" inherits="Reference" version="4.0"> +<class name="RDVertexAttribute" inherits="Reference" version="4.0"> <brief_description> </brief_description> <description> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index 6d68eafc70..8a44d213e8 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -296,7 +296,7 @@ <method name="framebuffer_format_create"> <return type="int"> </return> - <argument index="0" name="attachments" type="Array"> + <argument index="0" name="attachments" type="RDAttachmentFormat[]"> </argument> <description> </description> @@ -386,9 +386,9 @@ </argument> <argument index="1" name="format" type="int" enum="RenderingDevice.IndexBufferFormat"> </argument> - <argument index="2" name="data" type="PackedByteArray"> + <argument index="2" name="data" type="PackedByteArray" default="PackedByteArray( )"> </argument> - <argument index="3" name="arg3" type="bool" default="PackedByteArray( )"> + <argument index="3" name="arg3" type="bool" default="false"> </argument> <description> </description> @@ -576,7 +576,7 @@ </argument> <argument index="1" name="view" type="RDTextureView"> </argument> - <argument index="2" name="data" type="Array" default="[ ]"> + <argument index="2" name="data" type="PackedByteArray[]" default="[ ]"> </argument> <description> </description> @@ -712,7 +712,7 @@ <method name="vertex_format_create"> <return type="int"> </return> - <argument index="0" name="vertex_descriptions" type="Array"> + <argument index="0" name="vertex_descriptions" type="RDVertexAttribute[]"> </argument> <description> </description> diff --git a/doc/classes/RigidBody2D.xml b/doc/classes/RigidBody2D.xml index e746d7fc96..8379fc5b58 100644 --- a/doc/classes/RigidBody2D.xml +++ b/doc/classes/RigidBody2D.xml @@ -81,7 +81,7 @@ </description> </method> <method name="get_colliding_bodies" qualifiers="const"> - <return type="Array"> + <return type="Node2D[]"> </return> <description> Returns a list of the bodies colliding with this one. Use [member contacts_reported] to set the maximum number reported. You must also set [member contact_monitor] to [code]true[/code]. diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 10d6e5f578..f0ad781f77 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -4,6 +4,7 @@ Godot editor's script editor. </brief_description> <description> + [b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_script_editor]. </description> <tutorials> </tutorials> diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 08404fb467..4460b519fc 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -157,7 +157,7 @@ <method name="physical_bones_start_simulation"> <return type="void"> </return> - <argument index="0" name="bones" type="Array" default="[ ]"> + <argument index="0" name="bones" type="StringName[]" default="[ ]"> </argument> <description> </description> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 4304a8df5e..eeb6b6cd9d 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -15,6 +15,8 @@ The above [SurfaceTool] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calling [method add_uv] or [method add_color], then the last values would be used. Vertex attributes must be passed [b]before[/b] calling [method add_vertex]. Failure to do so will result in an error when committing the vertex information to a mesh. Additionally, the attributes used before the first vertex is added determine the format of the mesh. For example, if you only add UVs to the first vertex, you cannot add color to any of the subsequent vertices. + See also [ArrayMesh], [ImmediateGeometry3D] and [MeshDataTool] for procedural geometry generation. + [b]Note:[/b] Godot uses clockwise [url=https://learnopengl.com/Advanced-OpenGL/Face-culling]winding order[/url] for front faces of triangle primitive modes. </description> <tutorials> </tutorials> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 5b7694b775..bfe6983f06 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -74,14 +74,14 @@ </description> </method> <method name="get_used_cells" qualifiers="const"> - <return type="Array"> + <return type="Vector2i[]"> </return> <description> Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). </description> </method> <method name="get_used_cells_by_id" qualifiers="const"> - <return type="Array"> + <return type="Vector2i[]"> </return> <argument index="0" name="id" type="int"> </argument> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 371b027534..f9320ac55e 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -6,7 +6,7 @@ <description> Tweens are useful for animations requiring a numerical property to be interpolated over a range of values. The name [i]tween[/i] comes from [i]in-betweening[/i], an animation technique where you specify [i]keyframes[/i] and the computer interpolates the frames that appear between them. [Tween] is more suited than [AnimationPlayer] for animations where you don't know the final values in advance. For example, interpolating a dynamically-chosen camera zoom value is best done with a [Tween] node; it would be difficult to do the same thing with an [AnimationPlayer] node. - Here is a brief usage example that causes a 2D node to move smoothly between two positions: + Here is a brief usage example that makes a 2D node move smoothly between two positions: [codeblock] var tween = get_node("Tween") tween.interpolate_property($Node2D, "position", @@ -15,7 +15,8 @@ tween.start() [/codeblock] Many methods require a property name, such as [code]"position"[/code] above. You can find the correct property name by hovering over the property in the Inspector. You can also provide the components of a property directly by using [code]"property:component"[/code] (eg. [code]position:x[/code]), where it would only apply to that particular component. - Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. + Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. + [b][url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url][/b] </description> <tutorials> </tutorials> diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index c6c6cae6c0..5ceab52523 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -973,11 +973,14 @@ def format_table(f, data, remove_empty_columns=False): # type: (TextIO, Iterabl f.write("\n") -def make_type(t, state): # type: (str, State) -> str - if t in state.classes: - return ":ref:`{0}<class_{0}>`".format(t) - print_error("Unresolved type '{}', file: {}".format(t, state.current_class), state) - return t +def make_type(klass, state): # type: (str, State) -> str + link_type = klass + if link_type.endswith("[]"): # Typed array, strip [] to link to contained type. + link_type = link_type[:-2] + if link_type in state.classes: + return ":ref:`{}<class_{}>`".format(klass, link_type) + print_error("Unresolved type '{}', file: {}".format(klass, state.current_class), state) + return klass def make_enum(t, state): # type: (str, State) -> str diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/dummy/texture_loader_dummy.cpp index d92d03eabb..ddd2943720 100644 --- a/drivers/dummy/texture_loader_dummy.cpp +++ b/drivers/dummy/texture_loader_dummy.cpp @@ -67,10 +67,19 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi } void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("png"); - p_extensions->push_back("hdr"); + p_extensions->push_back("bmp"); + p_extensions->push_back("dds"); + p_extensions->push_back("exr"); + p_extensions->push_back("jpeg"); p_extensions->push_back("jpg"); + p_extensions->push_back("hdr"); + p_extensions->push_back("pkm"); + p_extensions->push_back("png"); + p_extensions->push_back("pvr"); + p_extensions->push_back("svg"); + p_extensions->push_back("svgz"); p_extensions->push_back("tga"); + p_extensions->push_back("webp"); } bool ResourceFormatDummyTexture::handles_type(const String &p_type) const { @@ -79,7 +88,22 @@ bool ResourceFormatDummyTexture::handles_type(const String &p_type) const { String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const { String extension = p_path.get_extension().to_lower(); - if (extension == "png" || extension == "hdr" || extension == "jpg" || extension == "tga") + if ( + extension == "bmp" || + extension == "dds" || + extension == "exr" || + extension == "jpeg" || + extension == "jpg" || + extension == "hdr" || + extension == "pkm" || + extension == "png" || + extension == "pvr" || + extension == "svg" || + extension == "svgz" || + extension == "tga" || + extension == "webp") { return "ImageTexture"; + } + return ""; } diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 083b822676..d293abdee3 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -165,16 +165,17 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_n Error VulkanContext::_create_validation_layers() { VkResult err; + const char *instance_validation_layers_alt1[] = { "VK_LAYER_KHRONOS_validation" }; + const char *instance_validation_layers_alt2[] = { "VK_LAYER_LUNARG_standard_validation" }; + const char *instance_validation_layers_alt3[] = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" }; + uint32_t instance_layer_count = 0; - uint32_t validation_layer_count = 0; - const char *instance_validation_layers_alt1[] = { "VK_LAYER_LUNARG_standard_validation" }; - const char *instance_validation_layers_alt2[] = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", - "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", - "VK_LAYER_GOOGLE_unique_objects" }; - VkBool32 validation_found = 0; err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - const char **instance_validation_layers = instance_validation_layers_alt1; + + VkBool32 validation_found = 0; + uint32_t validation_layer_count = 0; + const char **instance_validation_layers = nullptr; if (instance_layer_count > 0) { VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); @@ -183,27 +184,33 @@ Error VulkanContext::_create_validation_layers() { ERR_FAIL_V(ERR_CANT_CREATE); } - validation_found = _check_layers(ARRAY_SIZE(instance_validation_layers_alt1), instance_validation_layers, - instance_layer_count, instance_layers); - if (validation_found) { - enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); - enabled_layers[0] = "VK_LAYER_LUNARG_standard_validation"; - validation_layer_count = 1; - } else { - // use alternative set of validation layers - instance_validation_layers = instance_validation_layers_alt2; - enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); - validation_found = _check_layers(ARRAY_SIZE(instance_validation_layers_alt2), instance_validation_layers, - instance_layer_count, instance_layers); + validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); + instance_validation_layers = instance_validation_layers_alt1; + validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + + // use alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers + if (!validation_found) { validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); - for (uint32_t i = 0; i < validation_layer_count; i++) { - enabled_layers[i] = instance_validation_layers[i]; - } + instance_validation_layers = instance_validation_layers_alt2; + validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); + } + + // use alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers + if (!validation_found) { + validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt3); + instance_validation_layers = instance_validation_layers_alt3; + validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers); } + free(instance_layers); } - if (!validation_found) { + if (validation_found) { + enabled_layer_count = validation_layer_count; + for (uint32_t i = 0; i < validation_layer_count; i++) { + enabled_layers[i] = instance_validation_layers[i]; + } + } else { return ERR_CANT_CREATE; } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index f10e439f10..b7c8769f64 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -754,14 +754,17 @@ public: for (Map<int, List<float>>::Element *E = key_ofs_map.front(); E; E = E->next()) { + int key = 0; for (List<float>::Element *F = E->value().front(); F; F = F->next()) { float key_ofs = F->get(); - if (from != key_ofs) + if (from != key_ofs) { + key++; continue; + } int track = E->key(); - key_ofs_map[track][key_ofs] = to; + key_ofs_map[track][key] = to; if (setting) return; diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 1971abadc4..81a7d85b18 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -149,39 +149,74 @@ void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) { } void ScriptEditorDebugger::_file_selected(const String &p_file) { - Error err; - FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); - if (err != OK) { - ERR_PRINT("Failed to open " + p_file); - return; - } - Vector<String> line; - line.resize(Performance::MONITOR_MAX); + switch (file_dialog_purpose) { + case SAVE_MONITORS_CSV: { + Error err; + FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); - // signatures - for (int i = 0; i < Performance::MONITOR_MAX; i++) { - line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); - } - file->store_csv_line(line); + if (err != OK) { + ERR_PRINT("Failed to open " + p_file); + return; + } + Vector<String> line; + line.resize(Performance::MONITOR_MAX); - // values - List<Vector<float>>::Element *E = perf_history.back(); - while (E) { + // signatures + for (int i = 0; i < Performance::MONITOR_MAX; i++) { + line.write[i] = Performance::get_singleton()->get_monitor_name(Performance::Monitor(i)); + } + file->store_csv_line(line); - Vector<float> &perf_data = E->get(); - for (int i = 0; i < perf_data.size(); i++) { + // values + List<Vector<float>>::Element *E = perf_history.back(); + while (E) { - line.write[i] = String::num_real(perf_data[i]); - } - file->store_csv_line(line); - E = E->prev(); - } - file->store_string("\n"); + Vector<float> &perf_data = E->get(); + for (int i = 0; i < perf_data.size(); i++) { + + line.write[i] = String::num_real(perf_data[i]); + } + file->store_csv_line(line); + E = E->prev(); + } + file->store_string("\n"); + + Vector<Vector<String>> profiler_data = profiler->get_data_as_csv(); + for (int i = 0; i < profiler_data.size(); i++) { + file->store_csv_line(profiler_data[i]); + } + } break; + case SAVE_VRAM_CSV: { + Error err; + FileAccessRef file = FileAccess::open(p_file, FileAccess::WRITE, &err); - Vector<Vector<String>> profiler_data = profiler->get_data_as_csv(); - for (int i = 0; i < profiler_data.size(); i++) { - file->store_csv_line(profiler_data[i]); + if (err != OK) { + ERR_PRINT("Failed to open " + p_file); + return; + } + + Vector<String> headers; + headers.resize(vmem_tree->get_columns()); + for (int i = 0; i < vmem_tree->get_columns(); ++i) { + headers.write[i] = vmem_tree->get_column_title(i); + } + file->store_csv_line(headers); + + if (vmem_tree->get_root()) { + TreeItem *ti = vmem_tree->get_root()->get_children(); + while (ti) { + Vector<String> values; + values.resize(vmem_tree->get_columns()); + for (int i = 0; i < vmem_tree->get_columns(); ++i) { + values.write[i] = ti->get_text(i); + } + file->store_csv_line(values); + + ti = ti->get_next(); + } + } + } break; } } @@ -233,6 +268,15 @@ void ScriptEditorDebugger::_video_mem_request() { _put_msg("core:memory", Array()); } +void ScriptEditorDebugger::_video_mem_export() { + + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog->clear_filters(); + file_dialog_purpose = SAVE_VRAM_CSV; + file_dialog->popup_centered_ratio(); +} + Size2 ScriptEditorDebugger::get_minimum_size() const { Size2 ms = MarginContainer::get_minimum_size(); @@ -766,6 +810,7 @@ void ScriptEditorDebugger::_notification(int p_what) { error_tree->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_error_selected)); error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated)); vmem_refresh->set_icon(get_theme_icon("Reload", "EditorIcons")); + vmem_export->set_icon(get_theme_icon("Save", "EditorIcons")); reason->add_theme_color_override("font_color", get_theme_color("error_color", "Editor")); @@ -840,6 +885,7 @@ void ScriptEditorDebugger::_notification(int p_what) { dobreak->set_icon(get_theme_icon("Pause", "EditorIcons")); docontinue->set_icon(get_theme_icon("DebugContinue", "EditorIcons")); vmem_refresh->set_icon(get_theme_icon("Reload", "EditorIcons")); + vmem_export->set_icon(get_theme_icon("Save", "EditorIcons")); } break; } } @@ -981,6 +1027,7 @@ void ScriptEditorDebugger::_export_csv() { file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_purpose = SAVE_MONITORS_CSV; file_dialog->popup_centered_ratio(); } @@ -1701,8 +1748,12 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { vmem_hb->add_child(vmem_total); vmem_refresh = memnew(ToolButton); vmem_hb->add_child(vmem_refresh); + vmem_export = memnew(ToolButton); + vmem_export->set_tooltip(TTR("Export list to a CSV file")); + vmem_hb->add_child(vmem_export); vmem_vb->add_child(vmem_hb); vmem_refresh->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_video_mem_request)); + vmem_export->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_video_mem_export)); VBoxContainer *vmmc = memnew(VBoxContainer); vmem_tree = memnew(Tree); diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 9cb7dc2edf..a08a7c67c2 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -88,6 +88,11 @@ private: PopupMenu *item_menu; EditorFileDialog *file_dialog; + enum FileDialogPurpose { + SAVE_MONITORS_CSV, + SAVE_VRAM_CSV, + }; + FileDialogPurpose file_dialog_purpose; int error_count; int warning_count; @@ -121,6 +126,7 @@ private: Tree *vmem_tree; Button *vmem_refresh; + Button *vmem_export; LineEdit *vmem_total; Tree *stack_dump; @@ -160,6 +166,7 @@ private: void _remote_object_property_updated(ObjectID p_id, const String &p_property); void _video_mem_request(); + void _video_mem_export(); int _get_node_path_cache(const NodePath &p_path); diff --git a/editor/icons/Decal.svg b/editor/icons/Decal.svg new file mode 100644 index 0000000000..fc7bfb8e2c --- /dev/null +++ b/editor/icons/Decal.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2c-3.3137085 0-6 2.6862915-6 6 0 2.220299 1.2092804 4.153789 3.0019531 5.191406l8.9082029-6.1894529c-.476307-2.8374399-2.937354-5.0019531-5.910156-5.0019531z" fill="#fc9c9c"/><path d="m5.001954 13.191406 8.908202-6.1894529c-.882819-.510985-1.904638-.808594-2.998046-.808594-3.3137079 0-6 2.686292-6 5.9999999 0 .340906.03522.672663.08984.998047z" fill="#ff5d5d"/><path d="m13.910156 7.0019531-8.908202 6.1894529c.882819.510985 1.904638.808594 2.998046.808594 3.313708 0 6-2.686292 6-5.9999999 0-.340906-.03522-.672663-.08984-.998047z" fill="#fc9c9c" fill-opacity=".392157"/></svg>
\ No newline at end of file diff --git a/misc/hooks/pre-commit-black b/misc/hooks/pre-commit-black index 2dcc2e8cf1..76d97294da 100755 --- a/misc/hooks/pre-commit-black +++ b/misc/hooks/pre-commit-black @@ -6,7 +6,7 @@ ################################################################## # SETTINGS # Set path to black binary. -BLACK=`which black` +BLACK=`which black 2>/dev/null` BLACK_OPTIONS="-l 120" # Remove any older patches from previous commits. Set to true or false. @@ -18,13 +18,22 @@ FILE_EXTS="py" # Use pygmentize instead of cat to parse diff with highlighting. # Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) -PYGMENTIZE=`which pygmentize` +PYGMENTIZE=`which pygmentize 2>/dev/null` if [ ! -z "$PYGMENTIZE" ]; then READER="pygmentize -l diff" else READER=cat fi +# Path to zenity +ZENITY=`which zenity 2>/dev/null` + +# Path to xmessage +XMSG=`which xmessage 2>/dev/null` + +# Path to powershell (Windows only) +PWSH=`which powershell 2>/dev/null` + ################################################################## # There should be no need to change anything below this line. @@ -53,6 +62,19 @@ else fi if [ ! -x "$BLACK" ] ; then + if [ ! -t 1 ] ; then + if [ -x "$ZENITY" ] ; then + $ZENITY --error --title="Error" --text="Error: black executable not found." + exit 1 + elif [ -x "$XMSG" ] ; then + $XMSG -center -title "Error" "Error: black executable not found." + exit 1 + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: black executable not found." + exit 1 + fi + fi printf "Error: black executable not found.\n" printf "Set the correct path in $(canonicalize_filename "$0").\n" exit 1 @@ -99,14 +121,62 @@ fi # a patch has been created, notify the user and exit printf "\nThe following differences were found between the code to commit " printf "and the black formatter rules:\n\n" -$READER "$patch" -printf "\n" -# Allows us to read user input below, assigns stdin to keyboard -exec < /dev/tty +if [ -t 1 ] ; then + $READER "$patch" + printf "\n" + # Allows us to read user input below, assigns stdin to keyboard + exec < /dev/tty + terminal="1" +else + cat "$patch" + printf "\n" + # Allows non zero zenity/powershell output + set +e + terminal="0" +fi while true; do - read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + if [ $terminal = "0" ] ; then + if [ -x "$ZENITY" ] ; then + ans=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage") + if [ "$?" = "0" ] ; then + yn="Y" + else + if [ "$ans" = "Apply and stage" ] ; then + yn="S" + else + yn="N" + fi + fi + elif [ -x "$XMSG" ] ; then + $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + else + printf "Error: zenity, xmessage, or powershell executable not found.\n" + exit 1 + fi + else + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + fi case $yn in [Yy] ) git apply $patch; printf "The patch was applied. You can now stage the changes and commit again.\n\n"; diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index c5cf4ecbb1..4e1fbdeb20 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -16,7 +16,7 @@ ################################################################## # SETTINGS # Set path to clang-format binary. -CLANG_FORMAT=`which clang-format` +CLANG_FORMAT=`which clang-format 2>/dev/null` # Remove any older patches from previous commits. Set to true or false. DELETE_OLD_PATCHES=false @@ -31,13 +31,22 @@ FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl" # Use pygmentize instead of cat to parse diff with highlighting. # Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac) -PYGMENTIZE=`which pygmentize` +PYGMENTIZE=`which pygmentize 2>/dev/null` if [ ! -z "$PYGMENTIZE" ]; then READER="pygmentize -l diff" else READER=cat fi +# Path to zenity +ZENITY=`which zenity 2>/dev/null` + +# Path to xmessage +XMSG=`which xmessage 2>/dev/null` + +# Path to powershell (Windows only) +PWSH=`which powershell 2>/dev/null` + ################################################################## # There should be no need to change anything below this line. @@ -66,6 +75,19 @@ else fi if [ ! -x "$CLANG_FORMAT" ] ; then + if [ ! -t 1 ] ; then + if [ -x "$ZENITY" ] ; then + $ZENITY --error --title="Error" --text="Error: clang-format executable not found." + exit 1 + elif [ -x "$XMSG" ] ; then + $XMSG -center -title "Error" "Error: clang-format executable not found." + exit 1 + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: clang-format executable not found." + exit 1 + fi + fi printf "Error: clang-format executable not found.\n" printf "Set the correct path in $(canonicalize_filename "$0").\n" exit 1 @@ -117,14 +139,62 @@ fi # a patch has been created, notify the user and exit printf "\nThe following differences were found between the code to commit " printf "and the clang-format rules:\n\n" -$READER "$patch" -printf "\n" -# Allows us to read user input below, assigns stdin to keyboard -exec < /dev/tty +if [ -t 1 ] ; then + $READER "$patch" + printf "\n" + # Allows us to read user input below, assigns stdin to keyboard + exec < /dev/tty + terminal="1" +else + cat "$patch" + printf "\n" + # Allows non zero zenity/powershell output + set +e + terminal="0" +fi while true; do - read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + if [ $terminal = "0" ] ; then + if [ -x "$ZENITY" ] ; then + ans=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage") + if [ "$?" = "0" ] ; then + yn="Y" + else + if [ "$ans" = "Apply and stage" ] ; then + yn="S" + else + yn="N" + fi + fi + elif [ -x "$XMSG" ] ; then + $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then + winmessage="$(canonicalize_filename "./.git/hooks/winmessage.ps1")" + $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?" + ans=$? + if [ "$ans" = "100" ] ; then + yn="Y" + elif [ "$ans" = "200" ] ; then + yn="S" + else + yn="N" + fi + else + printf "Error: zenity, xmessage, or powershell executable not found.\n" + exit 1 + fi + else + read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn + fi case $yn in [Yy] ) git apply $patch; printf "The patch was applied. You can now stage the changes and commit again.\n\n"; diff --git a/misc/hooks/winmessage.ps1 b/misc/hooks/winmessage.ps1 new file mode 100755 index 0000000000..3672579544 --- /dev/null +++ b/misc/hooks/winmessage.ps1 @@ -0,0 +1,103 @@ +Param ( + [string]$file = "", + [string]$text = "", + [string]$buttons = "OK:0", + [string]$default = "", + [switch]$nearmouse = $false, + [switch]$center = $false, + [string]$geometry = "", + [int32]$timeout = 0, + [string]$title = "Message" +) +Add-Type -assembly System.Windows.Forms + +$global:Result = 0 + +$main_form = New-Object System.Windows.Forms.Form +$main_form.Text = $title + +$geometry_data = $geometry.Split("+") +if ($geometry_data.Length -ge 1) { + $size_data = $geometry_data[0].Split("x") + if ($size_data.Length -eq 2) { + $main_form.Width = $size_data[0] + $main_form.Height = $size_data[1] + } +} +if ($geometry_data.Length -eq 3) { + $main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual + $main_form.Location = New-Object System.Drawing.Point($geometry_data[1], $geometry_data[2]) +} +if ($nearmouse) { + $main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual + $main_form.Location = System.Windows.Forms.Cursor.Position +} +if ($center) { + $main_form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen +} + +$main_form.SuspendLayout() + +$button_panel = New-Object System.Windows.Forms.FlowLayoutPanel +$button_panel.SuspendLayout() +$button_panel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft +$button_panel.Dock = [System.Windows.Forms.DockStyle]::Bottom +$button_panel.Autosize = $true + +if ($file -ne "") { + $text = [IO.File]::ReadAllText($file).replace("`n", "`r`n") +} + +if ($text -ne "") { + $text_box = New-Object System.Windows.Forms.TextBox + $text_box.Multiline = $true + $text_box.ReadOnly = $true + $text_box.Autosize = $true + $text_box.Text = $text + $text_box.Select(0,0) + $text_box.Dock = [System.Windows.Forms.DockStyle]::Fill + $main_form.Controls.Add($text_box) +} + +$buttons_array = $buttons.Split(",") +foreach ($button in $buttons_array) { + $button_data = $button.Split(":") + $button_ctl = New-Object System.Windows.Forms.Button + if ($button_data.Length -eq 2) { + $button_ctl.Tag = $button_data[1] + } else { + $button_ctl.Tag = 100 + $buttons_array.IndexOf($button) + } + if ($default -eq $button_data[0]) { + $main_form.AcceptButton = $button_ctl + } + $button_ctl.Autosize = $true + $button_ctl.Text = $button_data[0] + $button_ctl.Add_Click( + { + Param($sender) + $global:Result = $sender.Tag + $main_form.Close() + } + ) + $button_panel.Controls.Add($button_ctl) +} +$main_form.Controls.Add($button_panel) + +$button_panel.ResumeLayout($false) +$main_form.ResumeLayout($false) + +if ($timeout -gt 0) { + $timer = New-Object System.Windows.Forms.Timer + $timer.Add_Tick( + { + $global:Result = 0 + $main_form.Close() + } + ) + $timer.Interval = $timeout + $timer.Start() +} +$dlg_res = $main_form.ShowDialog() + +[Environment]::Exit($global:Result) diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 221cce205a..7ad0682637 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2150,7 +2150,7 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p } const GDScriptParser::ClassNode *clss = p_context._class; - bool _static = !p_context.function || p_context.function->_static; + bool _static = p_context.function && p_context.function->_static; while (clss) { GDScriptCompletionContext c = p_context; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index ca4d6f6de9..4e0891921e 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -337,15 +337,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } if (!argument_types[i].is_type(*p_args[i], true)) { - if (argument_types[i].is_type(Variant(), true)) { - memnew_placement(&stack[i], Variant); - continue; - } else { - r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; - return Variant(); - } + r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_err.argument = i; + r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + return Variant(); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a83416375c..c74d8cfbb0 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -746,6 +746,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s if (tokenizer->get_token() == GDScriptTokenizer::TK_CURSOR) { _make_completable_call(0); completion_node = op; + + if (op->arguments[0]->type == GDScriptParser::Node::Type::TYPE_BUILT_IN_FUNCTION) { + BuiltInFunctionNode *bn = static_cast<BuiltInFunctionNode *>(op->arguments[0]); + if (bn->function == GDScriptFunctions::Function::RESOURCE_LOAD) { + completion_type = COMPLETION_RESOURCE_PATH; + } + } } if (!replaced) { if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) @@ -2794,6 +2801,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } + _mark_line_as_safe(line); NewLineNode *nl2 = alloc_node<NewLineNode>(); nl2->line = line; p_block->statements.push_back(nl2); @@ -3307,6 +3315,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } + int assert_line = tokenizer->get_token_line(); + tokenizer->advance(); Vector<Node *> args; @@ -3320,8 +3330,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { return; } +#ifdef DEBUG_ENABLED + // Mark as safe, let type check mark as unsafe if needed + _mark_line_as_safe(assert_line); + _reduce_node_type(args[0]); +#endif AssertNode *an = alloc_node<AssertNode>(); an->condition = _reduce_expression(args[0], p_static); + an->line = assert_line; if (args.size() == 2) { an->message = _reduce_expression(args[1], p_static); diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs new file mode 100644 index 0000000000..85760a3705 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace GodotTools.Core +{ + public static class FileUtils + { + public static void SaveBackupCopy(string filePath) + { + string backupPathBase = filePath + ".old"; + string backupPath = backupPathBase; + + const int maxAttempts = 5; + int attempt = 1; + + while (File.Exists(backupPath) && attempt <= maxAttempts) + { + backupPath = backupPathBase + "." + (attempt); + attempt++; + } + + if (attempt > maxAttempts + 1) + return; + + File.Copy(filePath, backupPath, overwrite: true); + } + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj index 2c35ef540a..c9ea7d3a2c 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj @@ -31,6 +31,7 @@ <Reference Include="System" /> </ItemGroup> <ItemGroup> + <Compile Include="FileUtils.cs" /> <Compile Include="ProcessExtensions.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="StringExtensions.cs" /> diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs index 9afd9adeb1..6f318aab4a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs @@ -153,7 +153,12 @@ EndProject"; var result = regex.Replace(input,m => dict[m.Value]); if (result != input) + { + // Save a copy of the solution before replacing it + FileUtils.SaveBackupCopy(slnPath); + File.WriteAllText(slnPath, result); + } } } } diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index 1776b46e6a..f2ebef1a7d 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -9,8 +9,28 @@ using Microsoft.Build.Construction; namespace GodotTools.ProjectEditor { + public sealed class MSBuildProject + { + public ProjectRootElement Root { get; } + + public bool HasUnsavedChanges { get; set; } + + public void Save() => Root.Save(); + + public MSBuildProject(ProjectRootElement root) + { + Root = root; + } + } + public static class ProjectUtils { + public static MSBuildProject Open(string path) + { + var root = ProjectRootElement.Open(path); + return root != null ? new MSBuildProject(root) : null; + } + public static void AddItemToProjectChecked(string projectPath, string itemType, string include) { var dir = Directory.GetParent(projectPath).FullName; @@ -43,7 +63,6 @@ namespace GodotTools.ProjectEditor public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include) { - var dir = Directory.GetParent(projectPath).FullName; var root = ProjectRootElement.Open(projectPath); Debug.Assert(root != null); @@ -150,12 +169,9 @@ namespace GodotTools.ProjectEditor } /// Simple function to make sure the Api assembly references are configured correctly - public static void FixApiHintPath(string projectPath) + public static void FixApiHintPath(MSBuildProject project) { - var root = ProjectRootElement.Open(projectPath); - Debug.Assert(root != null); - - bool dirty = false; + var root = project.Root; void AddPropertyIfNotPresent(string name, string condition, string value) { @@ -170,7 +186,7 @@ namespace GodotTools.ProjectEditor } root.AddProperty(name, value).Condition = " " + condition + " "; - dirty = true; + project.HasUnsavedChanges = true; } AddPropertyIfNotPresent(name: "ApiConfiguration", @@ -212,7 +228,7 @@ namespace GodotTools.ProjectEditor } referenceWithHintPath.AddMetadata("HintPath", hintPath); - dirty = true; + project.HasUnsavedChanges = true; return; } @@ -221,14 +237,14 @@ namespace GodotTools.ProjectEditor { // Found a Reference item without a HintPath referenceWithoutHintPath.AddMetadata("HintPath", hintPath); - dirty = true; + project.HasUnsavedChanges = true; return; } } // Found no Reference item at all. Add it. root.AddItem("Reference", referenceName).Condition = " " + condition + " "; - dirty = true; + project.HasUnsavedChanges = true; } const string coreProjectName = "GodotSharp"; @@ -242,17 +258,11 @@ namespace GodotTools.ProjectEditor SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath); SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath); - - if (dirty) - root.Save(); } - public static void MigrateFromOldConfigNames(string projectPath) + public static void MigrateFromOldConfigNames(MSBuildProject project) { - var root = ProjectRootElement.Open(projectPath); - Debug.Assert(root != null); - - bool dirty = false; + var root = project.Root; bool hasGodotProjectGeneratorVersion = false; bool foundOldConfiguration = false; @@ -267,7 +277,7 @@ namespace GodotTools.ProjectEditor { configItem.Value = "Debug"; foundOldConfiguration = true; - dirty = true; + project.HasUnsavedChanges = true; } } @@ -275,7 +285,7 @@ namespace GodotTools.ProjectEditor { root.PropertyGroups.First(g => g.Condition == string.Empty)? .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString()); - dirty = true; + project.HasUnsavedChanges = true; } if (!foundOldConfiguration) @@ -301,7 +311,7 @@ namespace GodotTools.ProjectEditor foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition)) { propertyGroup.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } foreach (var propertyGroup in root.PropertyGroups) @@ -309,14 +319,14 @@ namespace GodotTools.ProjectEditor foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition)) { prop.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } } foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition)) { itemGroup.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } foreach (var itemGroup in root.ItemGroups) @@ -324,7 +334,7 @@ namespace GodotTools.ProjectEditor foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition)) { item.Condition = " " + newCondition + " "; - dirty = true; + project.HasUnsavedChanges = true; } } } @@ -340,10 +350,6 @@ namespace GodotTools.ProjectEditor MigrateConfigurationConditions("Release", "ExportRelease"); MigrateConfigurationConditions("Tools", "Debug"); // Must be last } - - - if (dirty) - root.Save(); } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index d782d4e61b..2ceb4888a2 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -168,13 +168,13 @@ namespace GodotTools.Export // Add dependency assemblies - var dependencies = new Godot.Collections.Dictionary<string, string>(); + 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"); - dependencies[projectDllName] = projectDllSrcPath; + assemblies[projectDllName] = projectDllSrcPath; if (platform == OS.Platforms.Android) { @@ -184,15 +184,15 @@ namespace GodotTools.Export if (!File.Exists(monoAndroidAssemblyPath)) throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath); - dependencies["Mono.Android"] = monoAndroidAssemblyPath; + assemblies["Mono.Android"] = monoAndroidAssemblyPath; } string bclDir = DeterminePlatformBclDir(platform); - var initialDependencies = dependencies.Duplicate(); - internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, bclDir, dependencies); + var initialAssemblies = assemblies.Duplicate(); + internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies); - AddI18NAssemblies(dependencies, bclDir); + AddI18NAssemblies(assemblies, bclDir); string outputDataDir = null; @@ -211,20 +211,32 @@ namespace GodotTools.Export Directory.CreateDirectory(outputDataGameAssembliesDir); } - foreach (var dependency in dependencies) + foreach (var assembly in assemblies) { - string dependSrcPath = dependency.Value; - - if (assembliesInsidePck) - { - string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile()); - AddFile(dependSrcPath, dependDstPath); - } - else + void AddToAssembliesDir(string fileSrcPath) { - string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile()); - File.Copy(dependSrcPath, dependDstPath); + 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); + } } + + string assemblySrcPath = assembly.Value; + + string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null); + string pdbSrcPath = assemblyPathWithoutExtension + ".pdb"; + + AddToAssembliesDir(assemblySrcPath); + + if (File.Exists(pdbSrcPath)) + AddToAssembliesDir(pdbSrcPath); } // AOT compilation @@ -254,7 +266,7 @@ namespace GodotTools.Export ToolchainPath = aotToolchainPath }; - AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, dependencies); + AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies); } } @@ -366,7 +378,7 @@ namespace GodotTools.Export if (PlatformRequiresCustomBcl(platform)) throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}"); - platformBclDir = typeof(object).Assembly.Location; // Use the one we're running on + platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on } } @@ -425,7 +437,7 @@ namespace GodotTools.Export } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialDependencies, - string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies); + 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/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index c9d7dd26f8..c070cb16d9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -1,4 +1,5 @@ using Godot; +using GodotTools.Core; using GodotTools.Export; using GodotTools.Utils; using System; @@ -442,13 +443,27 @@ namespace GodotTools { // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath); + + var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath) + ?? throw new Exception("Cannot open C# project"); + + // NOTE: The order in which changes are made to the project is important + // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease - ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath); + ProjectUtils.MigrateFromOldConfigNames(msbuildProject); - // Apply the other fixes after configurations are migrated + // Apply the other fixes only after configurations have been migrated // Make sure the existing project has Api assembly references configured correctly - ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath); + ProjectUtils.FixApiHintPath(msbuildProject); + + if (msbuildProject.HasUnsavedChanges) + { + // Save a copy of the project before replacing it + FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath); + + msbuildProject.Save(); + } } catch (Exception e) { diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 283d4beb8e..c3e7e67ae9 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -231,14 +231,14 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje return err; } -uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies, - MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) { - Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies); +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 dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies); + Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies); - return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, dependencies); + return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies); } MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) { diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 324013e5e2..4126da16be 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -50,13 +50,13 @@ String get_assemblyref_name(MonoImage *p_image, int index) { return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME])); } -Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) { +Error get_assembly_dependencies(GDMonoAssembly *p_assembly, 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++) { String ref_name = get_assemblyref_name(image, i); - if (r_dependencies.has(ref_name)) + if (r_assembly_dependencies.has(ref_name)) continue; GDMonoAssembly *ref_assembly = nullptr; @@ -93,17 +93,17 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'."); // Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir. - r_dependencies[ref_name] = path; + r_assembly_dependencies[ref_name] = path; - Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies); + Error err = get_assembly_dependencies(ref_assembly, 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_dependencies, - const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) { +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); @@ -113,16 +113,16 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencie Vector<String> search_dirs; GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir); - for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) { + for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) { String assembly_name = *key; - String assembly_path = p_initial_dependencies[*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 + "'."); - Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies); + Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies); if (err != OK) return err; } diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h index 36138f81b7..9ab57755de 100644 --- a/modules/mono/editor/godotsharp_export.h +++ b/modules/mono/editor/godotsharp_export.h @@ -41,8 +41,8 @@ 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_dependencies, - const String &p_build_config, const String &p_custom_lib_dir, 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 diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index aba1065498..a963810d63 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -15,10 +15,7 @@ namespace Godot.Collections public override bool IsInvalid { - get - { - return handle == IntPtr.Zero; - } + get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() @@ -43,7 +40,8 @@ namespace Godot.Collections if (collection == null) throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); - MarshalUtils.EnumerableToArray(collection, GetPtr()); + foreach (object element in collection) + Add(element); } internal Array(ArraySafeHandle handle) @@ -272,14 +270,8 @@ namespace Godot.Collections public T this[int index] { - get - { - return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); - } - set - { - objectArray[index] = value; - } + get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } + set { objectArray[index] = value; } } public int IndexOf(T item) @@ -301,18 +293,12 @@ namespace Godot.Collections public int Count { - get - { - return objectArray.Count; - } + get { return objectArray.Count; } } public bool IsReadOnly { - get - { - return objectArray.IsReadOnly; - } + get { return objectArray.IsReadOnly; } } public void Add(T item) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index d72109de92..213fc181c1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -15,10 +15,7 @@ namespace Godot.Collections public override bool IsInvalid { - get - { - return handle == IntPtr.Zero; - } + get { return handle == IntPtr.Zero; } } protected override bool ReleaseHandle() @@ -45,7 +42,8 @@ namespace Godot.Collections if (dictionary == null) throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); - MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); + foreach (DictionaryEntry entry in dictionary) + Add(entry.Key, entry.Value); } internal Dictionary(DictionarySafeHandle handle) @@ -330,14 +328,8 @@ namespace Godot.Collections public TValue this[TKey key] { - get - { - return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); - } - set - { - objectDict[key] = value; - } + get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } + set { objectDict[key] = value; } } public ICollection<TKey> Keys @@ -385,18 +377,12 @@ namespace Godot.Collections public int Count { - get - { - return objectDict.Count; - } + get { return objectDict.Count; } } public bool IsReadOnly { - get - { - return objectDict.IsReadOnly; - } + get { return objectDict.IsReadOnly; } } public void Add(KeyValuePair<TKey, TValue> item) @@ -440,7 +426,8 @@ namespace Godot.Collections public bool Remove(KeyValuePair<TKey, TValue> item) { - return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; + return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + ; } // IEnumerable<KeyValuePair<TKey, TValue>> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs index a1d63a62ef..c59d083080 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -16,10 +16,8 @@ namespace Godot /// <exception cref="System.InvalidOperationException"> /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// </exception> - static bool TypeIsGenericArray(Type type) - { - return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); - } + static bool TypeIsGenericArray(Type type) => + type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); /// <summary> /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> @@ -28,10 +26,20 @@ namespace Godot /// <exception cref="System.InvalidOperationException"> /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. /// </exception> - static bool TypeIsGenericDictionary(Type type) - { - return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); - } + static bool TypeIsGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + + static bool TypeIsSystemGenericList(Type type) => + type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>); + + static bool TypeIsSystemGenericDictionary(Type type) => + type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>); + + static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>); + + static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>); + + static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>); static void ArrayGetElementType(Type arrayType, out Type elementType) { @@ -45,105 +53,6 @@ namespace Godot valueType = genericArgs[1]; } - static bool GenericIEnumerableIsAssignableFromType(Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - return true; - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - return true; - } - - Type baseType = type.BaseType; - - if (baseType == null) - return false; - - return GenericIEnumerableIsAssignableFromType(baseType); - } - - static bool GenericIDictionaryIsAssignableFromType(Type type) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - return true; - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - return true; - } - - Type baseType = type.BaseType; - - if (baseType == null) - return false; - - return GenericIDictionaryIsAssignableFromType(baseType); - } - - static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - elementType = type.GetGenericArguments()[0]; - return true; - } - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - elementType = interfaceType.GetGenericArguments()[0]; - return true; - } - } - - Type baseType = type.BaseType; - - if (baseType == null) - { - elementType = null; - return false; - } - - return GenericIEnumerableIsAssignableFromType(baseType, out elementType); - } - - static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) - { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - var genericArgs = type.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - return true; - } - - foreach (var interfaceType in type.GetInterfaces()) - { - if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) - { - var genericArgs = interfaceType.GetGenericArguments(); - keyType = genericArgs[0]; - valueType = genericArgs[1]; - return true; - } - } - - Type baseType = type.BaseType; - - if (baseType == null) - { - keyType = null; - valueType = null; - return false; - } - - return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); - } - static Type MakeGenericArrayType(Type elemType) { return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); @@ -153,60 +62,5 @@ namespace Godot { return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); } - - // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> - // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized - - internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) - { - if (enumerable is ICollection collection) - { - int count = collection.Count; - - object[] tempArray = new object[count]; - collection.CopyTo(tempArray, 0); - - for (int i = 0; i < count; i++) - { - Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); - } - } - else - { - foreach (object element in enumerable) - { - Array.godot_icall_Array_Add(godotArrayPtr, element); - } - } - } - - internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) - { - foreach (DictionaryEntry entry in dictionary) - { - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); - } - } - - internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) - { -#if DEBUG - if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) - throw new InvalidOperationException("The type does not implement IDictionary<,>"); -#endif - - // TODO: Can we optimize this? - - var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); - var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); - - while (keys.MoveNext() && values.MoveNext()) - { - object key = keys.Current; - object value = values.Current; - - Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); - } - } } } diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 306a1997ab..3298c5da4c 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -129,12 +129,8 @@ void gd_mono_profiler_init() { } } -#if defined(DEBUG_ENABLED) - void gd_mono_debug_init() { - mono_debug_init(MONO_DEBUG_FORMAT_MONO); - CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); #ifdef TOOLS_ENABLED @@ -159,6 +155,10 @@ void gd_mono_debug_init() { return; // Exported games don't use the project settings to setup the debugger agent #endif + // Debugging enabled + + mono_debug_init(MONO_DEBUG_FORMAT_MONO); + // --debugger-agent=help const char *options[] = { "--soft-breakpoints", @@ -167,7 +167,6 @@ void gd_mono_debug_init() { mono_jit_parse_options(2, (char **)options); } -#endif // defined(DEBUG_ENABLED) #endif // !defined(JAVASCRIPT_ENABLED) #if defined(JAVASCRIPT_ENABLED) @@ -175,6 +174,7 @@ 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 @@ -185,9 +185,7 @@ MonoDomain *gd_initialize_mono_runtime() { } #else MonoDomain *gd_initialize_mono_runtime() { -#ifdef DEBUG_ENABLED gd_mono_debug_init(); -#endif #if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) // I don't know whether this actually matters or not @@ -1389,7 +1387,10 @@ bool _GodotSharp::is_runtime_initialized() { void _GodotSharp::_reload_assemblies(bool p_soft_reload) { #ifdef GD_MONO_HOT_RELOAD - CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); + // This method may be called more than once with `call_deferred`, so we need to check + // again if reloading is needed to avoid reloading multiple times unnecessarily. + if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) + CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); #endif } diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 8439769d84..0f211eebc6 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -277,12 +277,25 @@ 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); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index facc0da780..5ddf18d544 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -84,6 +84,7 @@ void CachedData::clear_corlib_cache() { class_IntPtr = nullptr; class_System_Collections_IEnumerable = nullptr; + class_System_Collections_ICollection = nullptr; class_System_Collections_IDictionary = nullptr; #ifdef DEBUG_ENABLED @@ -171,22 +172,18 @@ void CachedData::clear_godot_api_cache() { 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_ArrayGetElementType.nullify(); methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify(); - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType.nullify(); - methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info.nullify(); - methodthunk_MarshalUtils_MakeGenericArrayType.nullify(); methodthunk_MarshalUtils_MakeGenericDictionaryType.nullify(); - methodthunk_MarshalUtils_EnumerableToArray.nullify(); - methodthunk_MarshalUtils_IDictionaryToDictionary.nullify(); - methodthunk_MarshalUtils_GenericIDictionaryToDictionary.nullify(); - // End of MarshalUtils methods task_scheduler_handle = Ref<MonoGCHandleRef>(); @@ -213,6 +210,7 @@ void update_corlib_cache() { 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 @@ -297,22 +295,18 @@ void update_godot_api_cache() { 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, 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, GenericIEnumerableIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 1)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIEnumerableIsAssignableFromType", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryIsAssignableFromType", 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)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, GODOT_API_CLASS(MarshalUtils)->get_method("EnumerableToArray", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("IDictionaryToDictionary", 2)); - CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("GenericIDictionaryToDictionary", 2)); - // End of MarshalUtils methods #ifdef DEBUG_ENABLED diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 21c8ed4efe..3cf2bd6ce5 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -58,6 +58,7 @@ struct CachedData { GDMonoClass *class_IntPtr; // System.IntPtr GDMonoClass *class_System_Collections_IEnumerable; + GDMonoClass *class_System_Collections_ICollection; GDMonoClass *class_System_Collections_IDictionary; #ifdef DEBUG_ENABLED @@ -141,22 +142,18 @@ struct CachedData { 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; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType; GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType_with_info; - GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType_with_info; - GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericArrayType; GDMonoMethodThunkR<MonoReflectionType *, MonoReflectionType *, MonoReflectionType *> methodthunk_MarshalUtils_MakeGenericDictionaryType; - GDMonoMethodThunk<MonoObject *, Array *> methodthunk_MarshalUtils_EnumerableToArray; - GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_IDictionaryToDictionary; - GDMonoMethodThunk<MonoObject *, Dictionary *> methodthunk_MarshalUtils_GenericIDictionaryToDictionary; - // End of MarshalUtils methods Ref<MonoGCHandleRef> task_scheduler_handle; @@ -186,14 +183,6 @@ inline void clear_godot_api_cache() { cached_data.clear_godot_api_cache(); } -_FORCE_INLINE_ bool tools_godot_api_check() { -#ifdef TOOLS_ENABLED - return cached_data.godot_api_cache_updated; -#else - return true; // Assume it's updated if this was called, otherwise it's a bug -#endif -} - } // namespace GDMonoCache #define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class) diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index ede4203e35..2c65f7e3a0 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -31,6 +31,7 @@ #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" @@ -55,7 +56,11 @@ String GDMonoClass::get_full_name() const { return get_full_name(mono_class); } -MonoType *GDMonoClass::get_mono_type() { +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); @@ -264,6 +269,12 @@ 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, int p_params_count) { MethodKey key = MethodKey(p_name, p_params_count); @@ -328,6 +339,9 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo 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); diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 0c9a8cdafe..9237aae057 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -31,8 +31,6 @@ #ifndef GD_MONO_CLASS_H #define GD_MONO_CLASS_H -#include <mono/metadata/debug-helpers.h> - #include "core/map.h" #include "core/ustring.h" @@ -107,7 +105,8 @@ public: static MonoType *get_mono_type(MonoClass *p_mono_class); String get_full_name() const; - MonoType *get_mono_type(); + String get_type_desc() const; + MonoType *get_mono_type() const; uint32_t get_flags() const; bool is_static() const; @@ -137,6 +136,7 @@ public: 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, int p_params_count = 0); GDMonoMethod *get_method(MonoMethod *p_raw_method); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 3f4e5fe5ac..e76cb84d43 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -322,6 +322,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) { + MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } + ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type."); } break; @@ -353,56 +360,22 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Dictionary or IDictionary + if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); mono_field_set_value(p_object, mono_field, managed); break; } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Array or ICollection or IEnumerable + if (CACHED_CLASS(Array) == type_class || + type_class == CACHED_CLASS(System_Collections_ICollection) || + type_class == CACHED_CLASS(System_Collections_IEnumerable)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); break; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); - mono_field_set_value(p_object, mono_field, managed); - break; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; - } else { - MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); - mono_field_set_value(p_object, mono_field, managed); - break; - } - } - ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'."); } break; @@ -535,52 +508,62 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); + // Godot.Collections.Dictionary<TKey, TValue> if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } + // Godot.Collections.Array<T> if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + // 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); + MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(), + type.type_class, key_reftype, value_reftype); mono_field_set_value(p_object, mono_field, managed); break; } - if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); + // 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); + MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(), + type.type_class, elem_reftype); mono_field_set_value(p_object, mono_field, managed); break; } - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(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); + + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class); mono_field_set_value(p_object, mono_field, managed); break; } - if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; - } else { - MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); - mono_field_set_value(p_object, mono_field, managed); - break; - } + // 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); + + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), godot_array_class); + mono_field_set_value(p_object, mono_field, managed); + break; } } break; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 1878038f44..91ee07388b 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -154,6 +154,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ if (array_type->eklass == CACHED_CLASS_RAW(Color)) return Variant::PACKED_COLOR_ARRAY; + + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) + return Variant::ARRAY; } break; case MONO_TYPE_CLASS: { @@ -184,23 +188,14 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ return Variant::ARRAY; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return Variant::DICTIONARY; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + // IDictionary + if (p_type.type_class == CACHED_CLASS(System_Collections_IDictionary)) { return Variant::DICTIONARY; } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return Variant::ARRAY; - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + // 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; @@ -214,27 +209,33 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ 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; } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) - return Variant::DICTIONARY; - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { + // System.Collections.Generic.Dictionary<TKey, TValue> + if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) { return Variant::DICTIONARY; } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) + // System.Collections.Generic.List<T> + if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) { return Variant::ARRAY; + } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + // 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; } } break; @@ -252,10 +253,20 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_ 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: { + MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type()); + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + r_elem_type = ManagedType::from_class(array_type_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)) { + 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); @@ -263,12 +274,6 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ r_elem_type = ManagedType::from_reftype(elem_reftype); return true; } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) { - r_elem_type = ManagedType::from_reftype(elem_reftype); - return true; - } } break; default: { } break; @@ -282,7 +287,9 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma case MONO_TYPE_GENERICINST: { MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type()); - if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype) || + GDMonoUtils::Marshal::type_is_system_generic_dictionary(dict_reftype) || + GDMonoUtils::Marshal::type_is_generic_idictionary(dict_reftype)) { MonoReflectionType *key_reftype; MonoReflectionType *value_reftype; @@ -292,13 +299,6 @@ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, Ma r_value_type = ManagedType::from_reftype(value_reftype); return true; } - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) { - r_key_type = ManagedType::from_reftype(key_reftype); - r_value_type = ManagedType::from_reftype(value_reftype); - return true; - } } break; default: { } break; @@ -577,6 +577,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty if (array_type->eklass == CACHED_CLASS_RAW(Color)) return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray()); + GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass); + if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) + return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class); + ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type."); } break; @@ -600,41 +604,17 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::create_managed_from(p_var->operator RID()); } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Dictionary or IDictionary + if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Array or ICollection or IEnumerable + if (CACHED_CLASS(Array) == type_class || + CACHED_CLASS(System_Collections_ICollection) == type_class || + CACHED_CLASS(System_Collections_IEnumerable) == type_class) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } - - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); - } - - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - } else { - return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); - } - } } break; case MONO_TYPE_OBJECT: { // Variant @@ -755,38 +735,48 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty 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 GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.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.type_class); } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *key_reftype, *value_reftype; - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), - GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + // 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.type_class, key_reftype, value_reftype); } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); + // 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.type_class, elem_reftype); } - MonoReflectionType *elem_reftype; - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), - GDMonoUtils::Marshal::make_generic_array_type(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); } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - if (GDMonoCache::tools_godot_api_check()) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); - } else { - return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); - } + // 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); } } break; } break; @@ -922,6 +912,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type if (array_type->eklass == CACHED_CLASS_RAW(Color)) return mono_array_to_PackedColorArray((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 { @@ -957,44 +951,27 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return ptr ? Variant(*ptr) : Variant(); } - if (CACHED_CLASS(Array) == type_class) { + // Godot.Collections.Dictionary + if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = nullptr; - Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); + Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } - if (CACHED_CLASS(Dictionary) == type_class) { + // Godot.Collections.Array + if (CACHED_CLASS(Array) == type_class) { MonoException *exc = nullptr; - Dictionary *ptr = CACHED_METHOD_THUNK(Dictionary, GetPtr).invoke(p_obj, &exc); + Array *ptr = CACHED_METHOD_THUNK(Array, GetPtr).invoke(p_obj, &exc); UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } - - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); - } - - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } - - if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } } 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); @@ -1002,6 +979,7 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type 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); @@ -1009,22 +987,19 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type return *unbox<Array *>(ret); } - // The order in which we check the following interfaces is very important (dictionaries and generics first) - - if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); - } - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + // 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); } - if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype)) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); - } - - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::Marshal::enumerable_to_array(p_obj); + // 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(p_obj, p_type.type_class, elem_reftype); } } break; } @@ -1081,6 +1056,80 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) { } } +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) { + GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class; + + String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)"; + 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); + + void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) }; + + MonoException *exc = nullptr; + ctor->invoke_raw(mono_object, ctor_args, &exc); + UNHANDLED_EXCEPTION(exc); + + return mono_object; +} + +Array system_generic_list_to_Array(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; + MonoArray *mono_array = (MonoArray *)to_array->invoke_raw(p_obj, nullptr, &exc); + UNHANDLED_EXCEPTION(exc); + + return mono_array_to_Array(mono_array); +} + 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); @@ -1093,6 +1142,18 @@ MonoArray *Array_to_mono_array(const Array &p_array) { return ret; } +MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) { + int length = p_array.size(); + MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), 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) diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 8b405291a7..f2d887e6d6 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -123,9 +123,18 @@ Variant mono_object_to_variant_no_err(MonoObject *p_obj, const ManagedType &p_ty /// 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); +Array system_generic_list_to_Array(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, GDMonoClass *p_array_type_class); Array mono_array_to_Array(MonoArray *p_array); // PackedInt32Array diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index c8cc5247c6..432aa74a6f 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -30,13 +30,14 @@ #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" -#include <mono/metadata/attrdefs.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. diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 00119ced88..f9d492dabb 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -30,6 +30,7 @@ #include "gd_mono_utils.h" +#include <mono/metadata/debug-helpers.h> #include <mono/metadata/exception.h> #include "core/debugger/engine_debugger.h" @@ -358,6 +359,14 @@ MonoDomain *create_domain(const String &p_friendly_name) { 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; @@ -584,75 +593,56 @@ bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { return (bool)res; } -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); -} - -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { +bool type_is_system_generic_list(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType).invoke(p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericList).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { +bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType).invoke(p_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsSystemGenericDictionary).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { +bool type_is_generic_ienumerable(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info).invoke(p_reftype, r_elem_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIEnumerable).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { +bool type_is_generic_icollection(MonoReflectionType *p_reftype) { NO_GLUE_RET(false); MonoException *exc = nullptr; - MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info).invoke(p_reftype, r_key_reftype, r_value_reftype, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericICollection).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); return (bool)res; } -Array enumerable_to_array(MonoObject *p_enumerable) { - NO_GLUE_RET(Array()); - Array result; +bool type_is_generic_idictionary(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray).invoke(p_enumerable, &result, &exc); + MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericIDictionary).invoke(p_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; + return (bool)res; } -Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { - NO_GLUE_RET(Dictionary()); - Dictionary result; +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { MonoException *exc = nullptr; - CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary).invoke(p_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; } -Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { - NO_GLUE_RET(Dictionary()); - Dictionary result; +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, GenericIDictionaryToDictionary).invoke(p_generic_idictionary, &result, &exc); + CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes).invoke(p_dict_reftype, r_key_reftype, r_value_reftype, &exc); UNHANDLED_EXCEPTION(exc); - return result; } GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index b850e1be9b..e3011ade5d 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -52,22 +52,18 @@ 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); 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); -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype); -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype); -bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); -bool generic_idictionary_is_assignable_from(MonoReflectionType *p_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); -Array enumerable_to_array(MonoObject *p_enumerable); -Dictionary idictionary_to_dictionary(MonoObject *p_idictionary); -Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary); - } // namespace Marshal _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { @@ -115,6 +111,9 @@ 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 set_exception_message(MonoException *p_exc, String message); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 1eb1ee0d29..71febd3433 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1699,7 +1699,7 @@ public: valid = false; } else { Error errn; - DirAccessRef da = DirAccess::open(sdk_path.plus_file("tools"), &errn); + DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); if (errn != OK) { err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n"; valid = false; diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 99080eeb3c..9ae47d6174 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -76,7 +76,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - doNotStrip '**/*.so' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } // Both signing and zip-aligning will be done at export time diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index 6bb438c249..19eee5a315 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -26,7 +26,9 @@ android { packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/NOTICE' - doNotStrip '**/*.so' + + // Should be uncommented for development purpose within Android Studio + // doNotStrip '**/*.so' } sourceSets { diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index d56a095a5b..cdc8db8aea 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -307,19 +307,22 @@ Ref<Material> MeshInstance3D::get_surface_material(int p_surface) const { Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { - if (get_material_override() != Ref<Material>()) { - return get_material_override(); - } else if (p_surface < materials.size()) { - return materials[p_surface]; - } else { - Ref<Mesh> mesh = get_mesh(); + Ref<Material> material_override = get_material_override(); + if (material_override.is_valid()) { + return material_override; + } - if (mesh.is_null() || p_surface >= mesh->get_surface_count()) { - return Ref<Material>(); - } + Ref<Material> surface_material = get_surface_material(p_surface); + if (surface_material.is_valid()) { + return surface_material; + } + Ref<Mesh> mesh = get_mesh(); + if (mesh.is_valid()) { return mesh->surface_get_material(p_surface); } + + return Ref<Material>(); } void MeshInstance3D::_mesh_changed() { diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 835fef81e1..02ea5b24b8 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1004,8 +1004,8 @@ void Environment::_bind_methods() { ADD_GROUP("SSAO", "ssao_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssao_enabled"), "set_ssao_enabled", "is_ssao_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_ssao_radius", "get_ssao_radius"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_intensity", "get_ssao_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_radius", PROPERTY_HINT_RANGE, "0.1,128,0.01"), "set_ssao_radius", "get_ssao_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_intensity", PROPERTY_HINT_RANGE, "0.0,128,0.01"), "set_ssao_intensity", "get_ssao_intensity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect"); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 3740b59430..a3bb39cd90 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -272,7 +272,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("vertex_buffer_create", "size_bytes", "data"), &RenderingDevice::vertex_buffer_create, DEFVAL(Vector<uint8_t>())); ClassDB::bind_method(D_METHOD("vertex_format_create", "vertex_descriptions"), &RenderingDevice::_vertex_format_create); - ClassDB::bind_method(D_METHOD("index_buffer_create", "size_indices", "format", "data"), &RenderingDevice::index_buffer_create, DEFVAL(Vector<uint8_t>())); + ClassDB::bind_method(D_METHOD("index_buffer_create", "size_indices", "format", "data"), &RenderingDevice::index_buffer_create, DEFVAL(Vector<uint8_t>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("index_array_create", "index_buffer", "index_offset", "index_count"), &RenderingDevice::index_array_create); ClassDB::bind_method(D_METHOD("shader_compile_from_source", "shader_source", "allow_cache"), &RenderingDevice::_shader_compile_from_source, DEFVAL(true)); |