Editor/TypedInputGenerator.cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using Editor;
public class TypedInputGenerator
{
[Menu( "Editor", "Input/Generate Typed Inputs" )]
public static void GenerateInputs()
{
if ( !Editor.FileSystem.ProjectSettings.FileExists( "Input.config" ) )
{
Log.Error( "[Input Generator] Could not find Input.config in ProjectSettings." );
return;
}
string jsonContent = Editor.FileSystem.ProjectSettings.ReadAllText( "Input.config" );
var actionNames = new HashSet<string>();
try
{
using var document = JsonDocument.Parse( jsonContent );
if ( document.RootElement.TryGetProperty( "Actions", out var actionsArray ) &&
actionsArray.ValueKind == JsonValueKind.Array )
{
foreach ( var action in actionsArray.EnumerateArray() )
{
if ( action.TryGetProperty( "Name", out var nameProp ) )
{
string rawName = nameProp.GetString();
if ( !string.IsNullOrWhiteSpace( rawName ) )
{
string cleanName = rawName.Replace( " ", "" );
actionNames.Add( cleanName );
}
}
}
}
}
catch ( JsonException e )
{
Log.Error( $"[Input Generator] Error parsing Input.config: {e.Message}" );
return;
}
if ( actionNames.Count == 0 )
{
Log.Warning( "[Input Generator] Did not find any action on Input.config." );
return;
}
string sourceCode = BuildCSharpCode( actionNames.ToList() );
string configPhysicalPath = Editor.FileSystem.ProjectSettings.GetFullPath( "Input.config" );
string projectRoot = System.IO.Path.GetDirectoryName( System.IO.Path.GetDirectoryName( configPhysicalPath ) );
string finalPath = System.IO.Path.Combine( projectRoot, "Code", "InputManager", "GeneratedInputs.cs" );
System.IO.Directory.CreateDirectory( System.IO.Path.GetDirectoryName( finalPath ) );
System.IO.File.WriteAllText( finalPath, sourceCode );
Log.Info( $"[Input Generator] Success! {actionNames.Count} inputs injected to {finalPath}." );
}
private static string BuildCSharpCode( List<string> actions )
{
var sb = new StringBuilder();
sb.AppendLine( "// AUTOGENERATED FILE. DO NOT MODIFY." );
sb.AppendLine( "using System;" );
sb.AppendLine( "using Sandbox;" );
sb.AppendLine();
sb.AppendLine( "namespace Sandbox.InputManager" );
sb.AppendLine( "{" );
sb.AppendLine( " public static class InputActions" );
sb.AppendLine( " {" );
sb.AppendLine( " public static class Pressed" );
sb.AppendLine( " {" );
foreach ( var a in actions )
{
sb.AppendLine( $" public static event Action {a};" );
sb.AppendLine( $" internal static void Trigger{a}() => {a}?.Invoke();" );
}
sb.AppendLine( " }" );
sb.AppendLine( " public static class Down" );
sb.AppendLine( " {" );
foreach ( var a in actions )
{
sb.AppendLine( $" public static event Action {a};" );
sb.AppendLine( $" internal static void Trigger{a}() => {a}?.Invoke();" );
}
sb.AppendLine( " }" );
sb.AppendLine( " public static class Released" );
sb.AppendLine( " {" );
foreach ( var a in actions )
{
sb.AppendLine( $" public static event Action {a};" );
sb.AppendLine( $" internal static void Trigger{a}() => {a}?.Invoke();" );
}
sb.AppendLine( " }" );
sb.AppendLine( " }" );
sb.AppendLine();
sb.AppendLine( " public sealed class InputBroadcasterService : Component" );
sb.AppendLine( " {" );
sb.AppendLine( " protected override void OnUpdate()" );
sb.AppendLine( " {" );
foreach ( var a in actions )
{
sb.AppendLine( $" if ( Input.Pressed( \"{a}\" ) ) InputActions.Pressed.Trigger{a}();" );
sb.AppendLine( $" if ( Input.Down( \"{a}\" ) ) InputActions.Down.Trigger{a}();" );
sb.AppendLine( $" if ( Input.Released( \"{a}\" ) ) InputActions.Released.Trigger{a}();" );
}
sb.AppendLine( " }" );
sb.AppendLine( " }" );
sb.AppendLine( "}" );
return sb.ToString();
}
}