godotengine / godot

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

GetType().Name not returning the same in Addons #95974

Closed maxime4000 closed 2 weeks ago

maxime4000 commented 3 weeks ago

Tested versions

4.3-Stable

System information

Window 10 - 4.3-Stable

Issue description

I'm doing some Meta programming and I'm doing it using EditorPlugin because it's seem the right way to do it. Now GetType().Name won't return the right thing in addons...

Godot Engine v4.3.stable.mono.official.77dcf97d8 - https://godotengine.org
Vulkan 1.3.242 - Forward Mobile - Using Device #0: NVIDIA - NVIDIA GeForce RTX 3060 Ti

Hello from the Game!
Weapon Type: WeaponNode
--- Debugging process stopped ---
Hello from the plugin!
Weapon Type: Node3D

Unrelated question:

How do you debug EditorPlugin in C# with VSCode? I can't get the editor to be able to debug addons. Printing code is not fun! πŸ˜…

Steps to reproduce

GD.Print("Hello from the Game!");
var weaponScene = GD.Load<PackedScene>("res://WeaponScene.tscn");
var untypedWeapon = weaponScene.Instantiate(); // normally would be : weaponScene.Instantiate<WeaponNode>();
var weaponTypeName = untypedWeapon.GetType().Name;
GD.Print($"Weapon Type: {weaponTypeName}"); 

Minimal reproduction project (MRP)

mrp-addons-type.zip

Maximilian-Seitz commented 2 weeks ago

I'm pretty sure this is intended behavior, and inevitable, if your WeaponNode isn't a tool script.

Non-tool scripts don't get attached to nodes and resources during editor-time. They only get created during runtime.

maxime4000 commented 2 weeks ago

Addons have access to all the project files and classes, so I don't see why the typing should be wrong in that case. This information can be found by reading the Scene file, so I don't see why this is an issues. Here some question I'm curious to know for this issues:

Reflection

Do you think I Should insert a special command that I can trigger in my Game to execute a script that generate code that will be used by the game? I've learn that this should be living outside of the game's code, right? I mean, I could do this, but this is not right? Right?

(Though, it would fix my debugging issues at the same time πŸ€”πŸ˜…)

IMO

Tools should has the same knowledge that they would have at runtime? If I can do better stuff when running the game than when running a Script, this encourage bad design choice? I'm pretty sure that there are reasons to have it done this way, but I also think this is inconsistency in how limited Tools script could be.

Testing needed

What I didn't tried, is to do the same code but with GDScript. Would it be Node3D or WeaponNode ?

Maximilian-Seitz commented 2 weeks ago

To reiterate: Godot does not, and should not, instantiate your custom classes, when creating nodes and resources, as long as those custom classes aren't marked at tools (using the "Tool" attribute). You may still be able to access these classes, but the generated nodes will only include anything that is permittet to be instantiated by the editor.

The information about what script is attached to a node/resource can be easily found using the "script" property. If you want your custom nodes to be accessible during editor time, simply annotate them all using the Tool attribute; that's the intended behavior.

The "test" you're proposing will also result in a Node3D in GDScript, unless the "@tool" annotation is used by that custom node.

If you want to limit the scope of what classes are available to tool scripts, that would be an entirely different issue than what you originally proposed... Your original issue seems to only rely on a missunderstanding of how godot is meant to work, rather than a bug.

maxime4000 commented 2 weeks ago

Maybe? I mean, I'm searching ways to read scenes somewhere and generate some content somewhere else with it. This script is a run once in awhile. But the output I'm looking to generate is: Functional C# code. I need the proper type of the PackedScene dynamically read through folder navigation so I can type my C# code output accurately. If I can't get this information, I need to manually change the types of Node3D to WeaponNode or any other custom class and that's a no-go!

EditorPlugin seems the right place to execute those kinda task. I just think that the GetType().Name should be the proper type of the packedscene loaded from any context. Tools or Runtime!

But here is the primary issue

The documentation around EditorPlugin could be more explained. I feel that this part of the documentation could be improved greatly. There is no example that tries to solve a complex issues just for demonstration sake. The C# documentation is not fully on par with GDScript. And if those restrictions that you mentioned are by design, why this information is not available easily?

Asking for suggestions

But really, how would you build meta programming features for your project in my case? How would you do it? Where would you do it? Help me understand what I don't know that if I knew, it would help me achieve my goals easier?

Here is a example of the line I'm looking to generate: (consider preloading assets the next steps after)

public static WeaponNode PistolA => GD.Load<PackedScene>("res://scenes/weapons/PistolA tscn").Instantiate<WeaponNode>();

But in the Tools scope, I get this output:

public static Node3D PistolA => GD.Load<PackedScene>("res://scenes/weapons/PistolA tscn").Instantiate<Node3D>();
AThousandShips commented 2 weeks ago

But really, how would you build meta programming features for your project in my case? How would you do it? Where would you do it? Help me understand what I don't know that if I knew, it would help me achieve my goals easier?

This isn't really the place to ask support questions, please ask in the other community channels instead and lets focus on actual bugs here, does it work correctly when using [Tool]? If so then this can be closed, otherwise we need to pin down what else might be wrong, but let's stay on topic

maxime4000 commented 2 weeks ago

If so then this can be closed, otherwise we need to pin down what else might be wrong, but let's stay on topic

No, that IS the reason why I think this issue is relevant.

Why can't it GetType() correctly here when the same code elsewhere can do it right? It's inconsistant and that's not explained...

Maybe in the Engine Code it can be explained, but as a Developer and User of the Engine, my worries are about building my game, not about limitation of the Tool.

All I'm looking for is a solution that I can have the same code work exactly the same in the game or as a Script. If behavior is different between those environment, where can I run a Script that will use the right type? If RunTime is better, why can't I have a Plugin that use the Runtime environment and features?

