godotengine / godot-cpp

C++ bindings for the Godot script API
MIT License
1.68k stars 509 forks source link

Exported/registered properties periodically get reset to default values #453

Open FractalDiane opened 4 years ago

FractalDiane commented 4 years ago

I've been dealing with the exasperating problem of my registered/exported class properties getting reset to their default values in the editor every so often. For example, here's one that happens practically every time I open Godot after a few days: I have this Ref to a custom class declared in a header:

Ref<EnemyGroup> enemy_group;

And this register property declaration in _register_methods in the source:

register_property("enemy_group", &Battle::enemy_group, Ref<EnemyGroup>(), GODOT_METHOD_RPC_MODE_DISABLED, GODOT_PROPERTY_USAGE_DEFAULT, GODOT_PROPERTY_HINT_RESOURCE_TYPE, "Resource");

The fact that it involves a custom class or a Ref doesn't seem like the issue, because it's also happened with simple ints, floats, and Strings as well. It's also happened with things that don't have property hints. Also, with things that use Refs, I've tried both Ref<T>() and nullptr as defaults, and it happens in both cases.

It seems to affect some classes more than others, mainly the ones I've been actively editing. I have a lot of classes that derive from Resource that just exist in the filesystem and not as nodes, and it doesn't seem to be affecting anything exported in them. That being said, I haven't been actively editing the class I've given as an example here, and sometimes I'll just close Godot and open it several days later having done nothing, and everything still gets reset. This class I've shown here is in the main scene, so maybe just saving the scene can cause it to happen?

I've looked all over the internet, the issues for this repo, and the gdnative channel in the Discord, and I haven't found anyone else talking about this, so I'm wondering if I'm just missing something or doing something wrong. Anyone have any ideas?

Zylann commented 4 years ago

I wonder wether this is a c++ bindings bug or a Godot bug. I can't imagine what could possibly cause something like that on our side.

I know for a fact that a "similar" oddity happens with globally named classes, where Godot scans for them before having loaded the NativeScript library and looses them as a result. Perhaps something similar happens here, but I'm not sure.

Does EnemyGroup inherit Resource? Do you have a test project somewhere?

FractalDiane commented 4 years ago

If that test project isn't enough to figure it out, I could upload the full project I've been working on, but it's quite huge.

Zylann commented 4 years ago

I've been working on another project in GDScript for about a year now, and I've never had this issue with it, so I feel like it must be something C++ specific.

That's not the same thing. Here the problem is to know wether or not this is specific to the C++ bindings, or GDNative itself (and I suspect the latter). GDScript can't be compared to that.

image

FractalDiane commented 4 years ago

Ah, okay. That makes sense.

Zylann commented 4 years ago

On which platform are you building this? I'm failing to execute this SConstruct file on windows, I get this error:

scons platform=windows target=debug

AttributeError: 'str' object has no attribute 'attributes':
  File "C:\Projects\Godot\CppBindings\Reports\CppSample_issue453\src\SConstruct", line 134:
    library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Environment.py", line 260:
    return MethodWrapper.__call__(self, target, source, *args, **kw)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Environment.py", line 224:
    return self.method(*nargs, **kwargs)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Builder.py", line 645:
    return self._execute(env, target, source, OverrideWarner(kw), ekw)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Builder.py", line 564:
    tlist, slist = self._create_nodes(env, target, source)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Builder.py", line 528:
    target, source = self.emitter(target=tlist, source=slist, env=env)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Builder.py", line 353:
    target, source = e(target, source, env)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Tool\link.py", line 104:
    return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.ShLibSymlinkGenerator)
  File "c:\users\marc\appdata\local\programs\python\python36-32\lib\site-packages\scons-3.0.1\SCons\Tool\link.py", line 85:
    tgt.attributes.shared = 1
FractalDiane commented 4 years ago

I'm building it on Windows, 64 bit. The SConstruct file is actually set up so that the godot-cpp folder is three directories above the src folder in the project.

godot_headers_path = "../../../godot-cpp/godot_headers/"
cpp_bindings_path = "../../../godot-cpp/"

My filesystem looks something like this:

Documents
    - Godot
        - godot-cpp
        - Projects
            - CppSample
                - game
                - src

With the src folder containing the code and SConstruct, and the game folder containing the project.godot file and everything else. I don't think that has anything to do with the error you're getting, though, so I'm not sure what would be causing that...

Zylann commented 4 years ago

I switched off MinGW and fixed a path error in the SConstruct since the bindings aren't built in the project and my external repo for them had a different name.

I opened the project and I can see this: image

FractalDiane commented 4 years ago

Alright, those are the values I set before zipping the project, so it looks like they didn't get reset.

Zylann commented 4 years ago

How do I reproduce it? I launched the game, printed this:

Hello there
My resource has an int of 5 and a String of Hello

