migueldeicaza / SwiftGodot

New Godot bindings for Swift
https://migueldeicaza.github.io/SwiftGodotDocs/tutorials/swiftgodot-tutorials/
MIT License
1.15k stars 74 forks source link

Runtime crash when calling @Callable function on a resource #435

Open Airyzz opened 7 months ago

Airyzz commented 7 months ago

If you have a resource like so:

import SwiftGodot

@Godot
class MyResource: Resource {

    @Callable
    public func do_thing() {
    }
}

When calling do_thing on the resource via gdscript the game will crash

migueldeicaza commented 7 months ago

Would you mind attaching the stack trace, or other bits I could use to reproduce this?

Airyzz commented 7 months ago

There is not very much useful info being printed in stack trace, it only shows a single call coming from godot. I can attach debugger tomorrow and see if I can find anything

tishin commented 7 months ago

How do you call it? I added GD.print("do_thing") into the method for indication, and calling it from a simple GDScript works fine, I can see the output:

extends Node2D

func _ready():
    MyResource.new().do_thing();
Airyzz commented 7 months ago

Hmm, when I was doing it I was getting the resource from an @export variable in gdscript, instead of creating a new one. Thats the only difference I see

Airyzz commented 7 months ago

This error does not seem consistent, in some scripts the call works fine, but in others it does not. makes me think the issue is coming from elsewhere, but my resource is like so:

import SwiftGodot

@Godot
class RandomStringResource: Resource {
    @Export
    var lines: GArray = .init()

    @Callable
    public func get_line() -> String {
        var count = lines.count
        var index = DeterministicRandom.randi(from: 0, to: count - 1, seed: 0xABCD)
        var element = lines[index]

        if let str = String(element) {
            return str
        }

        return "INVALID_STRING"
    }
}

The backtrace is not particularly insightful:

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.2.stable.official (46dc277917a93cbf601bbcf0d27d00f6feeec0d5)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f2f31642520] (??:0)
-- END OF BACKTRACE --
================================================================

💣 Program crashed: Aborted at 0x000003e800004555

Thread 0 "godot" crashed:

0 0x00007f2f316969fc <unknown> in libc.so.6

The most interesting thing, is this call to get_line was working fine, but seemingly after something (im not sure what) was changed in the editor, it started crashing.

Airyzz commented 7 months ago

If I add a print to the beginning of get_line, the print statement never appears in the console, so it seems the crash is happening before my swift code is actually executed

Airyzz commented 7 months ago

Ok, so i have kind of figured some things out, but I cannot explain the cause.

In my game, there is a killfeed which displays a name + random line sourced from a RandomStringResource, we will call killfeed_lines, so it says something like {name} died. This is handled in gdscript, sourcing the random line from Swift.

There's a second script, which displays a banner when a player dies. This is also sourced from a RandomStringResource, but not the same resource as the killfeed, this one will be called banner_lines.

Then, I have a global event bus, which raises an event containing the player's name, which both these scripts are listening to.

Now, the crash occurs when the killfeed script tries to get a line from killfeed_lines, however, the crash will only occur when the banner script contains a reference to banner_lines. Even though these scripts are different, and referencing different resources. somehow the reference to banner_lines existing on the banner node, is causing a crash during the killfeed script accessing killfeed_lines

To make things even more interesting, if i remove any code which interacts with banner_lines from the banner script, but keep the exported variables, the killfeed script still fails.

to summarize: killfeed script interacting with killfeed_lines resource crashes, if banner script has a reference to a banner_lines resource.

It doesn't make sense