Compare commits

...

20 Commits
0.9.12 ... main

Author SHA1 Message Date
DCFApixels
f50021007b up version to 0.9.18 2025-03-29 16:14:54 +08:00
DCFApixels
6ab2c8321a fix 2025-03-24 19:30:52 +08:00
DCFApixels
69075891af reflection optimization 2025-03-17 11:07:06 +08:00
DCFApixels
443a69615a up version to 0.9.17 2025-03-15 17:08:05 +08:00
DCFApixels
a0bec24fd2 defines refactoring 2025-03-15 15:10:16 +08:00
DCFApixels
0684b90584 fix 2025-03-15 15:09:11 +08:00
DCFApixels
f23f9692a2 defines refactoring 2025-03-14 16:53:33 +08:00
DCFApixels
99f6c30e2e fix 2025-03-14 15:00:53 +08:00
DCFApixels
4b4fb185eb add .md extension for LICENSE 2025-03-14 14:24:58 +08:00
DCFApixels
6a3b78026d Update AutoInjectSystem.cs 2025-03-13 20:48:22 +08:00
DCFApixels
1b2cf99a68 update the aspect auto-build 2025-03-13 13:38:58 +08:00
DCFApixels
ae49f52272 fix assignable types injection 2025-03-10 22:21:27 +08:00
DCFApixels
73c10e98ca add AutoInjectModule 2025-03-10 13:04:52 +08:00
DCFApixels
235b450f1a Update .gitignore 2025-01-06 10:50:46 +08:00
DCFApixels
889d02d1e2 code cleanup 2025-01-06 10:48:52 +08:00
DCFApixels
44f8df5391 up version to 0.9.16 2024-12-31 23:22:01 +08:00
DCFApixels
89cec06ae1 add named injection 2024-12-04 17:40:57 +08:00
Mikhail
435b5ac71d
Update LICENSE 2024-11-12 19:46:19 +08:00
DCFApixels
2517274fd8 update readme 2024-11-12 13:30:34 +08:00
DCFApixels
abcf75560f update readme 2024-11-12 13:26:21 +08:00
16 changed files with 326 additions and 158 deletions

1
.gitignore vendored
View File

@ -35,6 +35,7 @@ ExportedObj/
*.csproj *.csproj
*.unityproj *.unityproj
*.sln *.sln
*.sln.meta
*.suo *.suo
*.tmp *.tmp
*.user *.user

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Mikhail Copyright (c) 2023 Mikhail(DCFApixels)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -11,11 +11,35 @@
# Auto Injections for [DragonECS](https://github.com/DCFApixels/DragonECS) # Auto Injections for [DragonECS](https://github.com/DCFApixels/DragonECS)
| Languages: | [Русский](https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS-AutoInjections) | <table>
| :--- | :--- | :--- | <tr></tr>
<tr>
<td colspan="3">Readme Languages:</td>
</tr>
<tr></tr>
<tr>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<span>Русский</span>
</a>
</td>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS-AutoInjections">
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
<span>English(WIP)</span>
</a>
</td>
</tr>
</table>
</br>
Расширение призвано сократить объем кода, упростив инъекцию зависимостей, делая их автоматически. Расширение призвано сократить объем кода, упростив инъекцию зависимостей, делая их автоматически.
> **ВАЖНО!** Проект в стадии разработки. API может меняться.
> [!WARNING]
> Проект в стадии разработки. API может меняться.
# Оглавление # Оглавление
- [Установка](#установка) - [Установка](#установка)
- [Интеграция](#интеграция) - [Интеграция](#интеграция)
@ -37,7 +61,7 @@
Опционально: Опционально:
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д. + Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестированно: Протестировано:
+ **Unity:** Минимальная версия 2020.1.0; + **Unity:** Минимальная версия 2020.1.0;
## Установка для Unity ## Установка для Unity
@ -46,7 +70,7 @@
``` ```
https://github.com/DCFApixels/DragonECS-AutoInjections.git https://github.com/DCFApixels/DragonECS-AutoInjections.git
``` ```
* ### В виде иходников * ### В виде исходников
Фреймворк так же может быть добавлен в проект в виде исходников. Фреймворк так же может быть добавлен в проект в виде исходников.
</br> </br>

View File

@ -11,11 +11,34 @@
# Auto Injections for [DragonECS](https://github.com/DCFApixels/DragonECS) # Auto Injections for [DragonECS](https://github.com/DCFApixels/DragonECS)
| Languages: | [Русский](https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS-AutoInjections) | <table>
| :--- | :--- | :--- | <tr></tr>
<tr>
<td colspan="3">Readme Languages:</td>
</tr>
<tr></tr>
<tr>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md">
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"></br>
<span>Русский</span>
</a>
</td>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS-AutoInjections">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<span>English(WIP)</span>
</a>
</td>
</tr>
</table>
</br>
The extension is designed to reduce the amount of code by simplifying dependency injection by doing injections automatically. The extension is designed to reduce the amount of code by simplifying dependency injection by doing injections automatically.
> **NOTICE:** The project is a work in progress, API may change. > [!WARNING]
> The project is a work in progress, API may change.
>
> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md). > While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md).
@ -36,12 +59,12 @@ Tested with:
## Unity Installation ## Unity Installation
* ### Unity Package * ### Unity Package
The framework can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`: The package can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`:
``` ```
https://github.com/DCFApixels/DragonECS-AutoInjections.git https://github.com/DCFApixels/DragonECS-AutoInjections.git
``` ```
* ### Source Code * ### Source Code
The framework can also be added to the project as source code. The package can also be added to the project as source code.
</br> </br>

