PkmnLib.NET/PkmnLib.Dynamic/ScriptHandling/Registry/ScriptRegistry.cs

119 lines
4.8 KiB
C#
Raw Normal View History

2024-07-27 14:26:45 +00:00
using System.Linq.Expressions;
using System.Reflection;
2024-07-28 10:52:17 +00:00
using JetBrains.Annotations;
2024-07-27 14:26:45 +00:00
using PkmnLib.Dynamic.Libraries;
2025-01-10 10:58:23 +00:00
using PkmnLib.Static;
2024-07-28 10:18:12 +00:00
using PkmnLib.Static.Utils;
2024-07-27 14:26:45 +00:00
2024-07-28 10:52:17 +00:00
namespace PkmnLib.Dynamic.ScriptHandling.Registry;
2024-07-27 14:26:45 +00:00
2024-07-28 10:52:17 +00:00
/// <summary>
/// A helper data class that's passed through all plugins when initializing a library, so they can
/// register their scripts and other data.
/// </summary>
2024-07-27 14:26:45 +00:00
public class ScriptRegistry
{
2024-07-28 10:52:17 +00:00
private readonly Dictionary<(ScriptCategory category, StringKey name), Func<Script>> _scriptTypes = new();
2025-01-10 10:58:23 +00:00
private readonly Dictionary<StringKey, Func<IItem, ItemScript>> _itemScriptTypes = new();
2024-07-27 14:26:45 +00:00
private IBattleStatCalculator? _battleStatCalculator;
private IDamageCalculator? _damageCalculator;
private IMiscLibrary? _miscLibrary;
2025-01-10 10:58:23 +00:00
private ICaptureLibrary? _captureLibrary;
2024-07-27 14:26:45 +00:00
2024-07-28 10:52:17 +00:00
/// <summary>
/// Automatically register all scripts in the given assembly that have the <see cref="ScriptAttribute"/>, and
/// inherit from <see cref="Script"/>.
/// </summary>
2024-07-27 14:26:45 +00:00
public void RegisterAssemblyScripts(Assembly assembly)
{
var baseType = typeof(Script);
foreach (var type in assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t)))
{
var attribute = type.GetCustomAttribute<ScriptAttribute>();
if (attribute == null)
continue;
RegisterScriptType(attribute.Category, attribute.Name, type);
}
2025-01-10 10:11:50 +00:00
var itemBaseType = typeof(ItemScript);
foreach (var type in assembly.GetTypes().Where(t => itemBaseType.IsAssignableFrom(t)))
{
var attribute = type.GetCustomAttribute<ItemScriptAttribute>();
if (attribute == null)
continue;
RegisterItemScriptType(attribute.Name, type);
}
2024-07-27 14:26:45 +00:00
}
2024-07-28 10:52:17 +00:00
/// <summary>
/// Register a script type with the given category and name.
/// </summary>
[PublicAPI]
2024-07-28 10:18:12 +00:00
public void RegisterScriptType(ScriptCategory category, StringKey name, Type type)
2024-07-27 14:26:45 +00:00
{
if (type == null)
throw new ArgumentNullException(nameof(type));
2024-12-22 11:19:42 +00:00
var constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null, Type.EmptyTypes, null);
2024-07-27 14:26:45 +00:00
if (constructor == null)
2025-02-01 14:26:57 +00:00
return;
2024-07-27 14:26:45 +00:00
// We create a lambda that creates a new instance of the script type.
// This is more performant than using Activator.CreateInstance.
_scriptTypes[(category, name)] = Expression.Lambda<Func<Script>>(Expression.New(constructor)).Compile();
}
2025-01-10 10:11:50 +00:00
/// <summary>
/// Register an item script type with the given name.
/// </summary>
[PublicAPI]
public void RegisterItemScriptType(StringKey name, Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
2025-01-10 10:58:23 +00:00
null, [typeof(IItem)], null);
2025-01-10 10:11:50 +00:00
if (constructor == null)
2025-01-10 10:58:23 +00:00
throw new ArgumentException($"Type {type} does not have a constructor that takes an IItem.");
2025-01-10 10:11:50 +00:00
// We create a lambda that creates a new instance of the script type.
// This is more performant than using Activator.CreateInstance.
2025-01-10 10:58:23 +00:00
var parameterExpression = Expression.Parameter(typeof(IItem), "item");
var newExpression = Expression.New(constructor, parameterExpression);
_itemScriptTypes[name] = Expression.Lambda<Func<IItem, ItemScript>>(newExpression, parameterExpression).Compile();
2025-01-10 10:11:50 +00:00
}
2024-07-27 14:26:45 +00:00
2024-07-28 10:52:17 +00:00
/// <summary>
/// Register a battle stat calculator.
/// </summary>
2024-07-27 14:26:45 +00:00
public void RegisterBattleStatCalculator<T>(T battleStatCalculator)
where T : IBattleStatCalculator => _battleStatCalculator = battleStatCalculator;
2024-07-28 10:52:17 +00:00
/// <summary>
/// Register a damage calculator.
/// </summary>
2024-07-27 14:26:45 +00:00
public void RegisterDamageCalculator<T>(T damageCalculator)
where T : IDamageCalculator => _damageCalculator = damageCalculator;
2024-07-28 10:52:17 +00:00
/// <summary>
/// Register a misc library.
/// </summary>
2024-07-27 14:26:45 +00:00
public void RegisterMiscLibrary<T>(T miscLibrary) where T : IMiscLibrary
=> _miscLibrary = miscLibrary;
2025-01-10 10:58:23 +00:00
/// <summary>
/// Register a capture library.
/// </summary>
public void RegisterCaptureLibrary<T>(T captureLibrary) where T : ICaptureLibrary
=> _captureLibrary = captureLibrary;
2024-07-27 14:26:45 +00:00
2025-01-10 10:11:50 +00:00
internal IReadOnlyDictionary<(ScriptCategory category, StringKey name), Func<Script>> ScriptTypes => _scriptTypes;
2025-01-10 10:58:23 +00:00
internal IReadOnlyDictionary<StringKey, Func<IItem, ItemScript>> ItemScriptTypes => _itemScriptTypes;
2024-07-27 14:26:45 +00:00
internal IBattleStatCalculator? BattleStatCalculator => _battleStatCalculator;
internal IDamageCalculator? DamageCalculator => _damageCalculator;
internal IMiscLibrary? MiscLibrary => _miscLibrary;
2025-01-10 10:58:23 +00:00
internal ICaptureLibrary? CaptureLibrary => _captureLibrary;
2024-07-27 14:26:45 +00:00
}