#Updating the resource file in real time when attached script saved.

90 messages · Page 1 of 1 (latest)

wild python
#

I have a resource with a script attached but its values don't update until I overwrite the .tres each time I update the script by right clicking on the script and creating a new resource. Is there any better way to update the actual contents of the resource just as I update the script? (not via the editor, its not ideal for nested dictionaries for me)

granite hamlet
#

Isn't clicking the arrows back in the values sufficient to reset them?

wild python
#

I can see in the editor when I double tap the resource file that it has those new fields, but when I open the .tres in an external editor, I can see it has no new fields or functions until I overwrite it.

granite hamlet
#

If the fields have default settings, then they won't be written. But if you add a new value that's not default, it will be written to the file

#

In theory, at least

#

There's no reason to save fields that are similar to the default

wild python
#

Well, they aren't. I can make a new variable in the script and save the script and the .tres remains as it is.

granite hamlet
#

Let me understand. You make a resource script. Create a new resource from this type.

Then, you edit the script, you add an @export stuff := "".
You click the resource in the editor, and set "stuff" to "foo"

#

And "foo" does not get written to the resource file?

#

Is that the behavior you're describing?

wild python
#

Not really.

granite hamlet
#

As long as you do not set "stuff" to something else than "", there's no reason for Godot to write "stuff" to the saved resource. Because upon loading, the value of the resource is the same as the default value in the script.
It only needs to save this value if "stuff" is changed.

wild python
#

I edit the script, I click the Resource in the editor, it shows that it has updated (the new field is there, foo is now editable via the Godot editor). But, when I open the resource.tres in the text editor of my choice, the new field is not there. For the field to be there, I have to Script.gd Right click > New > Resource and overwrite the existing resource.

#

This is my behaviour

granite hamlet
#

Yes, that is an expected behavior

#

Resources are expected to be edited through the editor

#

There's no provision to update resources with default values if the script changes. It'd be prohibitively expensive to do this on each script change

#

The conversion happens on a per-need basis

wild python
#

The problem is, in my game, I don't see the changes many times until I overwrite the resource file itself.

#

Let's say I add a new signal to a value. That won't emit.

granite hamlet
#

Can you describe this more? That does sound like a bug.

wild python
#

I have value var foo
I go in the script (that is attached to the resource), I add a signal in my script.gd. This signal is emitted via foo.set when the foo value changes. Now, I open the game and do action which must emit this signal. Nothing happens.

#

I go ahead, overwrite the existing resource file and it works.

granite hamlet
#

Hmmm that definitely sounds like a bug

#

This said, for your current problem: is it possible to simply create a new fresh resource on run?

#

While you're working, at least

#

You can always stop that once your resource crystallizes

wild python
#

You mean via Resource.new() ?

granite hamlet
#

Yea

#

Create it, save it

#

And then use it

#
func _init():
  const my_resource := preload("MyResource.gd").new()
  ResourceSaver.save(my_resource, "res://resource.tres")
wild python
#

Not really. What I am doing is basically storing the player attributes for that run in the resource, many scripts read from this and signals go all over the place, its highly coupled. So if I do new on it, I'll have to pass it from script to another to access the same thing.

granite hamlet
#

Save it and load it everywhere as normal

wild python
granite hamlet
#

Maybe there's a flag in case the file exists, I'm not sure

#

But I think it overwrites by default

wild python
#

Alright, thanks, I'll try this later. Looked into the docs, no flag for overwrite or file already exists type stuff so should be fine, perhaps.

granite hamlet
#

In case, you can delete then write

#

You can also create an editor script to run this quickly

#

If you prefer doing it before the game

wild python
#

That actually sounds like the best idea, a singleton type script that just overwrites all the existing resources.

granite hamlet
#

Or a plugin I mean, but an editor script is quick n easy

wild python
#

Tysm, the editorscript is what I need here.

#

Wait, how do I delete a file with a known path?

granite hamlet
#

Godot 3 or 4?

wild python
#

4

granite hamlet
#
var dir = DirAccess.open("res://")
dir.remove("file.tres")

or

var file_to_remove = "res://file.tres"
OS.move_to_trash(ProjectSettings.globalize_path(file_to_remove))
#

2nd one sends to trash

wild python
wild python
granite hamlet
#

oh right

#

Just out of a long javascript session

wild python
#

Unexpected identifier on this gdthinking

#

assigned it, error gone

#

Doesn't seem to work. Added a flag that says the path is changed. Still nothing.

