Skip to content
This repository was archived by the owner on Jun 22, 2023. It is now read-only.

Commit

Permalink
* Изменение лицензии.
Browse files Browse the repository at this point in the history
Leopotam committed Mar 21, 2022
0 parents commit 1966a4c
Showing 22 changed files with 4,160 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.sln
.vscode
.idea
Library
bin
obj
Temp
.DS_Store
33 changes: 33 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Copyright (c) 2012 - 2022 leopotam@yandex.ru

Данное программное обеспечение и сопутствующая документация (далее - Продукт)
выпускается на условиях двойного лицензирования - под собственнической/коммерческой
и MIT-Red лицензиями.

Условия использования под собственнической/коммерческой лицензии обсуждаются
индивидуально, для подробностей следует писать на электронную почту.

MIT-Red регулируется совокупностью следующих правил, если хотя бы
одно из них невыполнимо - использование Продукта запрещено:

Если вы за применение opensource программного обеспечения в
военной сфере - вы не можете использовать этот Продукт.

Если вы испытываете ненависть к русским или поддерживаете
любые нападки на них - вы не можете использовать этот Продукт.

Данная лицензия разрешает лицам, получившим копию данного Продукта,
безвозмездно использовать Программное обеспечение без ограничений, включая
неограниченное право на использование, копирование, изменение, слияние,
публикацию и распространение копий Продукта.

Указанное выше уведомление об авторском праве и данные условия должны быть
включены во все копии или значимые части данного Продукта.

ДАННЫЙ ПРОДУКТ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО
ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ,
СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО
НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ
ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ,
В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ
ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОДУКТОМ.
7 changes: 7 additions & 0 deletions LICENSE.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Leopotam.Ecs.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "Leopotam.Ecs",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
7 changes: 7 additions & 0 deletions Leopotam.Ecs.asmdef.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

559 changes: 559 additions & 0 deletions README.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions README.md.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "com.leopotam.ecs",
"author": "Leopotam",
"displayName": "LeoECS",
"description": "LeoECS - легковесный ECS-фреймворк, основанный на структурах. Производительность, нулевые или минимальные аллокации, минимизация использования памяти, отсутствие зависимостей от любого игрового движка - это основные цели данного фреймворка.",
"unity": "2020.3",
"version": "2022.3.22",
"keywords": [
"leoecs",
"ecs",
"performance"
],
"dependencies": {},
"repository": {
"type": "git",
"url": "https://github.com/Leopotam/ecs.git"
}
}
7 changes: 7 additions & 0 deletions package.json.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

245 changes: 245 additions & 0 deletions src/EcsComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// ----------------------------------------------------------------------------
// The Proprietary or MIT-Red License
// Copyright (c) 2012-2022 Leopotam <leopotam@yandex.ru>
// ----------------------------------------------------------------------------

using System;
using System.Runtime.CompilerServices;
using System.Threading;

// ReSharper disable ClassNeverInstantiated.Global

