Hello everyone!

Im trying to serialize my custom class in godot in c#. Everything works fine unless i try to get data back as a custom class.
Here is a code snippet.


public partial class GodotSerilisationTest : Node
{
    public override void _Ready()
    {
        FileAccess fa = FileAccess.Open("user://test.sav", FileAccess.ModeFlags.Write);
        string test = "mytestdata";

        Testclass testclass = new Testclass("sometestclass");
        fa.StoreVar(test, true);
        fa.StoreVar("Anotehr Test", true);
        fa.StoreVar(testclass, true);
        fa.Dispose();

        fa = FileAccess.Open("user://test.sav", FileAccess.ModeFlags.Read);

        var foo = fa.GetVar(true);
        var foo2 = fa.GetVar(true);

        //ERRROR
        var foo3 = fa.GetVar(true);



        GD.Print(foo);
        GD.Print(foo2);
        if (foo3.AsGodotObject() is Testclass bar)
        {
            GD.Print(bar.teststring);
        }
        fa.Dispose();
    }
}
public partial class Testclass : GodotObject
{
    public string teststring;

    public Testclass(string teststring)
    {
        this.teststring = teststring;
    }
}

It throws the following error:

E 0:00:00:0786 Godot.NativeInterop.NativeFuncs.generated.cs:345 @ void Godot.NativeInterop.NativeFuncs.godotsharp_method_bind_ptrcall(IntPtr , IntPtr , System.Void** , System.Void* ): Cannot instance script because the associated class could not be found. Script: ''. Make sure the script exists and contains a class definition with a name that matches the filename of the script exactly (it's case-sensitive).
<C++ Error> Method/function failed. Returning: false
<C++ Source> modules/mono/csharp_script.cpp:2388 @ can_instantiate()
<Stack Trace> Godot.NativeInterop.NativeFuncs.generated.cs:345 @ void Godot.NativeInterop.NativeFuncs.godotsharp_method_bind_ptrcall(IntPtr , IntPtr , System.Void** , System.Void* )
NativeCalls.cs:3710 @ Godot.Variant Godot.NativeCalls.godot_icall_1_432(IntPtr , IntPtr , Godot.NativeInterop.godot_bool )
FileAccess.cs:485 @ Godot.Variant Godot.FileAccess.GetVar(Boolean )
GodotSerilisationTest.cs:20 @ void GodotSerilisationTest._Ready()
Node.cs:2093 @ Boolean Godot.Node.InvokeGodotClassMethod(Godot.NativeInterop.godot_string_name& , Godot.NativeInterop.NativeVariantPtrArgs , Godot.NativeInterop.godot_variant& )
GodotSerilisationTest_ScriptMethods.generated.cs:24 @ Boolean GodotSerilisationTest.InvokeGodotClassMethod(Godot.NativeInterop.godot_string_name& , Godot.NativeInterop.NativeVariantPtrArgs , Godot.NativeInterop.godot_variant& )
CSharpInstanceBridge.cs:24 @ Godot.NativeInterop.godot_bool 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* )

If someone has a pointer on why this is happening let me know.

  • xyz replied to this.

    Moep Which line it complains about?

    Btw it's much cleaner to use custom resource classes for data storage.

    • Moep replied to this.

      xyz

      The line i wrote //Error above aka var foo3 = fa.GetVar(true);

      Also from what i have gathered, using custom resources for data storage is potentially dangerous as they can easily be temperd with and someone could upload a tempered save file that can cause harm to someone who uses it.

      • xyz replied to this.

        Moep Put the Testclass definition in its own source file named Testclass.cs

        • Moep replied to this.

          xyz

          I tried it and it results in the same error sadly.

          • xyz replied to this.

            Moep Are you sure? The filename is case-sensitive. It must be exactly the same as class name.

            • Moep replied to this.

              Moep Any reason you need to serialize by manually writing into a binary file instead of using a Resource-derived class that handles all that automatically?

              Can you provide, or link to an example of saving and restoring binary data using resources? I experimented with that, but couldn't figure it out.

              • xyz replied to this.

                DaveTheCoder Can you provide, or link to an example of saving and restoring binary data using resources? I experimented with that, but couldn't figure it out.

                Just save it as *.res instead of *.tres. ResourceSaver determines the format according to extension.

                class_name DeadParrot extends Resource
                var pushing_up_the_daisies = true;
                func _ready():
                	var polly = DeadParrot.new()
                	ResourceSaver.save(polly, "res://polly.res")
                	ResourceSaver.save(polly, "res://polly.tres")

                Regarding the OP. I tried to do the same in GDScript but also got some funky errors. Didn't have time to investigate further. Object serialization is always tricky so better play it safe and go with engine's preferred system - and that's resource objects/files.

                • Moep replied to this.

                  xyz

                  Yeah, for now i just serialize the properties one at a time with storevar() and then compose the class when loading back the simple property types.

                  • xyz replied to this.

                    Moep Yeah, for now i just serialize the properties one at a time with storevar() and then compose the class when loading back the simple property types.

                    No need to do it like this. It's error prone, tedious and non-scalable. Simply inherit the class from Resource and use ResourceSaver.Save() and ResourceLoader.Load() to (de)serialize the object.

                    • Moep replied to this.

                      xyz

                      I know that resources are the easiest to handle solution, but this is why i shy away from them for now https://github.com/godotengine/godot-proposals/issues/4925

                      Also Godot provides an entire documentation page on saving and loading a game and binary serialization based on fA, so i don't think that resources are the one accepted/intended way to handle this.

                      • xyz replied to this.

                        Moep Resources are not the only way. They are the easiest way though. The security problem is relevant only if you're handling user generated content shared between users.

                        Anyway, you should perhaps report this as an issue and see what happens, because it looks like it should work. I tried doing this in GDscript and it appears to be working in 3.5 but it's throwing class name shadowing error in 4.1