godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.55k stars 20.69k forks source link

Custom Resource does not update in editor if changed/saved from script #24646

Closed williamd1k0 closed 2 years ago

williamd1k0 commented 5 years ago

Bugsquad edit: This issue has been confirmed several times already. No need to confirm it further.


Godot version:

Godot 3.1 alpha 4 and 3.2 master (a0ce88f953a4311edc726523ff8724b891407855)

OS/device including version:

Windows 10 x64 Manjaro Linux 18.1.5

Issue description:

I'm using a Node with a tool script to open a file and generate a custom resource. For instance, using a Twine/Yarn dialogue data to generate a native godot resource (.tres) with ResourceSaver.save().

The issue is when I need to re-import this data and overwrite the .tres file. If this resource is already loaded in the resource cache/history of the editor, the .tres will be updated but if I save/run any scene, it will be overwritten again with the cached data.

To fix it I need to import the data and restart the editor.

Steps to reproduce:

  1. Open the mininal reproduction project;
  2. Run the Main scene (it will print a "Lorem ipsum" imported from data1.txt);
  3. Re-import data using the data2.txt file (select ImportData in the Scene Tree, change the path and click in the import checkbox); 3.1. Open the data.tres file in an external text editor (its content will be a GNU/Linux Interjection quote);
  4. Run the Main scene again (it will print a "Lorem ipsum" again); 4.1. Open the data.tres file in an external text editor again (its content will be a Lorem ipsum);

Minimal reproduction project:

custom-resource-update.zip

erodozer commented 5 years ago

I'm having this issue with pretty much any custom resources tres file. If you change any data in an external application it will not resync in Godot, external including running your own game from the editor. Appears that the engine is not watching changes to the filesystem for file modification, only file creation and deletion.

A fast workaround to fix this would be to expose the ability to manually reindex the project with a button on the FileSystem inspector in Godot editor. This is a common ability that I see in many IDEs that index their projects, such as IntelliJ. There should also probably be a filesystem watcher (inotify) that recursively checks and reimports files if it detects any changes potentially from external editors. This would help significantly when dealing with things like git merge conflicts while the editor is open, since I usually always have it running when working on a project.

NathanLovato commented 5 years ago

Just like @nhydock Nicholas points out above, we have the issue with [Open RPG](): we're trying to use Godot's Resources to store most data, but anything saved during a debug run of the game with ResourceSaver.save, the entire tree of Resources this Resource holds will be reverted to their default values upon closing the game.

I wanted to use this so we could save and load debug savegames and other user data like game settings in the res:// folder during development to be able to edit and test the resources straight in the inspector, for testing purposes.

reduz commented 5 years ago

i think what the editor does is read the properties from disk and update them on the running resource. This works for some resources but not all of them. Maybe a method you can override to do your own logic would be enough?

NathanLovato commented 5 years ago

That sounds fine by me :)

williamd1k0 commented 5 years ago

@reduz Maybe a method you can override to do your own logic would be enough?

How exactly would that work?

nicolasdiehl commented 5 years ago

// edit, seems to be a won't fix https://github.com/GDquest/godot-open-rpg/issues/159#issuecomment-450737664 ... will read about the other ways to store files.. maybe my git problem can be solved.

Godot 3.1.beta.calinou.9ca6ffa win x64: Open VehicleBody stored in res:// in Godot, Open VehicleBody in text editor, Change mass in text editor and save, --> Mass of VehicleBody doesn't change in Godot. Close VehicleBody in Godot without saving or changing something, --> Text editor complains that mass of VehicleBody changed back to old value.

So to pull changes from git it seems that I first have to close Godot, then pull, then reopen Godot. My suggestion would be only save when pressign save. Reload when opening / pressing a new "reload from disk" button / option to always directly reload disk changes.

akien-mga commented 4 years ago

Is this still reproducible in the current master branch / latest 3.2 builds?

williamd1k0 commented 4 years ago

Yep, still reproducible. Tested with master (a0ce88f953a4311edc726523ff8724b891407855).

MikeMnD commented 4 years ago

Just to add up another case where the editor doesn't refresh the .tres