namespace Leopotam.Ecs {
/// <summary>
/// Marks component type to be not auto-filled as GetX in filter.
/// </summary>
public interface IEcsIgnoreInFilter { }

/// <summary>
/// Marks component type for custom reset behaviour.
/// </summary>
/// <typeparam name="T">Type of component, should be the same as main component!</typeparam>
public interface IEcsAutoReset<T> where T : struct {
void AutoReset (ref T c);
}

/// <summary>
/// Marks field of IEcsSystem class to be ignored during dependency injection.
/// </summary>
public sealed class EcsIgnoreInjectAttribute : Attribute { }

/// <summary>
/// Global descriptor of used component type.
/// </summary>
/// <typeparam name="T">Component type.</typeparam>
public static class EcsComponentType<T> where T : struct {
// ReSharper disable StaticMemberInGenericType
public static readonly int TypeIndex;
public static readonly Type Type;
public static readonly bool IsIgnoreInFilter;
public static readonly bool IsAutoReset;
// ReSharper restore StaticMemberInGenericType

static EcsComponentType () {
TypeIndex = Interlocked.Increment (ref EcsComponentPool.ComponentTypesCount);
Type = typeof (T);
IsIgnoreInFilter = typeof (IEcsIgnoreInFilter).IsAssignableFrom (Type);
IsAutoReset = typeof (IEcsAutoReset<T>).IsAssignableFrom (Type);
#if DEBUG
if (!IsAutoReset && Type.GetInterface ("IEcsAutoReset`1") != null) {
throw new Exception ($"IEcsAutoReset should have <{typeof (T).Name}> constraint for component \"{typeof (T).Name}\".");
}
#endif
}
}

public sealed class EcsComponentPool {
/// <summary>
/// Global component type counter.
/// First component will be "1" for correct filters updating (add component on positive and remove on negative).
/// </summary>
internal static int ComponentTypesCount;
}

public interface IEcsComponentPool {
Type ItemType { get; }
object GetItem (int idx);
void Recycle (int idx);
int New ();
void CopyData (int srcIdx, int dstIdx);
}

/// <summary>
/// Helper for save reference to component.
/// </summary>
/// <typeparam name="T">Type of component.</typeparam>
public struct EcsComponentRef<T> where T : struct {
internal EcsComponentPool<T> Pool;
internal int Idx;

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public static bool AreEquals (in EcsComponentRef<T> lhs, in EcsComponentRef<T> rhs) {
return lhs.Idx == rhs.Idx && lhs.Pool == rhs.Pool;
}
}

#if ENABLE_IL2CPP
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
#endif
public static class EcsComponentRefExtensions {
[MethodImpl (MethodImplOptions.AggressiveInlining)]
public static ref T Unref<T> (in this EcsComponentRef<T> wrapper) where T : struct {
return ref wrapper.Pool.Items[wrapper.Idx];
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public static bool IsNull<T> (in this EcsComponentRef<T> wrapper) where T : struct {
return wrapper.Pool == null;
}
}

public interface IEcsComponentPoolResizeListener {
void OnComponentPoolResize ();
}

#if ENABLE_IL2CPP
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
#endif
public sealed class EcsComponentPool<T> : IEcsComponentPool where T : struct {
delegate void AutoResetHandler (ref T component);

public Type ItemType { get; }
public T[] Items = new T[128];
int[] _reservedItems = new int[128];
int _itemsCount;
int _reservedItemsCount;
readonly AutoResetHandler _autoReset;
#if ENABLE_IL2CPP && !UNITY_EDITOR
T _autoresetFakeInstance;
#endif
IEcsComponentPoolResizeListener[] _resizeListeners;
int _resizeListenersCount;

internal EcsComponentPool () {
ItemType = typeof (T);
if (EcsComponentType<T>.IsAutoReset) {
var autoResetMethod = typeof (T).GetMethod (nameof (IEcsAutoReset<T>.AutoReset));
#if DEBUG

if (autoResetMethod == null) {
throw new Exception (
$"IEcsAutoReset<{typeof (T).Name}> explicit implementation not supported, use implicit instead.");
}
#endif
_autoReset = (AutoResetHandler) Delegate.CreateDelegate (
typeof (AutoResetHandler),
#if ENABLE_IL2CPP && !UNITY_EDITOR
_autoresetFakeInstance,
#else
null,
#endif
autoResetMethod);
}
_resizeListeners = new IEcsComponentPoolResizeListener[128];
_reservedItemsCount = 0;
}

void RaiseOnResizeEvent () {
for (int i = 0, iMax = _resizeListenersCount; i < iMax; i++) {
_resizeListeners[i].OnComponentPoolResize ();
}
}

public void AddResizeListener (IEcsComponentPoolResizeListener listener) {
#if DEBUG
if (listener == null) { throw new Exception ("Listener is null."); }
#endif
if (_resizeListeners.Length == _resizeListenersCount) {
Array.Resize (ref _resizeListeners, _resizeListenersCount << 1);
}
_resizeListeners[_resizeListenersCount++] = listener;
}

public void RemoveResizeListener (IEcsComponentPoolResizeListener listener) {
#if DEBUG
if (listener == null) { throw new Exception ("Listener is null."); }
#endif
for (int i = 0, iMax = _resizeListenersCount; i < iMax; i++) {
if (_resizeListeners[i] == listener) {
_resizeListenersCount--;
if (i < _resizeListenersCount) {
_resizeListeners[i] = _resizeListeners[_resizeListenersCount];
}
_resizeListeners[_resizeListenersCount] = null;
break;
}
}
}

/// <summary>
/// Sets new capacity (if more than current amount).
/// </summary>
/// <param name="capacity">New value.</param>
public void SetCapacity (int capacity) {
if (capacity > Items.Length) {
Array.Resize (ref Items, capacity);
RaiseOnResizeEvent ();
}
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public int New () {
int id;
if (_reservedItemsCount > 0) {
id = _reservedItems[--_reservedItemsCount];
} else {
id = _itemsCount;
if (_itemsCount == Items.Length) {
Array.Resize (ref Items, _itemsCount << 1);
RaiseOnResizeEvent ();
}
// reset brand new instance if custom AutoReset was registered.
_autoReset?.Invoke (ref Items[_itemsCount]);
_itemsCount++;
}
return id;
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public ref T GetItem (int idx) {
return ref Items[idx];
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public void Recycle (int idx) {
if (_autoReset != null) {
_autoReset (ref Items[idx]);
} else {
Items[idx] = default;
}
if (_reservedItemsCount == _reservedItems.Length) {
Array.Resize (ref _reservedItems, _reservedItemsCount << 1);
}
_reservedItems[_reservedItemsCount++] = idx;
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public void CopyData (int srcIdx, int dstIdx) {
Items[dstIdx] = Items[srcIdx];
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public EcsComponentRef<T> Ref (int idx) {
EcsComponentRef<T> componentRef;
componentRef.Pool = this;
componentRef.Idx = idx;
return componentRef;
}

object IEcsComponentPool.GetItem (int idx) {
return Items[idx];
}
}
}
11 changes: 11 additions & 0 deletions src/EcsComponent.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

556 changes: 556 additions & 0 deletions src/EcsEntity.cs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/EcsEntity.cs.meta
1,654 changes: 1,654 additions & 0 deletions src/EcsFilter.cs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/EcsFilter.cs.meta
64 changes: 64 additions & 0 deletions src/EcsHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// ----------------------------------------------------------------------------
// The Proprietary or MIT-Red License
// Copyright (c) 2012-2022 Leopotam <leopotam@yandex.ru>
// ----------------------------------------------------------------------------

using System;
using System.Runtime.CompilerServices;

namespace Leopotam.Ecs {
/// <summary>
/// Fast List replacement for growing only collections.
/// </summary>
/// <typeparam name="T">Type of item.</typeparam>
public class EcsGrowList<T> {
public T[] Items;
public int Count;

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public EcsGrowList (int capacity) {
#if DEBUG
if (capacity <= 0) { throw new Exception ("Capacity should be greater than zero."); }
#endif
Items = new T[capacity];
Count = 0;
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public void Add (T item) {
if (Items.Length == Count) {
Array.Resize (ref Items, Items.Length << 1);
}
Items[Count++] = item;
}

[MethodImpl (MethodImplOptions.AggressiveInlining)]
public void EnsureCapacity (int count) {
if (Items.Length < count) {
var len = Items.Length << 1;
while (len <= count) {
len <<= 1;
}
Array.Resize (ref Items, len);
}
}
}
}

#if ENABLE_IL2CPP
// Unity IL2CPP performance optimization attribute.
namespace Unity.IL2CPP.CompilerServices {
enum Option {
NullChecks = 1,
ArrayBoundsChecks = 2
}

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
class Il2CppSetOptionAttribute : Attribute {
public Option Option { get; private set; }
public object Value { get; private set; }

public Il2CppSetOptionAttribute (Option option, object value) { Option = option; Value = value; }
}
}
#endif
11 changes: 11 additions & 0 deletions src/EcsHelpers.cs.meta
384 changes: 384 additions & 0 deletions src/EcsSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,384 @@
// ----------------------------------------------------------------------------
// The Proprietary or MIT-Red License
// Copyright (c) 2012-2022 Leopotam <leopotam@yandex.ru>
// ----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Reflection;

namespace Leopotam.Ecs {
/// <summary>
/// Base interface for all systems.
/// </summary>
public interface IEcsSystem { }

/// <summary>
/// Interface for PreInit systems. PreInit() will be called before Init().
/// </summary>
public interface IEcsPreInitSystem : IEcsSystem {
void PreInit ();
}

/// <summary>
/// Interface for Init systems. Init() will be called before Run().
/// </summary>
public interface IEcsInitSystem : IEcsSystem {
void Init ();
}

/// <summary>
/// Interface for PostDestroy systems. PostDestroy() will be called after Destroy().
/// </summary>
public interface IEcsPostDestroySystem : IEcsSystem {
void PostDestroy ();
}

/// <summary>
/// Interface for Destroy systems. Destroy() will be called last in system lifetime cycle.
/// </summary>
public interface IEcsDestroySystem : IEcsSystem {
void Destroy ();
}

/// <summary>
/// Interface for Run systems.
/// </summary>
public interface IEcsRunSystem : IEcsSystem {
void Run ();
}

#if DEBUG
/// <summary>
/// Debug interface for systems events processing.
/// </summary>
public interface IEcsSystemsDebugListener {
void OnSystemsDestroyed (EcsSystems systems);
}
#endif

/// <summary>
/// Logical group of systems.
/// </summary>
#if ENABLE_IL2CPP
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
[Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
#endif
public sealed class EcsSystems : IEcsInitSystem, IEcsDestroySystem, IEcsRunSystem {
public readonly string Name;
public readonly EcsWorld World;
readonly EcsGrowList<IEcsSystem> _allSystems = new EcsGrowList<IEcsSystem> (64);
readonly EcsGrowList<EcsSystemsRunItem> _runSystems = new EcsGrowList<EcsSystemsRunItem> (64);
readonly Dictionary<int, int> _namedRunSystems = new Dictionary<int, int> (64);
readonly Dictionary<Type, object> _injections = new Dictionary<Type, object> (32);
bool _injected;
#if DEBUG
bool _initialized;
bool _destroyed;
readonly List<IEcsSystemsDebugListener> _debugListeners = new List<IEcsSystemsDebugListener> (4);

/// <summary>
/// Adds external event listener.
/// </summary>
/// <param name="listener">Event listener.</param>
public void AddDebugListener (IEcsSystemsDebugListener listener) {
if (listener == null) { throw new Exception ("listener is null"); }
_debugListeners.Add (listener);
}

/// <summary>
/// Removes external event listener.
/// </summary>
/// <param name="listener">Event listener.</param>
public void RemoveDebugListener (IEcsSystemsDebugListener listener) {
if (listener == null) { throw new Exception ("listener is null"); }
_debugListeners.Remove (listener);
}
#endif

/// <summary>
/// Creates new instance of EcsSystems group.
/// </summary>
/// <param name="world">EcsWorld instance.</param>
/// <param name="name">Custom name for this group.</param>
public EcsSystems (EcsWorld world, string name = null) {
World = world;
Name = name;
}

/// <summary>
/// Adds new system to processing.
/// </summary>
/// <param name="system">System instance.</param>
/// <param name="namedRunSystem">Optional name of system.</param>
public EcsSystems Add (IEcsSystem system, string namedRunSystem = null) {
#if DEBUG
if (system == null) { throw new Exception ("System is null."); }
if (_initialized) { throw new Exception ("Cant add system after initialization."); }
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
if (!string.IsNullOrEmpty (namedRunSystem) && !(system is IEcsRunSystem)) { throw new Exception ("Cant name non-IEcsRunSystem."); }
#endif
_allSystems.Add (system);
if (system is IEcsRunSystem) {
if (namedRunSystem == null && system is EcsSystems ecsSystems) {
namedRunSystem = ecsSystems.Name;
}
if (namedRunSystem != null) {
#if DEBUG
if (_namedRunSystems.ContainsKey (namedRunSystem.GetHashCode ())) {
throw new Exception ($"Cant add named system - \"{namedRunSystem}\" name already exists.");
}
#endif
_namedRunSystems[namedRunSystem.GetHashCode ()] = _runSystems.Count;
}
_runSystems.Add (new EcsSystemsRunItem { Active = true, System = (IEcsRunSystem) system });
}
return this;
}

public int GetNamedRunSystem (string name) {
return _namedRunSystems.TryGetValue (name.GetHashCode (), out var idx) ? idx : -1;
}

/// <summary>
/// Sets IEcsRunSystem active state.
/// </summary>
/// <param name="idx">Index of system.</param>
/// <param name="state">New state of system.</param>
public void SetRunSystemState (int idx, bool state) {
#if DEBUG
if (idx < 0 || idx >= _runSystems.Count) { throw new Exception ("Invalid index"); }
#endif
_runSystems.Items[idx].Active = state;
}

/// <summary>
/// Gets IEcsRunSystem active state.
/// </summary>
/// <param name="idx">Index of system.</param>
public bool GetRunSystemState (int idx) {
#if DEBUG
if (idx < 0 || idx >= _runSystems.Count) { throw new Exception ("Invalid index"); }
#endif
return _runSystems.Items[idx].Active;
}

/// <summary>
/// Get all systems. Important: Don't change collection!
/// </summary>
public EcsGrowList<IEcsSystem> GetAllSystems () {
return _allSystems;
}

/// <summary>
/// Gets all run systems. Important: Don't change collection!
/// </summary>
public EcsGrowList<EcsSystemsRunItem> GetRunSystems () {
return _runSystems;
}

/// <summary>
/// Injects instance of object type to all compatible fields of added systems.
/// </summary>
/// <param name="obj">Instance.</param>
/// <param name="overridenType">Overriden type, if null - typeof(obj) will be used.</param>
public EcsSystems Inject (object obj, Type overridenType = null) {
#if DEBUG
if (_initialized) { throw new Exception ("Cant inject after initialization."); }
if (obj == null) { throw new Exception ("Cant inject null instance."); }
if (overridenType != null && !overridenType.IsInstanceOfType (obj)) { throw new Exception ("Invalid overriden type."); }
#endif
if (overridenType == null) {
overridenType = obj.GetType ();
}
_injections[overridenType] = obj;
return this;
}

/// <summary>
/// Processes injections immediately.
/// Can be used to DI before Init() call.
/// </summary>
public EcsSystems ProcessInjects () {
#if DEBUG
if (_initialized) { throw new Exception ("Cant inject after initialization."); }
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
#endif
if (!_injected) {
_injected = true;
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
if (_allSystems.Items[i] is EcsSystems nestedSystems) {
foreach (var pair in _injections) {
nestedSystems._injections[pair.Key] = pair.Value;
}
nestedSystems.ProcessInjects ();
} else {
InjectDataToSystem (_allSystems.Items[i], World, _injections);
}
}
}
return this;
}

/// <summary>
/// Registers component type as one-frame for auto-removing at this point in execution sequence.
/// </summary>
public EcsSystems OneFrame<T> () where T : struct {
return Add (new RemoveOneFrame<T> ());
}

/// <summary>
/// Closes registration for new systems, initialize all registered.
/// </summary>
public void Init () {
#if DEBUG
if (_initialized) { throw new Exception ("Already initialized."); }
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
#endif
ProcessInjects ();
// IEcsPreInitSystem processing.
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
var system = _allSystems.Items[i];
if (system is IEcsPreInitSystem preInitSystem) {
preInitSystem.PreInit ();
#if DEBUG
World.CheckForLeakedEntities ($"{preInitSystem.GetType ().Name}.PreInit()");
#endif
}
}
// IEcsInitSystem processing.
for (int i = 0, iMax = _allSystems.Count; i < iMax; i++) {
var system = _allSystems.Items[i];
if (system is IEcsInitSystem initSystem) {
initSystem.Init ();
#if DEBUG
World.CheckForLeakedEntities ($"{initSystem.GetType ().Name}.Init()");
#endif
}
}
#if DEBUG
_initialized = true;
#endif
}

/// <summary>
/// Processes all IEcsRunSystem systems.
/// </summary>
public void Run () {
#if DEBUG
if (!_initialized) { throw new Exception ($"[{Name ?? "NONAME"}] EcsSystems should be initialized before."); }
if (_destroyed) { throw new Exception ("Cant touch after destroy."); }
#endif
for (int i = 0, iMax = _runSystems.Count; i < iMax; i++) {
var runItem = _runSystems.Items[i];
if (runItem.Active) {
runItem.System.Run ();
}
#if DEBUG
if (World.CheckForLeakedEntities (null)) {
throw new Exception ($"Empty entity detected, possible memory leak in {_runSystems.Items[i].GetType ().Name}.Run ()");
}
#endif
}
}

/// <summary>
/// Destroys registered data.
/// </summary>
public void Destroy () {
#if DEBUG
if (_destroyed) { throw new Exception ("Already destroyed."); }
_destroyed = true;
#endif
// IEcsDestroySystem processing.
for (var i = _allSystems.Count - 1; i >= 0; i--) {
var system = _allSystems.Items[i];
if (system is IEcsDestroySystem destroySystem) {
destroySystem.Destroy ();
#if DEBUG
World.CheckForLeakedEntities ($"{destroySystem.GetType ().Name}.Destroy ()");
#endif
}
}
// IEcsPostDestroySystem processing.
for (var i = _allSystems.Count - 1; i >= 0; i--) {
var system = _allSystems.Items[i];
if (system is IEcsPostDestroySystem postDestroySystem) {
postDestroySystem.PostDestroy ();
#if DEBUG
World.CheckForLeakedEntities ($"{postDestroySystem.GetType ().Name}.PostDestroy ()");
#endif
}
}
#if DEBUG
for (int i = 0, iMax = _debugListeners.Count; i < iMax; i++) {
_debugListeners[i].OnSystemsDestroyed (this);
}
#endif
}

/// <summary>
/// Injects custom data to fields of ISystem instance.
/// </summary>
/// <param name="system">ISystem instance.</param>
/// <param name="world">EcsWorld instance.</param>
/// <param name="injections">Additional instances for injection.</param>
public static void InjectDataToSystem (IEcsSystem system, EcsWorld world, Dictionary<Type, object> injections) {
var systemType = system.GetType ();
var worldType = world.GetType ();
var filterType = typeof (EcsFilter);
var ignoreType = typeof (EcsIgnoreInjectAttribute);

foreach (var f in systemType.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
// skip statics or fields with [EcsIgnoreInject] attribute.
if (f.IsStatic || Attribute.IsDefined (f, ignoreType)) {
continue;
}
// EcsWorld
if (f.FieldType.IsAssignableFrom (worldType)) {
f.SetValue (system, world);
continue;
}
// EcsFilter
#if DEBUG
if (f.FieldType == filterType) {
throw new Exception ($"Cant use EcsFilter type at \"{system}\" system for dependency injection, use generic version instead");
}
#endif
if (f.FieldType.IsSubclassOf (filterType)) {
f.SetValue (system, world.GetFilter (f.FieldType));
continue;
}
// Other injections.
foreach (var pair in injections) {
if (f.FieldType.IsAssignableFrom (pair.Key)) {
f.SetValue (system, pair.Value);
break;
}
}
}
}
}

/// <summary>
/// System for removing OneFrame component.
/// </summary>
/// <typeparam name="T">OneFrame component type.</typeparam>
sealed class RemoveOneFrame<T> : IEcsRunSystem where T : struct {
readonly EcsFilter<T> _oneFrames = null;

void IEcsRunSystem.Run () {
for (var idx = _oneFrames.GetEntitiesCount () - 1; idx >= 0; idx--) {
_oneFrames.GetEntity (idx).Del<T> ();
}
}
}

/// <summary>
/// IEcsRunSystem instance with active state.
/// </summary>
public sealed class EcsSystemsRunItem {
public bool Active;
public IEcsRunSystem System;
}
}
11 changes: 11 additions & 0 deletions src/EcsSystem.cs.meta
524 changes: 524 additions & 0 deletions src/EcsWorld.cs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/EcsWorld.cs.meta

0 comments on commit 1966a4c

Please sign in to comment.