I tried restarting the editor a few times, no reset.

FractalDiane commented 4 years ago

It's not something I've been able to consistently reproduce; it seems to happen sporadically when opening my other project after not having it open for a while.

However, I've realized something that could potentially be causing it: I've been using this plugin: https://gitlab.com/turtlewit/godot_cpp_helper_plugin

We were also using this plugin on a previous project, and we were having the reset issue from time to time on that project as well. I hadn't done any large C++ projects before then, so I unfortunately don't have any large projects without the plugin to compare to. I looked through the plugin a bit, but I'm not sure what would be causing the properties to get reset, especially since only the properties in node scripts and not resource scripts are the ones getting reset.

Something else I'll mention is that I did a project with the Rust bindings a few months ago, and I don't remember this reset issue happening in it, so I feel like the problem isn't with GDNative itself.

I tried disabling the plugin and recompiling and re-saving the project a few times, and so far nothing has been reset, but it hasn't been too long yet. If no more resets end up happening with it disabled, I guess the plugin is the culprit.

Zylann commented 4 years ago

Without a reliable repro there is nothing I can do. My gut feeling is that Godot has a bug somewhere, a variable not initialized or something, because the C++ bindings has much less possible failing points when it comes to exported variables, i.e I don't know what could possibly be a failure in there. In Godot on the other hand, the culprit could be many things, like for example Godot loading your scene before the GDNative library being loaded, or the inspector assuming default values before the library being loaded. The only cause I can imagine for the variables resetting to zero, is that Godot didn't load, or could not load your library (did you check the logs? Godot has the fancy habit of logging important stuff, but getting unnoticed in the console). This can be a bug, or the fact your library is getting locked somehow.

And which version of Godot are you using? Tried with 3.2.2 and 3.2.3 rc5, also using a release build of your library, no problem. And when you say they reset, you mean they become 0 or null?

Note, I'm doing these tests using the latest bindings and MSVC (cuz for some reason MinGW refuses to work, specifically on your project 🙃 ).

If you think the Cpp Helper plugin has something to do with it, you could remove it to eliminate the possible cause.

FractalDiane commented 4 years ago

I'm on 3.2.2, and yeah, from what I've seen they become 0 or null. I've exported ints with a default of 180 but get reset to 0 when the bug happens.

I think for now I'm going to just keep experimenting on the project and seeing if I can find a reliable way to reproduce it.

FractalDiane commented 4 years ago

Okay, an update: With the plugin disabled, I've recompiled, saved, closed, and reopened the project numerous times today, and a property just got reset again. So I guess that means the helper plugin wasn't the culprit.

Again, I unfortunately still have no reliable way to reproduce this; I did absolutely nothing but close and reopen the project a bunch of times with some recompiles in there, and none of the times I compiled and immediately opened the project again caused it, so I have no idea how or what caused it to happen.

Here's one bit of potential evidence from the git diff, though: The actual external resource reference got wiped out of the scene file. I'm not sure if that always happens when a property relying on an external resource no longer relies on it, but maybe it's helpful somehow. image image

Zylann commented 4 years ago

If it gets wiped out it means Godot saved it (so basically, after it went wrong already). You didn't just open/closed the project? Godot only saves when you hit Ctrl+S, Save Scene or play the game. Did you get any repro with your test project? The screenshots are from the full game I guess

FractalDiane commented 4 years ago

That's so strange, because I swear the property fields were set every time I saved and closed Godot. Maybe I need to pay more attention to that.

And yeah, those screenshots are from the full game. The test project still has all its property values intact, but I also haven't been messing with it nearly as much.

Zylann commented 4 years ago

Also when you say you recompiled the library and then saved, do you mean you did that while Godot was open? Cuz I'm not very confident in Godot properly dealing with this. Yet I just tried doing that and still could not reproduce

FractalDiane commented 4 years ago

Yeah, I've been recompiling and saving while Godot is open. I've always closed Godot when pushing/pulling, but I don't remember having many issues when compiling while it's open. Maybe closing all scenes or just closing Godot entirely would help mitigate the issue. Like I said though, I've recompiled while it's open multiple times and it hasn't happened, so something must have just gone wrong the latest time that I didn't notice.

FractalDiane commented 4 years ago

Another update: Yes, it also happens on Linux, and I still haven't found a way to reliably reproduce it. However, something really bizarre happened this time:

I committed with the property values intact and closed Godot, and verified that the property values were still set in the .tscn file. The next time I opened Godot, all the property values were reset in the editor, but the .tscn file still showed them being set, and git said there were no differences. It wasn't until I actually ran the scene that the scene file updated to show the properties weren't set anymore and git showed that difference. I'm not sure if that gives any insight into what's happening.

Zylann commented 4 years ago

