Code/Autoloader.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Braxnet;

/// <summary>
///  Creates objects with components that have the Autoload attribute. Will stay between scenes.
/// </summary>
public sealed class Autoloader : GameObjectSystem
{
	
	private readonly Dictionary<TypeDescription, Component> _components = new();
	
	public T GetComponent<T>() where T : Component
	{
		if ( _components.TryGetValue( TypeLibrary.GetType<T>(), out var comp ) )
		{
			return (T)comp;
		}

		return null;
	}

	private bool HasComponent( TypeDescription type )
	{
		return Scene.GetAllComponents( type.GetType() ).Any();
	}

	public Autoloader( Scene scene ) : base( scene )
	{
		if ( Scene.IsEditor ) return;
		
		Log.Trace( $"Autoloader initialized on scene {scene.Name}" );

		var autoloadTypes = TypeLibrary.GetTypesWithAttribute<AutoloadAttribute>().ToList();

		if ( !autoloadTypes.Any() )
		{
			Log.Trace( "No types to autoload" );
			return;
		}

		foreach ( var type in autoloadTypes )
		{
			if ( !HasComponent( type.Type ) )
			{
				if ( type.Attribute.Prefab != null )
				{
					var obj = SceneUtility.GetPrefabScene( ResourceLibrary.Get<PrefabFile>( type.Attribute.Prefab ) ).Clone();
					_components[type.Type] = obj.Components.Get( type.Type.GetType() );
					obj.Name = type.Type.Name;
					obj.Flags |= GameObjectFlags.DontDestroyOnLoad;
					Log.Trace( $"Autoloading prefab {type.Type.Name} -> {obj}" );
				}
				else
				{
					var obj = Scene.CreateObject();
					_components[type.Type] = obj.Components.Create( type.Type );
					obj.Name = type.Type.Name;
					obj.Flags |= GameObjectFlags.DontDestroyOnLoad;
					Log.Trace( $"Autoloading {type.Type.Name} -> {obj}" );
				}
			}
		}
	}
}

/// <summary>
///  Components with this attribute will be created by the Autoloader on scene start.
/// </summary>
[AttributeUsage( AttributeTargets.Class )]
public sealed class AutoloadAttribute : Attribute
{
	public string Prefab { get; set; }
	public AutoloadAttribute()
	{
	}
	
	public AutoloadAttribute( string prefab )
	{
		Prefab = prefab;
	}
}