Adding a node to a scene file via an editor script

Godot Version

v4.1.2.stable.mono.official [399c9dc39]

Question

I want to add a node to an existing scene.tscn but I can’t get it to work whatever I try the node isn’t getting added to the scene. This is where I’m at:

	public override void _Run()
	{
		Node nodeToAdd = new Node(); // Your node to add
		nodeToAdd.Name = "Test123";
		AddNodeToScene(nodeToAdd, FindSceneByName("level_01"));
	}
public void AddNodeToScene(Node nodeToAdd, string scenePath)
	{
		var scene = GD.Load<PackedScene>(scenePath);
		if (scene != null)
		{
			var instance = scene.Instantiate();
			var sceneTree = (SceneTree) Engine.GetMainLoop();
			sceneTree.Root.AddChild(instance);
			
			if (instance is Node sceneNode)
			{
				instance.AddChild(nodeToAdd);
				nodeToAdd.Owner = instance; //needs to be after the AddChild()

				PackedScene packedInstance = new PackedScene();
				packedInstance.Pack(instance);
				ResourceSaver.Save(packedInstance, scenePath);
				GD.Print("(AddNodeToScene) Node added to the scene successfully.");
			}
	}

I already got some help in discord but that want enough to get it to work. The api has nothing useful on this. I’m instantiating the tscn file, then I add it to the scene, add the node to the scene instance, then I pack and save it. All I get is:
Script Started:

File found at path: res:///level_01.tscn
Test: res:///level_01.tscn
File found at path: res:///level_01.tscn
(AddNodeToScene) Node added to the scene successfully.

There is no error not feedback not anything I can work with.

It does show up in the file when I open it:

[gd_scene format=3 uid="uid://c8hco1o8xxplv"]

[node name="@Node3D@28376" type="Node3D"]

[node name="Test123" type="Node" parent="."]

I’ve tried changing it manually to something like:

[gd_scene format=3 uid="uid://c8hco1o8xxplv"]

[node name="@Node3D@28376" type="Node3D"]

[node name="Test123" type="Node3D" parent="@Node3D@28376"]

But that doesn’t make it show the node in the scene view either:
Screenshot 2023-12-26 22:07:56

I got the suggestion to run the project for the node to show up but it didn’t work.
I’ve noticed that the [node name=“Test123” type=“Node” parent=“.”] is removed from the file when I run the project.

Seems like the editor keeps the old PackedScene loaded in memory and won’t update it to the new PackedScene. Reusing the PackedScene object to pack() the new node seems to work.

Here’s how I got it to work in GDScript:

@tool
extends EditorScript


func _run() -> void:
	var path = 'res://test_add_node_from_editor_script.tscn'
	var packed_scene = load(path) as PackedScene

	var scene_root = packed_scene.instantiate()
	var node = Node.new()
	node.name = "Test123"
	scene_root.add_child(node, true)
	node.owner = scene_root

	var result = packed_scene.pack(scene_root)
	print('Scene packed: ', error_string(result))
	if not result == OK:
		return

	result = ResourceSaver.save(packed_scene, path)
	print('Scene saved: ', error_string(result))
	if not result == OK:
		return

	var opened_scenes = EditorInterface.get_open_scenes()
	if path in opened_scenes:
		print('Reloading scene')
		EditorInterface.reload_scene_from_path(path)

Should also work in C#

I’ve replicated it as well as I could:

	public void AddNodeToScene(Node nodeToAdd, string scenePath)
	{
		if (nodeToAdd == null)
		{
			GD.PrintErr($"(AddNodeToScene) Node {nodeToAdd} is null.");
			return;
		}

		if (scenePath == null)
		{
			GD.PrintErr("(AddNodeToScene) Scene path {scenePath} is null.");
			return;
		}

		var packedScene = GD.Load<PackedScene>(scenePath);
		if (packedScene != null)
		{
			//create scene file instance
			var sceneRoot = packedScene.Instantiate();
			var editorInterface = GetEditorInterface();

			if (sceneRoot is Node sceneNode)
			{
				sceneRoot.AddChild(nodeToAdd, true);
				nodeToAdd.Owner = sceneRoot; //needs to be after the AddChild()
	
				packedScene.Pack(sceneRoot);
				ResourceSaver.Save(packedScene, scenePath);
				var openedScenes = editorInterface.GetOpenScenes();
				if (openedScenes.Contains(scenePath));
				{
					GD.Print($"AddNodeToScene) Reloading scene");
					editorInterface.ReloadSceneFromPath(scenePath);
				}
				GD.Print($"(AddNodeToScene) Node added to the scene successfully.");
			}
			else
			{
				GD.PrintErr("(AddNodeToScene) Loaded scene is not of type 'Node'.");
			}
		}
		else
		{
			GD.PrintErr("(AddNodeToScene) Failed to load the scene.");
		}
	}

It almost works. The node is added. Esp. when the scene is not open.
When it’s open it takes ~10 to 30 seconds and I have to press:
Screenshot 2023-12-27 11:42:24

for it to show. I’m not sure but maybe the:

var openedScenes = editorInterface.GetOpenScenes();
if (openedScenes.Contains(scenePath));

is incorrect.

That ; in the line if (openedScenes.Contains(scenePath)); seems wrong. Not sure how C# parses it.

Yea that is wrong but it doesn’t change the delayed refresh when I rem it.

I don’t know then, sorry.