Did you do any change to the library between the moment you opened the project the first time, and reopened it the second time? We need to rule out every detail when the bug occurs. You could even record your screen as you start working just to recap the course of events if you have to :D

FractalDiane commented 4 years ago

I did not touch the library in between opening it the first time and opening it the second time.

Also, after the properties got reset, I rolled back to the last commit and opened the editor, and this time the properties didn't get reset. I know it makes less sense, but everything I've seen points to the problem happening when Godot gets opened and not something that happens while it's open.

Zylann commented 4 years ago

It would be interesting if you manage to have it happen in your test project, or any project that you can put in common with me, because no matter what I try I can't reproduce it with what you uploaded. So either your test project makes the bug very unlikely to happen, or the problem only exists within your project and not the test one.

FractalDiane commented 4 years ago

Okay, I think I've found a reproducible way to make the properties in the test project get reset. This is a process that wouldn't happen in my project right now, but it is reproducible and doesn't seem like intended behavior, so hopefully it'll give some hint as to what's happening.

  1. Download and unzip CppSample.zip from the post above onto a Linux computer. (It might also work on Mac but I'm not sure)
  2. Check the Scene.tscn file, and the following properties should be intact: image
  3. Initialize a Git repo in the CppSample folder and commit (optional, but lets you roll back easily for testing).
  4. Open Godot, and you'll get an error saying there's no library for the current platform, since the only library that currently exists in the folder is a Windows one.
  5. From the src folder, compile the library for Linux (I used scons platform=linux target=debug -j10). You can either compile the library and then open Godot, or compile it while Godot is open; there was no difference in behavior in my testing.
  6. Assign the new library (it should be in bin/Linux) to the 64 bit Linux slot of bin/Library.tres.

AT THIS POINT, if you press Ctrl+S (which saves both the scene and the library resource), the properties of the scene instantly get reset, and this is reflected in the .tscn file. If you ONLY save the library file by using the save button in the inspector tab, then close and reopen Godot, the properties do NOT get reset, and should display in the editor as they initially were in the .tscn file.

From my perspective, it looks like saving the scene itself is causing the problem, or maybe the two resources are getting saved in the wrong order, which is causing the properties to be reset.

Zylann commented 4 years ago

That reproduction is ill-formed, unfortunately. If Godot could not load the library when you opened the scene, there is no chance it could possibly know what should be the state of the script (it will see no properties). So if you were to save the scene in that situation, it will of course remove every value because it had no place to load them to begin with. That's a design flaw in Godot, but surely isn't the direct cause of your problem since as you said, you aren't doing this in your project. You could file an issue to the Godot repo to ask this use case to be fixed, though.

Secondly, putting in the correct library after the fact doesnt mean Godot will magically fix its failed loading, unfortunately (you'd have to close the scene and reopen it I guess?). That might be another design flaw, but again, not the one we are looking for. These two issues exist regardless of the NativeScript language used.

The bug we are looking for is, why Godot doesn't properly load properties when the library definitely is available.

FractalDiane commented 4 years ago

Alright, I tried changing the main scene and I've been testing it for a few days now, and the properties have yet to be reset. It's looking like the problem only occurs on the scene that gets loaded when Godot opens.

jknightdoeswork commented 3 years ago

It's looking like the problem only occurs on the scene that gets loaded when Godot opens.

Sounds like you have a clear understanding. Can you elaborate & clarify your current understanding of the problem? And offer the minimum repro steps based on your new knowledge?

sammyflemington commented 4 months ago

Hello, I have run into this same issue in Godot 3.5.3 and I have created a project that can replicate the problem as simply as I could. There are two custom resources, one of which extends the other (CustomResource.gd/CustomResourceExtension.gd). The parent resource has two properties, an export category dropdown (enum) and a PackedScene, loaded as such: var scn = preload("res://TestScene.tscn") The extended resource adds an export(String) so they can be differentiated more easily in the inspector. The project also contains an autoloaded scene, "ResourceDatabase.tscn" which holds a reference to the parent resource. Without this resource held in the autoloaded scene, the bug does not occur. Also, if you change preload(...) to load(...) in CustomResource.gd, the bug does not occur. Maybe this hints at the cause of the issue.

Steps to reproduce:

  1. Open the project in the attached .zip
  2. Upon viewing MyExtendedResource.tres, the inspector will be missing export properties from the parent resource (category). They can be made visible again by adding a comment to CustomResource.gd and CustomResourceExtension.gd, in that order, and saving each change.
  3. Change the category property of MyExtendedResource.tres to something other than CAT1
  4. Save and restart the editor, and repeat step 2
  5. The category property will be reset to the default (CAT1) showing that information was reset somehow.

It seems that this issue can be worked around by refraining from using preload() in resources and instead opting for load(). I hope this is helpful!

ResourceTestProject.zip