I have a Tileset with collisions if I add a collision manually in the .tres and if this is a new collision shape for a particular tile. It's loaded fine. But if I edit existing shape than the new shape is not loaded until I close the Godot editor and reopened it again.

First thing I was looking was some button "Reload resource" or "Reimport" So it'll be really good is something like this is added.

Calinou commented 4 years ago

But if I edit existing shape than the new shape is not loaded until I close the Godot editor and reopened it again.

If you save the scene, you can use Scene > Revert Scene Reload Saved Scene to reload the scene file on disk. This is faster than closing and reopening the editor. There's no keyboard shortcut associated to that action by default, but you can define one in the Editor Settings. (If we can find a sensible default shortcut, it'd make sense to add one.)

raptdwar commented 4 years ago

Are there any news about this issue? I was implementing an in-game editor like @NathanLovato said in the previous comment. For my use case I managed to bypass the actual problem by making a copy of the targeted resource in the user:/ folder and checking if the file exists there before the actual resource in the res:/ directory.

This will definitely help anyone making in-game resource editors

Calinou commented 4 years ago

@raptdwar As far as I know, nobody has started working on a fix yet.

cgbeutler commented 4 years ago

Anyone know a workaround?

I made an editor that saves off textures to res, but it's having this issue..

Shadowblitz16 commented 3 years ago

This really needs to be addressed. I shouldn't have to implement a custom handler or call reload or update through setters. The editor should do all this for us.

setzer22 commented 3 years ago

Note that the Scene > Revert Scene approach suggested above does not seem to work (anymore?). At least not with AnimationTrees as resources. This leaves me with no other choice than restarting the editor every time I want to see the changes to my procedurally generated resources.

aaaaaaaaargh commented 3 years ago

Looks like my issue may be related to this one. In my case I want to create multiple resources in a custom importer. Right now I'm simply doing it by calling:

ResourceSaver.save(filename, res, ResourceSaver.FLAG_REPLACE_SUBRESOURCE_PATHS | ResourceSaver.FLAG_CHANGE_PATH)

I'm doing this to produce multiple files and even write to the imported file itself without any problems on filesystem level. However, the editor simply will not update the resources as it should, it either displays some cached version of the file or simply throws an error about no valid resource loader being found.

I tried many things, including re-loading the resource after it has been saved, copying the file manually using Directory.copy(), using .take_over_path() but none of them seemed to have worked.

What I'm expecting is the editor to reload the resource once I have updated it using the ResourceSaver. The observed behaviour does not update anything, at least not in a reliable way. So that begs the question: Is this some kind of bug or am I doing the wrong thing here?

cgbeutler commented 3 years ago

It seems there are a few errors being posted about. For those working on tool scripts and just want the editor to update its references, this may work:

Workaround I use:

The main issue is that the editor has loaded and cached the resource already. That cached ref is already held by all kinds of things and there may not be a way to really replace all of their references.

In other words, we need to maintain the cached value, even when saving. You can call ResourceLoader.exists( path ) to see if it was already saved or ResourceLoader.has_cached( path ) if you just care about cache. If the cache is there, you can call 'ResourceLoader.load' to get that cached value. From there, change what data you need to and then you can re-save it.

Here is an example from the included minimal project:

tool  #<- must be tool mode to get editor's cached value
...
func import():
    var f = File.new()
    f.open(path, f.READ)
    var text = f.get_as_text()

        # Get cached value first, then update and save.
    var res
    if ResourceLoader.exists('res://data.tres'):
        res = ResourceLoader.load('res://data.tres')
    else:
        res = CustomData.new()

    res.data = text

    ResourceSaver.save('res://data.tres', res)
    OS.alert('Saved to res://data.tres', 'Import')

NOTE: This may occasionally have issues if you are editing the script of the thing being saved. The saved file may then get out of line with the script. In those cases you may need to delete the saved file or restart the editor. Other than that limitation, this method will at least let you keep working most of the time.

raptdwar commented 3 years ago

Does #31747 resolve this issue?

williamd1k0 commented 3 years ago

Does #31747 resolve this issue?