View File

@ -8,7 +8,7 @@
"displayName": "DragonECS-AutoInjections", "displayName": "DragonECS-AutoInjections",
"description": "Auto Injections for DragonECS", "description": "Auto Injections for DragonECS",
"unity": "2020.3", "unity": "2020.3",
"version": "0.9.12", "version": "0.9.18",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/DCFApixels/DragonECS-AutoInjections.git" "url": "https://github.com/DCFApixels/DragonECS-AutoInjections.git"

View File

@ -1,5 +1,10 @@
using DCFApixels.DragonECS.AutoInjections.Internal; #if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.AutoInjections.Internal;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
@ -8,7 +13,7 @@ namespace DCFApixels.DragonECS
{ {
protected sealed override void Init(Builder b) protected sealed override void Init(Builder b)
{ {
EcsAspectAutoHelper.Fill(this, b); //EcsAspectAutoHelper.Fill(this, b);
InitAfterDI(b); InitAfterDI(b);
} }
protected virtual void InitAfterDI(Builder b) { } protected virtual void InitAfterDI(Builder b) { }
@ -16,90 +21,119 @@ namespace DCFApixels.DragonECS
internal static class EcsAspectAutoHelper internal static class EcsAspectAutoHelper
{ {
public static void Fill(EcsAspect s, EcsAspect.Builder b) private static readonly MethodInfo _incluedMethod;
private static readonly MethodInfo _excludeMethod;
private static readonly MethodInfo _optionalMethod;
private static readonly MethodInfo _combineMethod;
private const BindingFlags REFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
static EcsAspectAutoHelper()
{ {
Type builderType = b.GetType(); Type builderType = typeof(EcsAspect.Builder);
MethodInfo incluedMethod = builderType.GetMethod("IncludePool", BindingFlags.Instance | BindingFlags.Public);
MethodInfo excludeMethod = builderType.GetMethod("ExcludePool", BindingFlags.Instance | BindingFlags.Public);
MethodInfo optionalMethod = builderType.GetMethod("OptionalPool", BindingFlags.Instance | BindingFlags.Public);
MethodInfo includeImplicitMethod = builderType.GetMethod("IncludeImplicit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo excludeImplicitMethod = builderType.GetMethod("ExcludeImplicit", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo combineMethod = builderType.GetMethod("Combine", BindingFlags.Instance | BindingFlags.Public);
Type aspectType = s.GetType(); _incluedMethod = builderType.GetMethod("IncludePool", REFL_FLAGS);
_excludeMethod = builderType.GetMethod("ExcludePool", REFL_FLAGS);
foreach (var attribute in aspectType.GetCustomAttributes<ImplicitInjectAttribute>())//TODO убрать дублирование кода - вынести в отедльный метод _optionalMethod = builderType.GetMethod("OptionalPool", REFL_FLAGS);
_combineMethod = builderType.GetMethod("Combine", REFL_FLAGS);
}
public static void FillMaskFields(object aspect, EcsMask mask)
{ {
if (attribute is IncImplicitAttribute incImplicit) foreach (FieldInfo fieldInfo in aspect.GetType().GetFields(REFL_FLAGS))
{
if (fieldInfo.GetCustomAttribute<MaskAttribute>() == null)
{ {
if (incImplicit.isPool)
incluedMethod.MakeGenericMethod(incImplicit.type).Invoke(b, null);
else
includeImplicitMethod.Invoke(b, new object[] { incImplicit.type });
continue; continue;
} }
if (attribute is ExcImplicitAttribute excImplicit)
if (fieldInfo.FieldType == typeof(EcsMask))
{ {
if (excImplicit.isPool) fieldInfo.SetValue(aspect, mask);
excludeMethod.MakeGenericMethod(excImplicit.type).Invoke(b, null);
else
excludeImplicitMethod.Invoke(b, new object[] { excImplicit.type });
continue;
} }
}//TODO КОНЕЦ убрать дублирование кода - вынести в отедльный метод else if (fieldInfo.FieldType == typeof(EcsStaticMask))
{
fieldInfo.SetValue(aspect, mask.ToStatic());
}
}
}
public static void FillFields(object aspect, EcsAspect.Builder builder)
{
Type aspectType = aspect.GetType();
var implicitInjectAttributes = (IEnumerable<ImplicitInjectAttribute>)aspectType.GetCustomAttributes<ImplicitInjectAttribute>();
FieldInfo[] fieldInfos = aspectType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo[] fieldInfos = aspectType.GetFields(REFL_FLAGS);
foreach (FieldInfo fieldInfo in fieldInfos) foreach (FieldInfo fieldInfo in fieldInfos)
{ {
Type fieldType = fieldInfo.FieldType; Type fieldType = fieldInfo.FieldType;
foreach (var attribute in fieldInfo.GetCustomAttributes<ImplicitInjectAttribute>())//TODO убрать дублирование кода - вынести в отедльный метод implicitInjectAttributes = implicitInjectAttributes.Concat(fieldInfo.GetCustomAttributes<ImplicitInjectAttribute>());
{
if (attribute is IncImplicitAttribute incImplicit)
{
if (incImplicit.isPool)
incluedMethod.MakeGenericMethod(incImplicit.type).Invoke(b, null);
else
includeImplicitMethod.Invoke(b, new object[] { incImplicit.type });
continue;
}
if (attribute is ExcImplicitAttribute excImplicit)
{
if (excImplicit.isPool)
excludeMethod.MakeGenericMethod(excImplicit.type).Invoke(b, null);
else
excludeImplicitMethod.Invoke(b, new object[] { excImplicit.type });
continue;
}
}//TODO КОНЕЦ убрать дублирование кода - вынести в отедльный метод
if (!fieldInfo.TryGetCustomAttribute(out InjectAspectMemberAttribute injectAttribute)) if (fieldInfo.TryGetCustomAttribute(out InjectAspectMemberAttribute injectAttribute) == false)
{ {
continue; continue;
} }
IEcsPool pool;
switch (injectAttribute)
{
case IncAttribute incAtr:
if (builder.World.TryFindPoolInstance(fieldType, out pool))
{
builder.SetMaskInclude(fieldType);
fieldInfo.SetValue(aspect, pool);
}
else
{
pool = (IEcsPool)_incluedMethod.MakeGenericMethod(fieldType).Invoke(builder, null);
}
fieldInfo.SetValue(aspect, pool);
break;
case ExcAttribute extAtr:
if (builder.World.TryFindPoolInstance(fieldType, out pool))
{
builder.SetMaskExclude(fieldType);
fieldInfo.SetValue(aspect, pool);
}
else
{
pool = (IEcsPool)_excludeMethod.MakeGenericMethod(fieldType).Invoke(builder, null);
}
fieldInfo.SetValue(aspect, pool);
break;
case OptAttribute optAtr:
if (builder.World.TryFindPoolInstance(fieldType, out pool))
{
fieldInfo.SetValue(aspect, pool);
}
else
{
pool = (IEcsPool)_optionalMethod.MakeGenericMethod(fieldType).Invoke(builder, null);
}
fieldInfo.SetValue(aspect, pool);
break;
case CombineAttribute combineAtr:
pool = builder.World.FindPoolInstance(fieldType);
fieldInfo.SetValue(aspect, _combineMethod.MakeGenericMethod(fieldType).Invoke(builder, new object[] { combineAtr.Order }));
break;
default:
break;
}
}
if (injectAttribute is IncAttribute)
foreach (var attribute in implicitInjectAttributes)
{ {
fieldInfo.SetValue(s, incluedMethod.MakeGenericMethod(fieldType).Invoke(b, null)); if (attribute is IncImplicitAttribute incImplicitAtr)
{
builder.SetMaskInclude(incImplicitAtr.ComponentType);
continue; continue;
} }
if (injectAttribute is ExcAttribute) if (attribute is ExcImplicitAttribute excImplicitAtr)
{ {
fieldInfo.SetValue(s, excludeMethod.MakeGenericMethod(fieldType).Invoke(b, null)); builder.SetMaskExclude(excImplicitAtr.ComponentType);
continue;
}
if (injectAttribute is OptAttribute)
{
fieldInfo.SetValue(s, optionalMethod.MakeGenericMethod(fieldType).Invoke(b, null));
continue;
}
if (injectAttribute is CombineAttribute combAttribute)
{
fieldInfo.SetValue(s, combineMethod.MakeGenericMethod(fieldType).Invoke(b, new object[] { combAttribute.order }));
continue; continue;
} }
} }
} }
} }
} }

View File

@ -1,4 +1,7 @@
using DCFApixels.DragonECS.PoolsCore; #if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.PoolsCore;
using System; using System;
using System.Linq; using System.Linq;
@ -14,32 +17,30 @@ namespace DCFApixels.DragonECS
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class CombineAttribute : InjectAspectMemberAttribute public sealed class CombineAttribute : InjectAspectMemberAttribute
{ {
public readonly int order = 0; public readonly int Order = 0;
public CombineAttribute(int order = 0) { this.order = order; } public CombineAttribute(int order = 0) { Order = order; }
} }
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class MaskAttribute : InjectAspectMemberAttribute { }
public abstract class ImplicitInjectAttribute : Attribute { }
public abstract class ImplicitInjectAttribute : Attribute
{
public readonly Type ComponentType;
public ImplicitInjectAttribute(Type componentType)
{
ComponentType = componentType;
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, Inherited = false, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class IncImplicitAttribute : ImplicitInjectAttribute public sealed class IncImplicitAttribute : ImplicitInjectAttribute
{ {
public readonly Type type; public IncImplicitAttribute(Type type) : base(type) { }
public readonly bool isPool;
public IncImplicitAttribute(Type type)
{
this.type = type;
isPool = type.GetInterfaces().Any(o => o == typeof(IEcsPoolImplementation));
}
} }
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, Inherited = false, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class ExcImplicitAttribute : ImplicitInjectAttribute public sealed class ExcImplicitAttribute : ImplicitInjectAttribute
{ {
public readonly Type type; public ExcImplicitAttribute(Type type) : base(type) { }
public readonly bool isPool;
public ExcImplicitAttribute(Type type)
{
this.type = type;
isPool = type.GetInterfaces().Any(o => o == typeof(IEcsPoolImplementation));
}
} }
} }

View File

@ -1,15 +1,29 @@
using System; #if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
{ {
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class DIAttribute : Attribute public class DIAttribute : Attribute
{ {
public static readonly DIAttribute Dummy = new DIAttribute(null); public static readonly DIAttribute Dummy = new DIAttribute();
public readonly Type notNullDummyType; public readonly Type NotNullDummyType = null;
public DIAttribute(Type notNullDummyType = null) public readonly string NamedInjection = string.Empty;
public DIAttribute() { }
public DIAttribute(string namedInjection)
{ {
this.notNullDummyType = notNullDummyType; NamedInjection = namedInjection;
}
public DIAttribute(Type notNullDummyType)
{
NotNullDummyType = notNullDummyType;
}
public DIAttribute(string namedInjection, Type notNullDummyType)
{
NamedInjection = namedInjection;
NotNullDummyType = notNullDummyType;
} }
} }

View File

@ -1,3 +1,6 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.AutoInjections; using DCFApixels.DragonECS.AutoInjections;
using DCFApixels.DragonECS.AutoInjections.Internal; using DCFApixels.DragonECS.AutoInjections.Internal;
using System; using System;
@ -10,8 +13,8 @@ namespace DCFApixels.DragonECS
internal class AutoInjectionMap internal class AutoInjectionMap
{ {
private readonly EcsPipeline _source; private readonly EcsPipeline _source;
private Dictionary<Type, List<InjectedPropertyRecord>> _systemProperties; private Dictionary<Type, List<InjectedPropertyRecord>> _injectedTypeToPropertiesMap = new Dictionary<Type, List<InjectedPropertyRecord>>();
private HashSet<Type> _notInjected; private HashSet<Type> _notInjected = new HashSet<Type>();
private bool _isDummyInjected = false; private bool _isDummyInjected = false;
private bool _isPreInitInjectionComplete = false; private bool _isPreInitInjectionComplete = false;
@ -20,20 +23,19 @@ namespace DCFApixels.DragonECS
{ {
_source = source; _source = source;
var allsystems = _source.AllSystems; var allsystems = _source.AllSystems;
_systemProperties = new Dictionary<Type, List<InjectedPropertyRecord>>();
_notInjected = new HashSet<Type>();
foreach (var system in allsystems) foreach (var system in allsystems)
{ {
Type systemType = system.GetType(); Type systemType = system.GetType();
if (systemType == typeof(AutoInjectSystem)) { continue; } if (systemType == typeof(AutoInjectSystem)) { continue; }
foreach (var property in GetAllPropertiesFor(systemType, isAgressiveInjection)) foreach (var property in GetAllPropertiesFor(systemType, isAgressiveInjection))
{ {
Type propertType = property.PropertyType; Type propertType = property.PropertyType;
List<InjectedPropertyRecord> list; List<InjectedPropertyRecord> list;
if (!_systemProperties.TryGetValue(propertType, out list)) if (_injectedTypeToPropertiesMap.TryGetValue(propertType, out list) == false)
{ {
list = new List<InjectedPropertyRecord>(); list = new List<InjectedPropertyRecord>();
_systemProperties.Add(propertType, list); _injectedTypeToPropertiesMap.Add(propertType, list);
} }
list.Add(new InjectedPropertyRecord(system, property)); list.Add(new InjectedPropertyRecord(system, property));
if (property.GetAutoInjectAttribute() != DIAttribute.Dummy) if (property.GetAutoInjectAttribute() != DIAttribute.Dummy)
@ -42,89 +44,103 @@ namespace DCFApixels.DragonECS
} }
} }
} }
//foreach (var pair in _systemProperties)
//{
// _notInjected.Add(pair.Key);
//}
} }
private static List<IInjectedProperty> GetAllPropertiesFor(Type type, bool isAgressiveInjection)
//private bool IsInjectTarget(MemberInfo member)
//{
// return _isAgressiveInjection || member.GetCustomAttribute<EcsInjectAttribute>() != null;
//}
private static void Do(Type type, List<IInjectedProperty> result, bool isAgressiveInjection)
{ {
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; List<IInjectedProperty> result = new List<IInjectedProperty>();
result.AddRange(type.GetFields(bindingFlags) GetAllPropertiesFor(type, isAgressiveInjection, result);
.Where(o => isAgressiveInjection || o.GetCustomAttribute<DIAttribute>() != null) return result;
}
private static void GetAllPropertiesFor(Type type, bool isAgressiveInjection, List<IInjectedProperty> result)
{
const BindingFlags REFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
result.AddRange(type.GetFields(REFL_FLAGS)
.Where(o => isAgressiveInjection || o.HasAttribute<DIAttribute>())
.Select(o => new InjectedField(o))); .Select(o => new InjectedField(o)));
result.AddRange(type.GetProperties(bindingFlags)
result.AddRange(type.GetProperties(REFL_FLAGS)
.Where(o => .Where(o =>
{ {
if (!isAgressiveInjection && o.GetCustomAttribute<DIAttribute>() == null) if (!isAgressiveInjection && o.HasAttribute<DIAttribute>() == false)
{ {
return false; return false;
} }
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS #if DEBUG
if (!isAgressiveInjection && o.CanWrite == false) { Throw.PropertyIsCantWrite(o); } if (!isAgressiveInjection && o.CanWrite == false) { Throw.PropertyIsCantWrite(o); }
#endif #endif
return o.CanWrite == false; return o.CanWrite;
}) })
.Select(o => new InjectedProperty(o))); .Select(o => new InjectedProperty(o)));
result.AddRange(type.GetMethods(bindingFlags)
result.AddRange(type.GetMethods(REFL_FLAGS)
.Where(o => .Where(o =>
{ {
if (!isAgressiveInjection && o.GetCustomAttribute<DIAttribute>() == null) if (!isAgressiveInjection && o.HasAttribute<DIAttribute>() == false)
{ {
return false; return false;
} }
var parameters = o.GetParameters(); var parameters = o.GetParameters();
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (!isAgressiveInjection) if (!isAgressiveInjection)
{ {
#if DEBUG
if (o.IsGenericMethod) { Throw.MethodIsGeneric(o); } if (o.IsGenericMethod) { Throw.MethodIsGeneric(o); }
if (parameters.Length != 1) { Throw.MethodArgumentsGreater1(o); } if (parameters.Length != 1) { Throw.MethodArgumentsGreater1(o); }
}
#endif #endif
}
return o.IsGenericMethod == false && parameters.Length == 1; return o.IsGenericMethod == false && parameters.Length == 1;
}) })
.Select(o => new InjectedMethod(o))); .Select(o => new InjectedMethod(o)));
if (type.BaseType != null) if (type.BaseType != null)
{ {
Do(type.BaseType, result, isAgressiveInjection); GetAllPropertiesFor(type.BaseType, isAgressiveInjection, result);
} }
} }
private static List<IInjectedProperty> GetAllPropertiesFor(Type type, bool isAgressiveInjection) private Type[] _relatedTypesBuffer;
{
List<IInjectedProperty> result = new List<IInjectedProperty>();
Do(type, result, isAgressiveInjection);
return result;
}
public void Inject(Type fieldType, object obj) public void Inject(Type fieldType, object obj)
{ {
if (!_isPreInitInjectionComplete)
if (_relatedTypesBuffer == null || _relatedTypesBuffer.Length < _injectedTypeToPropertiesMap.Count)
{ {
_notInjected.Remove(fieldType); _relatedTypesBuffer = new Type[_injectedTypeToPropertiesMap.Count];
}
int relatedTypesCount = 0;
foreach (var pair in _injectedTypeToPropertiesMap)
{
if (pair.Key == fieldType || pair.Key.IsAssignableFrom(fieldType))
{
_relatedTypesBuffer[relatedTypesCount++] = pair.Key;
}
} }
if (_systemProperties.TryGetValue(fieldType, out List<InjectedPropertyRecord> list)) foreach (var type in new ReadOnlySpan<Type>(_relatedTypesBuffer, 0, relatedTypesCount))
{ {
if (_injectedTypeToPropertiesMap.TryGetValue(type, out List<InjectedPropertyRecord> list))
{
string name = string.Empty;
if (obj is INamedMember named)
{
name = named.Name;
}
foreach (var item in list) foreach (var item in list)
{ {
string propertyName = item.Attribute.NamedInjection;
if (string.IsNullOrEmpty(propertyName) || propertyName == name)
{
if (_isPreInitInjectionComplete == false)
{
_notInjected.Remove(item.property.PropertyType);
}
item.property.Inject(item.target, obj); item.property.Inject(item.target, obj);
} }
} }
Type baseType = fieldType.BaseType;
if (baseType != null)
{
Inject(baseType, obj);
} }
} }
}
public void InjectDummy() public void InjectDummy()
{ {
if (_isDummyInjected) { return; } if (_isDummyInjected) { return; }
@ -132,18 +148,18 @@ namespace DCFApixels.DragonECS
_isDummyInjected = true; _isDummyInjected = true;
foreach (var notInjectedItem in _notInjected) foreach (var notInjectedItem in _notInjected)
{ {
foreach (var systemRecord in _systemProperties[notInjectedItem]) foreach (var systemRecord in _injectedTypeToPropertiesMap[notInjectedItem])
{ {
if (systemRecord.Attribute.notNullDummyType == null) if (systemRecord.Attribute.NotNullDummyType == null)
continue; continue;
if (systemRecord.property.IsInjected) if (systemRecord.property.IsInjected)
continue; continue;
if (systemRecord.property.PropertyType.IsAssignableFrom(systemRecord.Attribute.notNullDummyType) == false) if (systemRecord.property.PropertyType.IsAssignableFrom(systemRecord.Attribute.NotNullDummyType) == false)
{ {
EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, $"The {systemRecord.Attribute.notNullDummyType} dummy cannot be assigned to the {systemRecord.property.PropertyType.Name} field"); EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, $"The {systemRecord.Attribute.NotNullDummyType} dummy cannot be assigned to the {systemRecord.property.PropertyType.Name} field");
continue; continue;
} }
systemRecord.property.Inject(systemRecord.target, DummyInstance.GetInstance(systemRecord.Attribute.notNullDummyType)); systemRecord.property.Inject(systemRecord.target, DummyInstance.GetInstance(systemRecord.Attribute.NotNullDummyType));
} }
} }
WarningMissedInjections(); WarningMissedInjections();
@ -156,7 +172,7 @@ namespace DCFApixels.DragonECS
#if DEBUG #if DEBUG
foreach (var item in _notInjected) foreach (var item in _notInjected)
{ {
foreach (var systemRecord in _systemProperties[item]) foreach (var systemRecord in _injectedTypeToPropertiesMap[item])
{ {
EcsDebug.PrintWarning($"in system {EcsDebugUtility.GetGenericTypeFullName(systemRecord.target.GetType(), 1)} is missing an injection of {EcsDebugUtility.GetGenericTypeFullName(item, 1)}."); EcsDebug.PrintWarning($"in system {EcsDebugUtility.GetGenericTypeFullName(systemRecord.target.GetType(), 1)} is missing an injection of {EcsDebugUtility.GetGenericTypeFullName(item, 1)}.");
} }
@ -186,15 +202,33 @@ namespace DCFApixels.DragonECS
[MetaColor(MetaColor.Gray)] [MetaColor(MetaColor.Gray)]
[MetaGroup(EcsAutoInjectionsConsts.PACK_GROUP, EcsConsts.DI_GROUP)] [MetaGroup(EcsAutoInjectionsConsts.PACK_GROUP, EcsConsts.DI_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, "The system responsible for the processing of automatic injections. The .AutoInject() method adds an AutoInjectSystem to the systems pipelines.")] [MetaDescription(EcsConsts.AUTHOR, "The system responsible for the processing of automatic injections. The .AutoInject() method adds an AutoInjectSystem to the systems pipelines.")]
public class AutoInjectSystem : IEcsInject<object>, IEcsPipelineMember, IOnInitInjectionComplete public class AutoInjectSystem : IEcsInject<object>, IEcsPipelineMember, IOnInitInjectionComplete, IEcsDefaultAddParams
{ {
public AddParams AddParams => new AddParams(layerName: EcsConsts.PRE_BEGIN_LAYER, isUnique: true);
private EcsPipeline _pipeline; private EcsPipeline _pipeline;
EcsPipeline IEcsPipelineMember.Pipeline { get => _pipeline; set => _pipeline = value; }
private List<object> _delayedInjects = new List<object>(); private List<object> _delayedInjects = new List<object>();
private AutoInjectionMap _autoInjectionMap; private AutoInjectionMap _autoInjectionMap;
private bool _isInitInjectionCompleted; private bool _isInitInjectionCompleted;
private bool _isAgressiveInjection; private bool _isAgressiveInjection;
static AutoInjectSystem()
{
EcsAspect.OnInit -= EcsAspect_OnInit;
EcsAspect.OnInit += EcsAspect_OnInit;
EcsAspect.OnAfterInit -= EcsAspect_OnBuild;
EcsAspect.OnAfterInit += EcsAspect_OnBuild;
}
private static void EcsAspect_OnInit(object aspect, EcsAspect.Builder builder)
{
EcsAspectAutoHelper.FillFields(aspect, builder);
}
private static void EcsAspect_OnBuild(object aspect, EcsMask mask)
{
EcsAspectAutoHelper.FillMaskFields(aspect, mask);
}
public AutoInjectSystem(bool isAgressiveInjection = false) public AutoInjectSystem(bool isAgressiveInjection = false)
{ {
_isAgressiveInjection = isAgressiveInjection; _isAgressiveInjection = isAgressiveInjection;
@ -212,6 +246,7 @@ namespace DCFApixels.DragonECS
} }
} }
public void OnBeforeInitInjection() { }
public void OnInitInjectionComplete() public void OnInitInjectionComplete()
{ {
_autoInjectionMap = new AutoInjectionMap(_pipeline, _isAgressiveInjection); _autoInjectionMap = new AutoInjectionMap(_pipeline, _isAgressiveInjection);
@ -228,6 +263,8 @@ namespace DCFApixels.DragonECS
_delayedInjects = null; _delayedInjects = null;
GC.Collect(0); GC.Collect(0);
} }
EcsPipeline IEcsPipelineMember.Pipeline { get => _pipeline; set => _pipeline = value; }
} }
#region Utils #region Utils

View File

@ -1,10 +1,23 @@
namespace DCFApixels.DragonECS #if DISABLE_DEBUG
#undef DEBUG
#endif
namespace DCFApixels.DragonECS
{ {
public static class AutoInjectSystemExtensions public static class AutoInjectSystemExtensions
{ {
[MetaColor(MetaColor.DragonCyan)]
public class AutoInjectModule : IEcsModule
{
public bool isAgressiveInjection;
public void Import(EcsPipeline.Builder b)
{
b.AddUnique(new AutoInjectSystem(isAgressiveInjection));
}
}
public static EcsPipeline.Builder AutoInject(this EcsPipeline.Builder self, bool isAgressiveInjection = false) public static EcsPipeline.Builder AutoInject(this EcsPipeline.Builder self, bool isAgressiveInjection = false)
{ {
self.Add(new AutoInjectSystem(isAgressiveInjection)); self.AddUnique(new AutoInjectSystem(isAgressiveInjection));
return self; return self;
} }
} }

View File

@ -1,4 +1,8 @@
using System; #if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
[AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)] [AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
public sealed class BindWithRunnerAttribute : Attribute public sealed class BindWithRunnerAttribute : Attribute
{ {

View File

@ -1,4 +1,7 @@
using DCFApixels.DragonECS.AutoInjections.Internal; #if DISABLE_DEBUG
#undef DEBUG
#endif
using DCFApixels.DragonECS.AutoInjections.Internal;
using System; using System;
using System.Reflection; using System.Reflection;

View File

@ -1,4 +1,7 @@
using System; #if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
using System.Reflection; using System.Reflection;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS

View File

@ -1,4 +1,7 @@
using System; #if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

View File

@ -1,4 +1,7 @@
using System; #if DISABLE_DEBUG
#undef DEBUG
#endif
using System;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -18,5 +21,10 @@ namespace DCFApixels.DragonECS.AutoInjections.Internal
attribute = self.GetCustomAttribute<T>(); attribute = self.GetCustomAttribute<T>();
return attribute != null; return attribute != null;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasAttribute<T>(this MemberInfo self) where T : Attribute
{
return self.GetCustomAttribute<T>() != null;
}
} }
} }