HeavenStudioPlus/Assets/Scripts/SourceGenerators/ControllerLoaderGenerator.cs
2024-01-18 21:38:01 -05:00

130 lines
4.1 KiB
C#

using static SatorImaging.UnitySourceGenerator.USGFullNameOf;
using SatorImaging.UnitySourceGenerator;
using System;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using UnityEditor;
#endif
using HeavenStudio;
using HeavenStudio.InputSystem;
// HOW TO USE: Add the following attribute to *target* class.
// Note that target class must be defined as partial.
//[UnitySourceGenerator(typeof(ControllerLoaderGenerator), OverwriteIfFileExists = false)]
public partial class ControllerLoaderGenerator
{
#if UNITY_EDITOR // USG: class definition is required to avoid build error but methods are not.
#pragma warning disable IDE0051
readonly static string MEMBER_ACCESS = "public static";
readonly static string MAIN_MEMBER_NAME = "InitInputControllers";
static string OutputFileName() => MAIN_MEMBER_NAME + ".cs"; // -> Name.<TargetClass>.<GeneratorClass>.g.cs
static bool Emit(USGContext context, StringBuilder sb)
{
List<PlayerInput.InputControllerInitializer> loadRunners = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(x => x.Namespace == "HeavenStudio.InputSystem.Loaders" && x.GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static) != null)
.Select(t => (PlayerInput.InputControllerInitializer)Delegate.CreateDelegate(
typeof(PlayerInput.InputControllerInitializer),
null,
t.GetMethod("Initialize", BindingFlags.Public | BindingFlags.Static),
false
))
.ToList();
// USG: static classes are IsAbstract is set.
if (!context.TargetClass.IsClass)
return false; // return false to tell USG doesn't write file.
// USG: you can modify output path. default file name is that USG generated.
// note that USG doesn't care the modified path is valid or not.
//context.OutputPath += "_MyFirstTest.txt";
// USG: EditorUtility.DisplayDialog() or others don't work in batch mode.
// throw if method depending on GUI based functions.
//if (UnityEngine.Application.isBatchMode)
// throw new System.NotSupportedException("GUI based functions do nothing in batch mode.");
// USG: write content into passed StringBuilder.
sb.Append($@"
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using HeavenStudio.InputSystem;
using HeavenStudio.InputSystem.Loaders;
using Debug = UnityEngine.Debug;
namespace {context.TargetClass.Namespace}
{{
partial class {context.TargetClass.Name}
{{
");
// class open ----------------------------------------------------------------------
#region // USG: MainMember
sb.Append($@"
{MEMBER_ACCESS} int {MAIN_MEMBER_NAME}()
{{
");
sb.IndentLevel(3);
sb.Append($@"
InputController[] controllers;
PlayerInputRefresh = new();
");
foreach (var loadRunner in loadRunners)
{
MethodInfo methodInfo = RuntimeReflectionExtensions.GetMethodInfo(loadRunner);
string callingClass = methodInfo.DeclaringType.Name;
string method = methodInfo.Name;
string fullMethodLabel = $"{callingClass}.{method}";
sb.Append($@"
controllers = {fullMethodLabel}();
if (controllers != null)
{{
inputDevices.AddRange(controllers);
}}
else
{{
Debug.Log(""{fullMethodLabel} had no controllers to initialize."");
}}
");
}
sb.Append($@"
return inputDevices.Count;
");
// USG: semicolon?
sb.Append($@"
}}
");
#endregion
// class close ----------------------------------------------------------------------
sb.Append($@"
}}
}}
");
// USG: return true to tell USG to write content into OutputPath. false to do nothing.
return true;
}
#pragma warning restore IDE0051
#endif
}