How can we solve that? How can I solve an issue that :

If you can't respond with a proper solution to thoses questions am asking, I think you are not the right person to debate about the irrelevance of my issue.

I'm looking for a proper fix for this inconsistency or a proper solution to do the same without the issues I'm facing. Then this issues can be close.

I'm looking for one of those 2 changes:

AThousandShips commented 2 weeks ago

What limitation are you talking about? That you need [Tool] to run code in the editor? That's well documented

maxime4000 commented 2 weeks ago

That GetType().Name don't return the same string value in one of the 2 environment! I'm reading the same file, running the same code, but the result differ and I don't think it should differ.

I want to use the Godot SDK as a way to generate code, batch transform GDScene or GDResource file to something else. That's a the purpose of a generator or a Script! And EditorPlugin seem the correct way to do those things? But! I can't because the environment is inferior to the Runtime environment for an unknown reason that I think this limitation should not exist or a Proper workaround should exist!

I'm trying to build something similar to Generator with Prisma, but instead of analysing a DMMF.Model, I'm analysing a GDScene. Isn't EditorPlugin the proper way to build Generator script?

For reference here is 2 repo that do Meta programming in a way that I would like to replicate with PackedScene and GetType().Name :

Reality I just want to know

How wrong I'm building my script? Because to my understanding, I'm using the right tools for the job and I get a worst experience for using the right tools... So what I am doing wrong and how I should be doing it instead, if it's "by design"? What is the "by design" way to do solve what I'm experiencing?

raulsntos commented 2 weeks ago

Scripts (both C# and GDScript) are only instantiated and executed at runtime by default, unless they are marked as tool scripts (with the [Tool] attribute in C# and the @tool annotation in GDScript). I think this is already well documented. See the Running code in the editor documentation page.

As a consequence of this, nodes and resources retrieved in a plugin that is running in the editor, will not have instantiated its script unless it's marked as tool.

Let's say you have a script MyNode like this one:

public partial class MyNode : Node2D
{
    [Export] public int MyNumber { get; set; }
}

Since the script is not marked as tool, when running in the editor we can't create an instance of MyNode, because of the rules just explained above. Instead, we use the base built-in type to create it (Node2D).

So when you retrieve a node with the MyNode script attached, you'll get a Node2D instance. The System.Object.GetType API will give you a System.Type instance that corresponds to the real type of the instance, which is Node2D (a MyNode instance was never created).

This is by design, because we don't want to execute a non-tool script in the editor. Even calling the constructor can have side-effects.

But this is just a consequence of the rule that non-tool scripts are not executed in the editor, if they are not executed in the editor they can't be instantiated and therefore an instance of MyType can't exist. I feel like this rule is well documented, but if you think it's not clear enough we can always improve the documentation.

Regarding your specific use-case, @Maximilian-Seitz already told you how to access the script information but you might have missed it. You can retrieve the script instance, for example:

var nodeScript = node.GetScript().As<Script?>();
if (nodeScript is not null && nodeScript.ResourcePath == "res://MyNode.cs")
{
    // The node has the 'MyNode' script attached.
    // Let's set the 'MyNumber' property.
    node.Set("MyNumber", 42);
}

Or you can also mark MyNode as a tool-script with [Tool]. That way, the script will be instantiated when running in the editor, and then you can use it as you normally would.

maxime4000 commented 2 weeks ago

@raulsntos Thanks for the details explanation. Thanks for the Demo about GetScript().As<Script?>(); That was the alternative I was looking for! I got the value I was looking for. I haven't yet test it out on a diverse set of scenes yet to see if it's a reliable alternative, but that seem to get the information I was looking for. Thanks!

Sorry @Maximilian-Seitz I was confuse about the meaning of the script part. Example of code would have make it clearer to me.

Now:

Response

Or you can also mark MyNode as a tool-script with [Tool]. That way, the script will be instantiated when running in the editor, and then you can use it as you normally would.

Maybe I'm not understanding it right, but should the Tool Attribute only being set when used for visually updating things through the editor? I mean, it feel like you should not use Tool unless you really need it, but maybe I did not understand that I should use it more often? So does it make sense to have many of your GlobalClass also being Tool ?

AThousandShips commented 2 weeks ago

Maybe I'm not understanding it right, but should the Tool Attribute only being set when used for visually updating things through the editor?

It's simply for running any code in the editor, check the link above for more details

Maximilian-Seitz commented 2 weeks ago

The documentation you're looking for can be found in the documentation page linked by @raulsntos. Specifically these subsections:

(These are just two mentions of these kinds of warnings I've found at a quick glance)

maxime4000 commented 2 weeks ago

I understand what you mean and to be completely honest, I'm writting mostly code and barely touch any Visual feature of the Editor for the past months of my Godot Journey.

I'm building an Action 3D Game with no knowledge of 3D. As I'm learning 3D slowly, I'm writing most of my game through code. My intents are that I'm building a "Framework" of the game I want to build, then when the main Gameplay loop will be fully functionnal, I will start working around the 3D features, which would implied a deep dive in this Tools side of the Engine at this point.

Because writing scripting is more code than any visual thing and because I have copied a other plugins C# code to understand how to make my own plugin, I have not read much on the Tools documentation and I did not consider that I would encounter one of the quirks of Tools and I'm sorry for not understanding that within the first response.

There are many issues that I have met with Godot and C# which aren't always obvious to solve. Some I encounter early on like:

If you search for GetType or GetScript, you won't find much in the documentation and if you haven't read the full documentation, you won't know that it's known and "by design" of a feature that you barely knew about, just that it allowed you to execute some script in the IDE.

I will be closing this issue. Thanks for your time and your help.