Cat-Lips / GodotSharp.SourceGenerators

C# source generators for the Godot Game Engine
MIT License
121 stars 13 forks source link

[OnInstantiate] does not search all project scene files #67

Open valkyrienyanko opened 1 month ago

valkyrienyanko commented 1 month ago

My project is setup so scripts are separate from scene files

I have always organized my project by having a res://Scenes folder for all .tscn files and a res://Scripts folder for all .cs files. I'm not sure how I feel about keeping the .tscn files next to the .cs files. I might just refactor my entire projects file structure just to use this attribute.

The Issue

I get the following error when I try to use [OnInstantiate] in UIOptions.cs. This is because there is no UIOptions.tscn file in this path as its located under res://Scenes not res://Scripts.

Error: Cannot open file 'res://Template/Scripts/UI/Options/UIOptions.tscn'.

Actual Scene Path: res://Scenes/Prefabs/UI/options.tscn Actual Script Path: res://Template/Scripts/UI/Options/UIOptions.cs

valkyrienyanko commented 1 month ago

I've made the decision to refactor my project to make it so all scripts are right next to their respective scene files.

Cat-Lips commented 1 month ago

Yes, true. I have an alternate path for tscn parsing on SceneTree, but not for any other attribute. It would suck having to specify an alternative for all these attribute-based generators, so unfortunately this paradigm does tend to lend itself to grouping scene files with source code.

If it helps, I nest my tscn/tres files under my cs files in Visual Studio: https://github.com/Cat-Lips/F00F (see .filenesting.json)

(Of course, also open to any ideas about how to better support separation of scenes and source if that's what is preferred...)

valkyrienyanko commented 1 month ago

You could tell users to include the following in their main Godot .csproj file.

<ItemGroup>
    <AdditionalFiles Include="**\*.tscn" />
</ItemGroup>

And then you could loop through each .tscn file in your source generator and parse it.

[Generator]
public class SourceGen: ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        // Get all additional text files
        IEnumerable<AdditionalText> tscnFiles = context.AdditionalFiles
            .Where(file => Path.GetExtension(file.Path)
            .Equals(".tscn", StringComparison.OrdinalIgnoreCase));

                // Do something with tscnFiles here
         }
}

Example of how I use AdditionalFiles

Cat-Lips commented 1 month ago

Ok, great. By providing the additional files to a source generator, it can find the matching tscn. But in your case, I've just realised that the names are different (options.tscn/UIOptions.cs), so even if they were in the same folder or using an additional files lookup, we'd probably still need to use an attribute property...

If the name and path is the same, it's easy to replace the extension to get the scene file, eg:

public static T GetScene<T>() where T : GodotObject
   => LoadScene<T>().Instantiate<T>();

public static PackedScene LoadScene<T>() where T : GodotObject
    => GD.Load<PackedScene>(GetScriptPath<T>().Replace(".cs", ".tscn"));

public static string GetScriptPath<T>() where T : GodotObject
    => typeof(T).GetCustomAttribute<ScriptPathAttribute>(false).Path;

(https://github.com/Cat-Lips/F00F.Core/blob/main/addons/F00F.Core/Utilities/Utils.cs)

Cat-Lips commented 1 month ago

Anyway, I'll see what I can do to fix this for you.

Cat-Lips commented 1 month ago

But in your case, I've just realised that the names are different (options.tscn/UIOptions.cs), so even if they were in the same folder or using an additional files lookup, we'd probably still need to use an attribute property...

Or... could parse tscn files to get cs path and create a static lookup