godot-proposals icon indicating copy to clipboard operation
godot-proposals copied to clipboard

Add a signal for when scripts reload

Open ydeltastar opened this issue 9 months ago • 5 comments

Describe the project you are working on

Prototyping a project. Hot reloading is great for fast iterations.

Describe the problem or limitation you are having in your project

Godot can hot reload edited scripts in both the editor and game running but there is no way to know if it happened. I often want to call a re-init function when a script reloads but there are no signals or callbacks for this event in Script or the various Editor interfaces and plugin extensions. We can't get notified from outside too like catching GDScript reloads from C# and GDExtension.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Script will emit a reloaded signal so we can be notified. It could be used like this:

var script = get_script() as Script
script.reloaded.connect(print.bind("%s reloaded" % script))

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Update: I implemented it in https://github.com/godotengine/godot/pull/91319

Emit it at the end of Script::reload Script.reload() can be used both at the editor and runtime so this signal shouldn't be an editor-only feature.

If this enhancement will not be used often, can it be worked around with a few lines of script?

I have to re-implement the file-watching system, dynamic source loading, and synchronization the editor already does.

Is there a reason why this should be core and not an add-on in the asset library?

There are no signals or callbacks for script reloading.

ydeltastar avatar Apr 28 '24 17:04 ydeltastar

Due to reload being an abstract virtual method internally, it looks like it will have to modify GDScript and CSharp as well for this to work.

This also requires modifying GDExtension to have it emit this.

radiantgurl avatar Apr 28 '24 18:04 radiantgurl

It appears to me like the script's _static_init gets called as part of the Script::reload. Have you tried to see if this is the case and can be used?

It's a static func, but you can find a way to get a list of instances of your script e.g. via a static var tracking the objects (add to list on _init), or through an Autoload Singleton that tracks all interesting reload events as global signals that the script listens to.

RedMser avatar Apr 28 '24 22:04 RedMser

Due to reload being an abstract virtual method internally, it looks like it will have to modify GDScript and CSharp as well for this to work.

I don't think the C# module supports individual script reloading yet, it reloads the whole assembly so the signal will be triggered for all scripts. It also doesn't restore the previous state like in GDScript so signal connections are lost after reloading. GDExtension doesn't work like managed scripts, we can't compile a C++ class at runtime using Godot's API.

It appears to me like the script's _static_init gets called as part of the Script::reload. Have you tried to see if this is the case and can be used?

I didn't notice I could use _static_init since it doesn't appear in the class reference. This could be a solution but it is called before the reloading process restores the state so static vars aren't assigned to previous values yet.

This will always print an empty array.

static var instances = []

static func _static_init() -> void:
	print(instances)

func _init() -> void:
	instances.append(self)

call_deferred has the same result. It will need to depend on other scripts and extra variables as you suggested. We can't get notified from outside too without modifying the script or catching GDScript reloads from C# or GDExtension. A post-reload signal would be more convenient.

ydeltastar avatar Apr 28 '24 23:04 ydeltastar

Have you tried the Resource.changed signal? A script is a resource too.

dalexeev avatar Apr 29 '24 17:04 dalexeev

Have you tried the Resource.changed signal? A script is a resource too.

That's for changes in properties, it doesn't emit on script reload which is a different process: parsing, compiling, and restoring state.

ydeltastar avatar Apr 29 '24 18:04 ydeltastar