Probably not, as it appears to be related only to scene files and project.godot (https://github.com/godotengine/godot/pull/31747/files#diff-b88f0bde58b731e8dd353707c8011fb7a1e452f0db2c9f15f4b7905d60e5e52eR884).

FeralBytes commented 3 years ago

If #45880 is included in 3.2.4rc4 then it does not fix this issue.

neilfranci commented 3 years ago

So basically no easy way to fix this issue ? I'm on 3.3.rc9 :(

cgbeutler commented 3 years ago

So basically no easy way to fix this issue ? I'm on 3.3.rc9 :(

Check out my earlier reply for a workaround. Maintaining resource refs is good to learn anyway.

To add a similar bug's workaround: If you mainly have an issue with losing data after editing scripts, you may need to export more variables. When the editor sees a resource's script updated, it will then construct a new one and copy all exported variables over to the new one. That means any data not in init, default values, or exports will be lost.

neilfranci commented 3 years ago

So basically no easy way to fix this issue ? I'm on 3.3.rc9 :(

Check out my earlier reply for a workaround. Maintaining resource refs is good to learn anyway.

To add a similar bug's workaround: If you mainly have an issue with losing data after editing scripts, you may need to export more variables. When the editor sees a resource's script updated, it will then construct a new one and copy all exported variables over to the new one. That means any data not in init, default values, or exports will be lost.

I'm currently use TexturePacker importer and export from there as spritesheets(png and tpsheets(json)) but my work not just make all assets and import once some time I need to add new texture to spritesheet or remove some. And yes the editor doesn't seem to update the region, since the plugin create a atlas. But when I check region in resources fille (.tres) is updated the region but not in the editor.

cgbeutler commented 3 years ago

If the plugin is creating resources that are already saved to disk, then read my big guide above. You need to load all previously saved resources first to get the cached version, then update and save them. Even if you don't plan on keeping any of the old data, you still need to load first.

If you are doing all that correctly, then you should be able to collapse and expand the resource in the inspector to see the new data. If that doesn't work, you are probably loading the old one wrong. If collapsing and re-opening does refresh the data, then you at least have that as a workaround till it's fixed.

If you didn't write the plugin with issues, submit a bug to that plugin.

NGermany commented 3 years ago

This bug costed me hours of work this morning. I finally realized it wasn't my code but the editor doing strange things.

I'm not writing a plugin like the others. I'm just saving a resource from code and then trying to inspect it to make sure I got the correct results. The problem is that unless it's a new file the editor won't display the contents properly. In fact when I do re-save it without reloading the scene I get strange results, like data being out of place or just plain wrong. It's as if the cached resource IS attempting to be updated, but simply failing to do it properly and being entirely out of sync.

When I reload the saved scene (as suggested above by Calinou), or close and reopen Godot, the file's contents appear correct. I also now have a data printout before I save the file, so I know the data is correct either way. So from what I can tell there is obviously something very wrong with the way Godot attempts to update saved resources in the inspector.

Trystan-SA commented 3 years ago

Same issue, lost about 2 hours. It feels like I'm fighting the editor which is not a good thing at all.

hunterloftis commented 2 years ago

Is there no way to trigger updating a resource's representation in the editor? I just ran into this bug as well. With:

export(Texture) var surface_mask: Texture
# ...
surface_mask = _get_surface_mask()

The mask only updates in the editor after blurring and focusing the node.

KoBeWi commented 2 years ago

I can't reproduce the issue in 3.5 beta with the original reproduction project. Anyone can provide updated project or is the issue fixed?

KoBeWi commented 2 years ago

Closing due to no response. Comment with a new MRP if you can still reproduce in 3.5 or 4.0.

williamd1k0 commented 2 years ago

Sorry for the delay on replying.

So, as mentioned by @lyuma in https://github.com/godotengine/godot/issues/30302#issuecomment-1140052274, the problem can be solved by calling Resource.take_over_path(path) before or after saving the resource (assuming you are updating the resource via script).

Reproduction project using the approach mentioned above: custom-resource-update-fix.zip

I haven't tested it on recent versions of Godot yet.

KoBeWi commented 2 years ago

What are the exact steps to reproduce? I tried the project and seems like the resource is saved correctly even without take_over_path()

williamd1k0 commented 2 years ago

I just tried reproducing (without take_over_path()) in Godot v3.4.4 and it's working fine, so I assume the workaround is only needed in versions prior to v3.3.4.

KoBeWi commented 2 years ago

Well, then the issue is indeed resolved. Thanks for confirming.

levidavidmurray commented 1 year ago

So, I guess I was hoping Resource would act similar to Unity's ScriptableObject.

I have a Config.gd class that extends Resource which holds, as I'm sure you can guess, various game relevant config data. I've gone ahead and created a Config.tres which works all fine and dandy. However, if I add a new exposed variable to Config.gd, this change is not reflected in the editor. The only way I can get it to update is by either dereferencing the reference script (losing all my saved data), or restarting Godot.

Is this the intended functionality?

I'm using Godot v3.5.1 by the way.

KoBeWi commented 1 year ago

Is this the intended functionality?

No. The property list should be updating automatically.

ForestGameDev commented 11 months ago

It seems this is not fully resolved. In Godot 4.1 I created a new Resource type with an exported int. From a Tool script I assigned the int value:

my_resource.size = 10 #assigned in some other method

var my_vector = Vector2i(my_resource.size, my_resource.size)
print(my_vector) #produces (0,0)

I then tried saving the value to an intermediate variable, and that worked:

my_resource.size = 10  #assigned in some other method

var helper = my_resource.size
var my_vector = Vector2i(helper , helper)
print(my_vector) #produces (10,10)
KoBeWi commented 11 months ago

Can you attach a minimal project?

MoogieOuttaMyDepth commented 1 month ago

I am also having this issue in Godot 4.2.2 Stable.

I'm aware of the bugsquad edit up top that says it's been confirmed, but a project file was requested and I can provide that for you.

Problem: I have a custom resource, horse_atlas_resource.gd (horse_atlas_resource.tres) which defines a dictionary containing a series of sprite coordinates for differently-coloured horses on a texture atlas. At game load, the Main scene looks at this resource and randomly picks keys from it to spawn different coloured horses into the scene.

Bug: If I change horse_atlas_resource.gd to add a new horse colour, remove an existing one, or alter the coordinates of an existing one, none of these changes are reflected. And this is not just an editor issue: Restarting Godot does not fix the problem for me.

Workaround: I must manually delete and recreate horse_atlas_resource.tres, otherwise my changes to the .gd script are never reflected in the project.

See attached, hope it helps resolve this very serious issue.

Horses.zip

(When you load the project, the file you want to inspect is "horse_atlas_resource.gd". It has been commented to explain the issue same as I have here.)

KoBeWi commented 1 month ago

@MoogieOuttaMyDepth It's unrelated to this issue, which is about using ResourceSaver to save the resource using a script.

Your problem is that modifying the script does not mark the Resource as modified. Normally when you change a resource and press Ctrl+S, editor will automatically save all modified resources. It does not happen when changing the resource's script and you have to save it manually, from the save menu in the inspector: image (this way you don't have to re-create it)

Although this behavior is a result of another bug. The character_frames variable is saved even though it's equal to the default value. Normally if something is equal to default then it's not saved, so when you change the default in script, all saved resources will use it automatically (unless they have the value overriden).

And finally, your problem is discrepancy in default value in script vs default value saved in resource. But since you are using the default value anyway, you don't need the resource at all - you can use the Dictionary defined in script directly if they are supposed to be equal anyway. Resources are only useful when you change their properties in the inspector or using custom editors.

lucassene commented 4 weeks ago

I have a similar issue when using 4.3, although my changes are being saved in the file.

I have a simple Resource containing two exported Dictionaries that I change and save using a @tool script.

If I check the resource file in the editor after running the save method, it will appear as unchanged. BUT, if I check the file externally (Nodepad), my changes are there and saved. The issue is simply the resource visualization in the Inspector is not being updated. That can be confirmed when I restart the editor, and I can see all my changes to the Resource file in the Inspector.

Edit: Using the "Scene > Reload Saved Scene" updates the Resource file in the Inspector. Not ideal, though.