granite hamlet
#

Hmmm what doesn't work?

#

wait it's inverted

wild python
#

I am running it from File > Run

granite hamlet
#

path, then resource

wild python
#

It's Resource, Path in the docs

#

May be it's different for 3.5

#

I tried creating a new file too, like with a different name. Doesn't show up.

#

Trying to look into what error codes its printing

#

Nevermind, my spelling error

granite hamlet
#

You shouldn't need any flag

wild python
#

I am adding it just in case

#

0 is OK, its printing 0

granite hamlet
#

Ah but you named it tres_2, it won't show in the Godot filesystem

wild python
#

But the resource file is not getting updated still.

wild python
granite hamlet
#

try a new file name then? maybe it's silently not overriding

#

try deleting the previous file first

wild python
#

So, it seems like a bug, since even on saving like this, no matter what unique name I give to the file, when it saves through Godot, the resource file is not updated

#

Let me try the manual overwrite now, posting a before just in case

#

before pic

#

Okay, it works, sorry my bad I didn't add export to the new var that I saved.

#

Yes this EditorScript works, thank you

granite hamlet
#

Aright cool

sleek patio
#

Do you mind share the script 🥺

wild python
#
@tool
extends EditorScript

func _run() -> void:
    var loader = \
        preload(<your_existing_script_path>).new()

    var saver = ResourceSaver.save\
        (loader, <path_where_you_save_tres_to>, 4)
    
    
    print(saver) # For debug purposes
sleek patio
#

It took me a while to get a resource. Note I used ResourceLoader.CACHE_MODE_IGNORE and ResourceSaver.FLAG_CHANGE_PATH

My resource script

## File: player_run_data.gd
extends Resource
class_name PlayerRunData

@export var move_speed:float = 1.2
@export var health:float = 1.2

Then in my Node2D

## File: resource_test.tscn
extends Node2D
@export var player_run:PlayerRunData

Saver script

## File: resource_saver.gd
@tool
extends EditorScript

var from_path := "res://resource_life_cycle/player_run_data.gd"
var to_path := "res://resource_life_cycle/player_run_data_latest.tres"

func _run() -> void:
    var loader = ResourceLoader.load(from_path, '', ResourceLoader.CACHE_MODE_IGNORE).new()
    var saver = ResourceSaver.save(loader, to_path, ResourceSaver.FLAG_CHANGE_PATH)

    print(saver) # For debug purposes

Thanks for sharing 🙂

granite hamlet
#

You don't need ResourceLoader, preload is better because it will verify the path and will type correctly.

Here's a script

# res://GameData.gd
class_name GameData extends Resource

const DEFAULT_PATH = "res://test_game_data.tres"

export var name := ""
export var health := 100

func save() -> void:
    var error := ResourceSaver.save(resource_path, self)
    if error != OK:
        push_error("Could not save the file '%s'"%[resource_path])


static func restore(file_path := DEFAULT_PATH) -> GameData:
    if File.new().file_exists(file_path):
        return ResourceLoader.load(file_path) as GameData
    # We need to use `load()` because static functions can't reference their
    # own class name
    var GameData = load("res://GameData.gd")
    var game_data = GameData.new()
    game_data.take_over_path(file_path)
    game_data.save()
    return game_data

And the tool script:

tool
extends EditorScript


func _run() -> void:
    var game_data := GameData.restore()
#

Anywhere you need the game data, just do var game_data := GameData.restore()

granite hamlet
#

Here's the Godot 4 version of it:

# res://GameData.gd
class_name GameData extends Resource

const DEFAULT_PATH = "res://test_game_data.tres"

@export var name := ""
@export var health := 100

func save() -> void:
    var error := ResourceSaver.save(self, resource_path)
    if error != OK:
        push_error("Could not save the file '%s'"%[resource_path])


static func restore(file_path := DEFAULT_PATH) -> GameData:
    if ResourceLoader.exists(file_path):
        return ResourceLoader.load(file_path) as GameData
    var game_data := GameData.new()
    game_data.take_over_path(file_path)
    game_data.save()
    return game_data

Tool script:

@tool
extends EditorScript


func _run() -> void:
    var game_data := GameData.restore()
#

Anywhere in the game you need access to the script, just do:

var game_data := GameData.restore()

and you have your singleton, with saving/loading included

#

If you don't want to use ClassNames, then you can do:

@tool
extends EditorScript

func _run() -> void:
    var game_data := preload("GameData.gd").restore()