Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# Editor Plugin crashes Godot 4.3 when GraphNode is added to GraphEdit #96289

Closed
Meythulhu opened this issue Aug 29, 2024 · 4 comments · Fixed by #96309
Closed

C# Editor Plugin crashes Godot 4.3 when GraphNode is added to GraphEdit #96289

Meythulhu opened this issue Aug 29, 2024 · 4 comments · Fixed by #96309

Comments

@Meythulhu
Copy link

Tested versions

  • Reproducible in v4.3.beta1 (a4f2ea9), Godot v4.3-stable (77dcf97), and v4.3.1-rc
  • Tested in Godot v4.2.2-stable (15073af), and this is not an issue

System information

Windows 11 - v4.3.stable.mono.official.77dcf97d8 - Vulkan 1.3.278 - Forward+ - NVIDIA GeForce RTX 4060 Ti

Issue description

Through the process of using the Dialogue Trees plugin, I noticed that an exception is thrown whenever trying to edit a DialogueTree node. The exception thrown is a AccessViolationException, which shortly crashes Godot 4.3 with no further information other than the stack trace.

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at Godot.NativeInterop.NativeFuncs.godotsharp_method_bind_ptrcall(IntPtr, IntPtr, Void**, Void*)
   at Godot.NativeCalls.godot_icall_3_767(IntPtr, IntPtr, IntPtr, Godot.NativeInterop.godot_bool, Int32)
   at Godot.Node.AddChild(Godot.Node, Boolean, InternalMode)
   at Ardot.DialogueTrees.DialogueGraph.<LoadTree>g__AddDialogueNode|26_0(Godot.Node, Int32, <>c__DisplayClass26_0 ByRef)
   at Ardot.DialogueTrees.DialogueGraph.LoadTree(Ardot.DialogueTrees.DialogueTreeData, Boolean, Godot.Collections.Array`1<Godot.Node>)
   at Ardot.DialogueTrees.DialogueTreeDock.LoadTree(Ardot.DialogueTrees.DialogueTree)
   at Ardot.DialogueTrees.DialogueTreesPlugin._Edit(Godot.GodotObject)
   at Godot.EditorPlugin.InvokeGodotClassMethod(Godot.NativeInterop.godot_string_name ByRef, Godot.NativeInterop.NativeVariantPtrArgs, Godot.NativeInterop.godot_variant ByRef)
   at Ardot.DialogueTrees.DialogueTreesPlugin.InvokeGodotClassMethod(Godot.NativeInterop.godot_string_name ByRef, Godot.NativeInterop.NativeVariantPtrArgs, Godot.NativeInterop.godot_variant ByRef)
   at Godot.Bridge.CSharpInstanceBridge.Call(IntPtr, Godot.NativeInterop.godot_string_name*, Godot.NativeInterop.godot_variant**, Int32, Godot.NativeInterop.godot_variant_call_error*, Godot.NativeInterop.godot_variant*)

It seems to occur when trying to add or remove a GraphNode from the DialogueGraph class (which extends GraphEdit). I tried passing brand new GraphNode, I tried deferring the calls, and I tried removing the optional parameters, as well as playing with the values of the different AddChild parameters. All of these seem to have no effect on the exception occurring and a crash following after.

It's also important to note that this does not occur on DialogueTree nodes which are recently added to the scene, only ones loaded from the file system. This is only impacting the DialogueGraph added to the dock; the DialogueTree node functions properly when running in-game.

I tried running memory dumps and attaching the godot process to a debugger, and there is no breakpoint hit or any sort of trace in the memory dump outside of the AccessViolationException being thrown only in the managed assembly. It just silently closes Godot with exit code 139 (SIGSEGV signal - Memory Access Violation).

Steps to reproduce

With the Minimal Reproduction Project:

  1. Open up TestScene.tscn in the Godot Editor
  2. If the crash hasn't happened yet, select the DialogueTree node in the scene.

Without the Minimal Reproduction Project:

  1. Install Dialogue Trees - C# from the Asset Library
  2. Create new scene with DialogueTree node
  3. In the DialogueTree editor in the bottom dock, add an output node and hook it up to the start node (you'll need to expand the window)
  4. Save the scene and close it
  5. Re-open the scene and try selecting the DialogueTree node

Minimal reproduction project (MRP)

DialogueTest.zip

@raulsntos
Copy link
Member

Looks like it may be a regression from #88014 cc @Geometror

It seems connections_layer is null when this Callable is called:

graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));

@Geometror
Copy link
Member

Geometror commented Aug 30, 2024

Well, I'm surprised it took so long until we see the non-internal connection layer causing problems ;)

It looks like you remove all children including the connection layer at some point (haven't checked the addon code). To fix this crash you need to check whether each node you remove is a GraphElement. The connections layer has to be kept non-internal for now because internal nodes at arbitrary indices (not just front or back) are currently not possible and it has to be drawn between frames and graph nodes.

I'm going to open a PR which adds a warning when you accidentally remove the connection layer as well as checks everywhere a crash could occur (not ideal, but at least it's safe).

@Meythulhu
Copy link
Author

It looks like you remove all children including the connection layer at some point (haven't checked the addon code). To fix this crash you need to check whether each node you remove is a GraphElement. The connections layer has to be kept non-internal for now because internal nodes at arbitrary indices (not just front or back) are currently not possible and it has to be drawn between frames and graph nodes.

Yep! It seems to be caused by DialogueGraph.ClearTree(), which just calls GetChildren() and frees all children. Adding a filter for any node named _connection_layer seems to prevent it from being removed.

This seems like an issue that can be easily ran into; Is it possible for _connection_layer to be made internal to prevent it from being treated as a non-internal node?

@Geometror
Copy link
Member

Unfortunately not, as I said:

The connections layer has to be kept non-internal for now because internal nodes at arbitrary indices (not just front or back) are currently not possible and it has to be drawn between frames and graph nodes.

The best we can do is warn the user about it and prevent the crash, hence #96309

@akien-mga akien-mga added this to the 4.4 milestone Oct 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants